diff --git a/.github/workflows/mtg-fetch-cards.yml b/.github/workflows/mtg-fetch-cards.yml index a61ea45cd5b3..8417b62cda3b 100644 --- a/.github/workflows/mtg-fetch-cards.yml +++ b/.github/workflows/mtg-fetch-cards.yml @@ -8,7 +8,9 @@ on: pull_request: types: [opened] pull_request_review: - type: [submitted] + types: [submitted] + pull_request_review_comment: + types: [created] jobs: fetch-card-references: diff --git a/.gitignore b/.gitignore index 0bac28a65b72..403ce01bda1a 100644 --- a/.gitignore +++ b/.gitignore @@ -63,6 +63,7 @@ Mage.Server.Plugins/Mage.Player.Human/target Mage.Server.Plugins/Mage.Tournament.BoosterDraft/target Mage.Server.Plugins/Mage.Tournament.Constructed/target Mage.Server.Plugins/Mage.Tournament.Sealed/target +Mage.Server.Plugins/target # Mage.Sets Mage.Sets/target diff --git a/Mage.Client/src/main/java/mage/client/cards/CardGrid.java b/Mage.Client/src/main/java/mage/client/cards/CardGrid.java index d237f83a7166..5bd6a93a0606 100644 --- a/Mage.Client/src/main/java/mage/client/cards/CardGrid.java +++ b/Mage.Client/src/main/java/mage/client/cards/CardGrid.java @@ -65,6 +65,16 @@ private void setGUISize() { cardDimension = GUISizeHelper.editorCardDimension; } + @Override + public Object getCardsStore() { + return this.cards; + } + + @Override + public void clearCardsStoreBeforeUpdate() { + this.cards.clear(); + } + @Override public void loadCards(CardsView showCards, SortSetting sortSetting, BigCard bigCard, UUID gameId) { this.loadCards(showCards, sortSetting, bigCard, gameId, true); @@ -353,7 +363,7 @@ class CardPanelCostComparator implements Comparator { @Override public int compare(MageCard o1, MageCard o2) { - int val = Integer.compare(o1.getOriginal().getConvertedManaCost(), o2.getOriginal().getConvertedManaCost()); + int val = Integer.compare(o1.getOriginal().getManaValue(), o2.getOriginal().getManaValue()); if (val == 0) { return o1.getOriginal().getName().compareTo(o2.getOriginal().getName()); } else { diff --git a/Mage.Client/src/main/java/mage/client/cards/CardsList.java b/Mage.Client/src/main/java/mage/client/cards/CardsList.java index 16996a40b466..ae0537283036 100644 --- a/Mage.Client/src/main/java/mage/client/cards/CardsList.java +++ b/Mage.Client/src/main/java/mage/client/cards/CardsList.java @@ -446,6 +446,17 @@ public void addCardEventListener(Listener listener) { mainModel.addCardEventListener(listener); } + @Override + public Object getCardsStore() { + return this.cards; + } + + @Override + public void clearCardsStoreBeforeUpdate() { + this.cards.clear(); + this.mageCards.clear(); + } + @Override public void loadCards(CardsView showCards, SortSetting sortSetting, BigCard bigCard, UUID gameId) { this.loadCards(showCards, sortSetting, bigCard, gameId, true); diff --git a/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java b/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java index 17810f923975..2e7e155a34fc 100644 --- a/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java +++ b/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java @@ -435,7 +435,7 @@ public void setDeckEditorMode(Constants.DeckEditorMode mode) { public enum Sort { NONE("No Sort", new CardViewNoneComparator()), CARD_TYPE("Card Type", new CardViewCardTypeComparator()), - CMC("Converted Mana Cost", new CardViewCostComparator()), + CMC("Mana Value", new CardViewCostComparator()), COLOR("Color", new CardViewColorComparator()), COLOR_IDENTITY("Color Identity", new CardViewColorIdentityComparator()), RARITY("Rarity", new CardViewRarityComparator()), @@ -1398,11 +1398,7 @@ public void reselectBy() { } // Casting cost if (!s) { - String mc = ""; - for (String m : card.getManaCost()) { - mc += m; - } - s |= mc.toLowerCase(Locale.ENGLISH).contains(searchStr); + s |= card.getManaCostStr().toLowerCase(Locale.ENGLISH).contains(searchStr); } // Rules if (!s) { @@ -1473,16 +1469,13 @@ public void analyseDeck() { } // Mana Cost - String mc = ""; - for (String m : card.getManaCost()) { - mc += m; - } + String mc = card.getManaCostStr(); mc = mc.replaceAll("\\{([WUBRG]).([WUBRG])\\}", "{$1}{$2}"); mc = mc.replaceAll("\\{", "#"); mc = mc.replaceAll("#2\\/", "#"); mc = mc.replaceAll("p}", "}"); mc = mc.toLowerCase(Locale.ENGLISH); - int cmc = card.getConvertedManaCost(); + int cmc = card.getManaValue(); // Do colorless mana pips Pattern regex = Pattern.compile("#([0-9]+)}"); diff --git a/Mage.Client/src/main/java/mage/client/cards/ICardGrid.java b/Mage.Client/src/main/java/mage/client/cards/ICardGrid.java index c036b8498d9b..99b1f9cee6a1 100644 --- a/Mage.Client/src/main/java/mage/client/cards/ICardGrid.java +++ b/Mage.Client/src/main/java/mage/client/cards/ICardGrid.java @@ -26,4 +26,11 @@ public interface ICardGrid { void refresh(); int cardsSize(); + + // only for debug, return inner cards list + Object getCardsStore(); + + // special memory optimization to clean inner cards list before new cards load, so you don't need 2x memory + // WARNING, you must call it in the same code as new cards list prepare + void clearCardsStoreBeforeUpdate(); } diff --git a/Mage.Client/src/main/java/mage/client/components/MageDesktopManager.java b/Mage.Client/src/main/java/mage/client/components/MageDesktopManager.java index da192c559abf..ecfd642ab727 100644 --- a/Mage.Client/src/main/java/mage/client/components/MageDesktopManager.java +++ b/Mage.Client/src/main/java/mage/client/components/MageDesktopManager.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.client.components; import java.awt.BorderLayout; diff --git a/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java b/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java index 374ba29c511e..1375095d5d1d 100644 --- a/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java +++ b/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java @@ -191,7 +191,7 @@ private static void generateSpells(CardCriteria criteria, int spellCount) { while (count < spellCount) { Card card = cardPool.get(RandomUtil.nextInt(retrievedCount)).getMockCard(); if (genPool.isValidSpellCard(card)) { - int cardCMC = card.getManaCost().convertedManaCost(); + int cardCMC = card.getManaValue(); for (DeckGeneratorCMC.CMC deckCMC : deckCMCs) { if (cardCMC >= deckCMC.min && cardCMC <= deckCMC.max) { int currentAmount = deckCMC.getAmount(); diff --git a/Mage.Client/src/main/java/mage/client/deck/generator/DeckGeneratorPool.java b/Mage.Client/src/main/java/mage/client/deck/generator/DeckGeneratorPool.java index d8dd0e06c278..2ba561b1ce98 100644 --- a/Mage.Client/src/main/java/mage/client/deck/generator/DeckGeneratorPool.java +++ b/Mage.Client/src/main/java/mage/client/deck/generator/DeckGeneratorPool.java @@ -178,7 +178,7 @@ public boolean tryAddReserve(Card card, int cardCMC) { * @return if all the mana symbols fit the chosen colors. */ private boolean cardFitsChosenColors(Card card) { - for (String symbol : card.getManaCost().getSymbols()) { + for (String symbol : card.getManaCostSymbols()) { boolean found = false; symbol = symbol.replace("{", "").replace("}", ""); if (isColoredManaSymbol(symbol)) { @@ -218,7 +218,7 @@ public Map calculateSpellColorPercentages() { List fixedSpells = getFixedSpells(); for(Card spell: fixedSpells) { - for (String symbol : spell.getManaCost().getSymbols()) { + for (String symbol : spell.getManaCostSymbols()) { symbol = symbol.replace("{", "").replace("}", ""); if (isColoredManaSymbol(symbol)) { for (ColoredManaSymbol allowed : allowedColors) { diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java index 0c2200120f77..cb83a88c32b7 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java @@ -22,17 +22,20 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; import mage.filter.predicate.mageobject.ColorlessPredicate; -import mage.filter.predicate.other.CardTextPredicate; -import mage.filter.predicate.other.ExpansionSetPredicate; +import mage.filter.predicate.card.CardTextPredicate; +import mage.filter.predicate.card.ExpansionSetPredicate; import mage.game.events.Listener; import mage.view.CardView; import mage.view.CardsView; +import org.apache.log4j.Logger; import org.mage.card.arcane.ManaSymbolsCellRenderer; import javax.swing.*; import javax.swing.table.DefaultTableCellRenderer; import java.awt.*; import java.awt.event.*; +import java.io.ByteArrayOutputStream; +import java.io.ObjectOutputStream; import java.util.List; import java.util.*; @@ -43,6 +46,8 @@ */ public class CardSelector extends javax.swing.JPanel implements ComponentListener, DragCardTarget { + private static final Logger logger = Logger.getLogger(CardSelector.class); + private final java.util.List cards = new ArrayList<>(); private BigCard bigCard; private boolean limited = false; @@ -412,6 +417,11 @@ private void filterCards() { FilterCard filter = buildFilter(); MageFrame.getDesktop().setCursor(new Cursor(Cursor.WAIT_CURSOR)); try { + + // debug + //debugObjectMemorySize("Old cards size", this.currentView.getCardsStore()); + this.currentView.clearCardsStoreBeforeUpdate(); + java.util.List filteredCards = new ArrayList<>(); if (chkPennyDreadful.isSelected() && pdAllowed.isEmpty()) { @@ -426,21 +436,32 @@ private void filterCards() { } } else { java.util.List foundCards = CardRepository.instance.findCards(buildCriteria()); + for (CardInfo cardInfo : foundCards) { - Card card = cardInfo.getMockCard(); - if (filter.match(card, null)) { - if (chkPennyDreadful.isSelected()) { - if (!pdAllowed.containsKey(card.getName())) { - continue; - } + // filter by penny + if (chkPennyDreadful.isSelected()) { + if (!pdAllowed.containsKey(cardInfo.getName())) { + continue; } - filteredCards.add(card); } + // filter by settings + Card card = cardInfo.getMockCard(); + if (!filter.match(card, null)) { + continue; + } + // found + filteredCards.add(card); } } + + // force to list mode on too much cards if (currentView instanceof CardGrid && filteredCards.size() > CardGrid.MAX_IMAGES) { this.toggleViewMode(); } + + // debug + //debugObjectMemorySize("New cards size", filteredCards); + this.currentView.loadCards(new CardsView(filteredCards), sortSetting, bigCard, null, false); this.cardCount.setText(String.valueOf(filteredCards.size())); } finally { @@ -448,6 +469,19 @@ private void filterCards() { } } + private void debugObjectMemorySize(String name, Object object) { + // just debug code, don't use it in production + // need 2x memory to find a size + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(object); + logger.info(name + ": " + baos.size()); + } catch (Throwable e) { + logger.fatal("Can't find object size: " + e.getMessage(), e); + } + } + public void setCardCount(int value) { this.cardCount.setText(String.valueOf(value)); } @@ -1212,6 +1246,7 @@ private void btnBoosterActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FI this.limited = true; cards.clear(); } + // accumulate boosters in one list ExpansionSet expansionSet = Sets.getInstance().get(sets.get(0)); if (expansionSet != null) { java.util.List booster = expansionSet.createBooster(); diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java index bf14225bbd53..d5e8afa1a7ee 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java @@ -711,7 +711,7 @@ private void importChoose(java.awt.event.ActionEvent evt) { Object[] options = {"From file", "From clipboard (new deck)", "From clipboard (append cards)"}; int n = JOptionPane.showOptionDialog(MageFrame.getDesktop(), - "Where would you like to import from?", + "Choose import location", "Deck import", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, @@ -817,7 +817,7 @@ private void exportChoose(java.awt.event.ActionEvent evt) { Object[] options = {"To file", "To clipboard"}; int n = JOptionPane.showOptionDialog(MageFrame.getDesktop(), - "Where would you like to export?", + "Choose export location", "Deck export", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/table/MageCardComparator.java b/Mage.Client/src/main/java/mage/client/deckeditor/table/MageCardComparator.java index 83185db8d8bf..1775689bfd71 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/table/MageCardComparator.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/table/MageCardComparator.java @@ -44,8 +44,8 @@ public int compare(CardView a, CardView b) { break; // Cost case 2: - aCom = a.getConvertedManaCost(); - bCom = b.getConvertedManaCost(); + aCom = a.getManaValue(); + bCom = b.getManaValue(); break; // Color case 3: diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/table/TableModel.java b/Mage.Client/src/main/java/mage/client/deckeditor/table/TableModel.java index 62b549e7ccd4..93e251641b6f 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/table/TableModel.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/table/TableModel.java @@ -71,6 +71,17 @@ public void clear() { this.view.clear(); } + @Override + public Object getCardsStore() { + return this.cards; + } + + @Override + public void clearCardsStoreBeforeUpdate() { + this.cards.clear(); + this.view.clear(); + } + @Override public void loadCards(CardsView showCards, SortSetting sortSetting, BigCard bigCard, UUID gameId) { this.loadCards(showCards, sortSetting, bigCard, gameId, true); @@ -215,7 +226,7 @@ private Object getColumn(Object obj, int column) { return c.getDisplayFullName(); // show full name in deck editor table, e.g. adventure with spell name case 2: // new svg images version - return ManaSymbols.getStringManaCost(c.getManaCost()); + return ManaSymbols.getClearManaCost(c.getManaCostStr()); /* // old html images version String manaCost = ""; diff --git a/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java b/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java index c833801f9fd9..a34543af42d9 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java @@ -473,12 +473,13 @@ private void autoAddLands() { land_number = 0; } for (Card cd : cards) { - Mana m = cd.getManaCost().getMana(); - red += m.getRed(); - green += m.getGreen(); - black += m.getBlack(); - blue += m.getBlue(); - white += m.getWhite(); + for (String s : cd.getManaCostSymbols()) { + if (s.contains("W")) white++; + if (s.contains("U")) blue++; + if (s.contains("B")) black++; + if (s.contains("R")) red++; + if (s.contains("G")) green++; + } } int total = red + green + black + blue + white; diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java index 454becb0dd59..4f2167c5fb71 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java @@ -522,6 +522,19 @@ private void btnOKActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:e return; } try { + // join AI + for (TablePlayerPanel player : players) { + if (player.getPlayerType() != PlayerType.HUMAN) { + if (!player.joinTable(roomId, table.getTableId())) { + // error message must be send by the server + SessionHandler.removeTable(roomId, table.getTableId()); + table = null; + return; + } + } + } + + // join itself if (SessionHandler.joinTable( roomId, table.getTableId(), @@ -529,16 +542,7 @@ private void btnOKActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:e PlayerType.HUMAN, 1, DeckImporter.importDeckFromFile(this.player1Panel.getDeckFile(), true), this.txtPassword.getText())) { - for (TablePlayerPanel player : players) { - if (player.getPlayerType() != PlayerType.HUMAN) { - if (!player.joinTable(roomId, table.getTableId())) { - // error message must be send by the server - SessionHandler.removeTable(roomId, table.getTableId()); - table = null; - return; - } - } - } + // all fine, can close create dialog (join dialog will be opened after feedback from server) this.hideDialog(); return; } diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java index bc884ff57d80..eaa9623baf1c 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java @@ -692,6 +692,20 @@ private void btnOkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:e // message must be send by server! return; } + + // join AI + for (TournamentPlayerPanel player : players) { + if (player.getPlayerType().getSelectedItem() != PlayerType.HUMAN) { + if (!player.joinTournamentTable(roomId, table.getTableId(), DeckImporter.importDeckFromFile(this.player1Panel.getDeckFile(), true))) { + // error message must be send by sever + SessionHandler.removeTable(roomId, table.getTableId()); + table = null; + return; + } + } + } + + // join itself if (SessionHandler.joinTournamentTable( roomId, table.getTableId(), @@ -699,19 +713,11 @@ private void btnOkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:e PlayerType.HUMAN, 1, DeckImporter.importDeckFromFile(this.player1Panel.getDeckFile(), true), tOptions.getPassword())) { - for (TournamentPlayerPanel player : players) { - if (player.getPlayerType().getSelectedItem() != PlayerType.HUMAN) { - if (!player.joinTournamentTable(roomId, table.getTableId(), DeckImporter.importDeckFromFile(this.player1Panel.getDeckFile(), true))) { - // error message must be send by sever - SessionHandler.removeTable(roomId, table.getTableId()); - table = null; - return; - } - } - } + // all fine, can close create dialog (join dialog will be opened after feedback from server) this.hideDialog(); return; } + JOptionPane.showMessageDialog(MageFrame.getDesktop(), "Error joining tournament.", "Error", JOptionPane.ERROR_MESSAGE); SessionHandler.removeTable(roomId, table.getTableId()); table = null; @@ -907,7 +913,7 @@ private void setNumberOfSwissRoundsMin(int numPlayers) { // set the number of minimum swiss rounds related to the number of players int minRounds = (int) Math.ceil(Math.log(numPlayers + 1) / Math.log(2)); int newValue = Math.max((Integer) spnNumRounds.getValue(), minRounds); - this.spnNumRounds.setModel(new SpinnerNumberModel(newValue, minRounds, 10, 1)); + this.spnNumRounds.setModel(new SpinnerNumberModel(newValue, 2, 10, 1)); this.pack(); this.revalidate(); this.repaint(); diff --git a/Mage.Client/src/main/java/mage/client/dialog/PickMultiNumberDialog.form b/Mage.Client/src/main/java/mage/client/dialog/PickMultiNumberDialog.form new file mode 100644 index 000000000000..a0252c10eb39 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/dialog/PickMultiNumberDialog.form @@ -0,0 +1,103 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Mage.Client/src/main/java/mage/client/dialog/PickMultiNumberDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PickMultiNumberDialog.java new file mode 100644 index 000000000000..550cf72640b2 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/dialog/PickMultiNumberDialog.java @@ -0,0 +1,215 @@ +package mage.client.dialog; + +import mage.constants.ColoredManaSymbol; +import org.mage.card.arcane.ManaSymbols; + +import javax.swing.*; +import java.awt.*; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * + * @author weirddan455 + */ +public class PickMultiNumberDialog extends MageDialog { + + private List labelList = null; + private List spinnerList = null; + + public PickMultiNumberDialog() { + initComponents(); + this.setModal(true); + } + + public void showDialog(List messages, int min, int max, Map options) { + this.header.setText((String) options.get("header")); + this.header.setHorizontalAlignment(SwingConstants.CENTER); + this.setTitle((String) options.get("title")); + + if (labelList != null) { + for (JLabel label : labelList) { + jPanel1.remove(label); + } + } + if (spinnerList != null) { + for (JSpinner spinner : spinnerList) { + jPanel1.remove(spinner); + } + } + int size = messages.size(); + labelList = new ArrayList<>(size); + spinnerList = new ArrayList<>(size); + jPanel1.setLayout(new GridBagLayout()); + GridBagConstraints labelC = new GridBagConstraints(); + GridBagConstraints spinnerC = new GridBagConstraints(); + for (int i = 0; i < size; i++) { + JLabel label = new JLabel(); + + // mana mode + String manaText = null; + String input = messages.get(i); + switch (input) { + case "W": + manaText = ColoredManaSymbol.W.getColorHtmlName(); + break; + case "U": + manaText = ColoredManaSymbol.U.getColorHtmlName(); + break; + case "B": + manaText = ColoredManaSymbol.B.getColorHtmlName(); + break; + case "R": + manaText = ColoredManaSymbol.R.getColorHtmlName(); + break; + case "G": + manaText = ColoredManaSymbol.G.getColorHtmlName(); + break; + } + if (manaText != null) { + label.setText("" + manaText); + Image image = ManaSymbols.getSizedManaSymbol(input); + if (image != null) { + label.setIcon(new ImageIcon(image)); + } + } else { + // text mode + label.setText("" + input); + } + + labelC.weightx = 0.5; + labelC.gridx = 0; + labelC.gridy = i; + jPanel1.add(label, labelC); + labelList.add(label); + + JSpinner spinner = new JSpinner(); + spinner.setModel(new SpinnerNumberModel(0, 0, max, 1)); + spinnerC.weightx = 0.5; + spinnerC.gridx = 1; + spinnerC.gridy = i; + spinnerC.ipadx = 20; + spinner.addChangeListener(e -> { + updateControls(min, max); + }); + jPanel1.add(spinner, spinnerC); + spinnerList.add(spinner); + } + this.counterText.setText("0 out of 0"); + this.counterText.setHorizontalAlignment(SwingConstants.CENTER); + + updateControls(min, max); + + this.pack(); + this.makeWindowCentered(); + this.setVisible(true); + } + + private void updateControls(int min, int max) { + int totalChosenAmount = 0; + for (JSpinner jSpinner : spinnerList) { + totalChosenAmount += ((Number) jSpinner.getValue()).intValue(); + } + counterText.setText(totalChosenAmount + " out of " + max); + chooseButton.setEnabled(totalChosenAmount >= min && totalChosenAmount <= max); + } + + public String getMultiAmount() { + return spinnerList + .stream() + .map(spinner -> ((Number) spinner.getValue()).intValue()) + .map(String::valueOf) + .collect(Collectors.joining(" ")); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + chooseButton = new javax.swing.JButton(); + header = new javax.swing.JLabel(); + counterText = new javax.swing.JLabel(); + jScrollPane1 = new javax.swing.JScrollPane(); + jPanel1 = new javax.swing.JPanel(); + + chooseButton.setText("Choose"); + chooseButton.setEnabled(false); + chooseButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + chooseButtonActionPerformed(evt); + } + }); + + header.setText("Header"); + + counterText.setText("Counter"); + + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); + jPanel1.setLayout(jPanel1Layout); + jPanel1Layout.setHorizontalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 413, Short.MAX_VALUE) + ); + jPanel1Layout.setVerticalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 273, Short.MAX_VALUE) + ); + + jScrollPane1.setViewportView(jPanel1); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGap(12, 12, 12) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(header, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(counterText, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) + .addGroup(layout.createSequentialGroup() + .addGap(184, 184, 184) + .addComponent(chooseButton) + .addGap(0, 172, Short.MAX_VALUE)) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(jScrollPane1))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addComponent(header) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(counterText) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 276, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(chooseButton) + .addContainerGap()) + ); + }// //GEN-END:initComponents + + private void chooseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chooseButtonActionPerformed + this.hideDialog(); + }//GEN-LAST:event_chooseButtonActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton chooseButton; + private javax.swing.JLabel counterText; + private javax.swing.JLabel header; + private javax.swing.JPanel jPanel1; + private javax.swing.JScrollPane jScrollPane1; + // End of variables declaration//GEN-END:variables +} diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form index 013b40f3e7e0..a2eff5066ea5 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form @@ -348,8 +348,8 @@ - - + + @@ -527,6 +527,9 @@ + + + @@ -564,6 +567,9 @@ + + + @@ -601,6 +607,9 @@ + + + @@ -641,6 +650,9 @@ + + + @@ -678,6 +690,9 @@ + + + @@ -715,6 +730,9 @@ + + + @@ -1992,6 +2010,9 @@ + + + @@ -2029,6 +2050,9 @@ + + + @@ -2066,6 +2090,9 @@ + + + @@ -2103,6 +2130,9 @@ + + + @@ -2144,6 +2174,9 @@ + + + @@ -2184,6 +2217,9 @@ + + + @@ -2225,6 +2261,9 @@ + + + diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java index 60b04022aea0..c175326c4a9e 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java @@ -35,7 +35,7 @@ import static mage.constants.Constants.*; /** - * @author nantuko, JayDi85 + * @author nantuko, JayDi85, leemi */ public class PreferencesDialog extends javax.swing.JDialog { @@ -320,7 +320,9 @@ public class PreferencesDialog extends javax.swing.JDialog { private static int selectedAvatarId; private static ThemeType currentTheme = null; - + + private static boolean ignoreGUISizeSliderStateChangedEvent = false; + public static ThemeType getCurrentTheme() { if (currentTheme == null) { currentTheme = ThemeType.valueByName(getCachedValue(KEY_THEME, "Default")); @@ -739,8 +741,8 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { }); showAbilityPickerForced.setSelected(true); - showAbilityPickerForced.setText("Show ability picker for abilities or spells without costs"); - showAbilityPickerForced.setToolTipText("This prevents you from accidently activating abilities without other costs than tapping or casting spells with 0 mana costs."); + showAbilityPickerForced.setText("Show ability picker for 1 available option (spells without costs, mdf/split side, adventure)"); + showAbilityPickerForced.setToolTipText("This prevents you from accidently activating abilities what you don't want (example: if you haven't mana to cast main side, but clicks on mdf card and play land instead)"); showAbilityPickerForced.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); showAbilityPickerForced.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -919,6 +921,11 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { sliderFontSize.setToolTipText("The size of the font used to display table text."); sliderFontSize.setBorder(javax.swing.BorderFactory.createEtchedBorder()); sliderFontSize.setMinimumSize(new java.awt.Dimension(150, 40)); + sliderFontSize.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + sliderGUISizeStateChanged(evt); + } + }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 0; @@ -948,6 +955,11 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { sliderChatFontSize.setToolTipText("The size of the font used to display the chat text"); sliderChatFontSize.setBorder(javax.swing.BorderFactory.createEtchedBorder()); sliderChatFontSize.setMinimumSize(new java.awt.Dimension(150, 40)); + sliderChatFontSize.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + sliderGUISizeStateChanged(evt); + } + }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 0; @@ -977,6 +989,11 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { sliderDialogFont.setToolTipText("The size of the font of messages and menues"); sliderDialogFont.setBorder(javax.swing.BorderFactory.createEtchedBorder()); sliderDialogFont.setMinimumSize(new java.awt.Dimension(150, 40)); + sliderDialogFont.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + sliderGUISizeStateChanged(evt); + } + }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 0; @@ -1007,6 +1024,11 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { sliderEditorCardSize.setToolTipText("The size of the card in editor and the picked zone of the draft panel"); sliderEditorCardSize.setBorder(javax.swing.BorderFactory.createEtchedBorder()); sliderEditorCardSize.setMinimumSize(new java.awt.Dimension(150, 40)); + sliderEditorCardSize.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + sliderGUISizeStateChanged(evt); + } + }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 2; @@ -1036,6 +1058,11 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { sliderEditorCardOffset.setToolTipText("The size of the card in editor and the picked zone of the draft panel"); sliderEditorCardOffset.setBorder(javax.swing.BorderFactory.createEtchedBorder()); sliderEditorCardOffset.setMinimumSize(new java.awt.Dimension(150, 40)); + sliderEditorCardOffset.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + sliderGUISizeStateChanged(evt); + } + }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 2; @@ -1065,6 +1092,11 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { sliderEnlargedImageSize.setToolTipText("The size of the image shown for the card your mouse pointer
is located over while you turn the mouse wheel "); sliderEnlargedImageSize.setBorder(javax.swing.BorderFactory.createEtchedBorder()); sliderEnlargedImageSize.setMinimumSize(new java.awt.Dimension(150, 40)); + sliderEnlargedImageSize.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + sliderGUISizeStateChanged(evt); + } + }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 2; @@ -1110,6 +1142,11 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { sliderCardSizeHand.setValue(14); sliderCardSizeHand.setBorder(javax.swing.BorderFactory.createEtchedBorder()); sliderCardSizeHand.setMinimumSize(new java.awt.Dimension(150, 40)); + sliderCardSizeHand.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + sliderGUISizeStateChanged(evt); + } + }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 0; @@ -1139,6 +1176,11 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { sliderCardSizeOtherZones.setToolTipText("The size of card in other game zone (e.g. graveyard, revealed cards etc.)"); sliderCardSizeOtherZones.setBorder(javax.swing.BorderFactory.createEtchedBorder()); sliderCardSizeOtherZones.setMinimumSize(new java.awt.Dimension(150, 40)); + sliderCardSizeOtherZones.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + sliderGUISizeStateChanged(evt); + } + }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 0; @@ -1168,6 +1210,11 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { sliderCardSizeMinBattlefield.setToolTipText("The minimum size of permanents on the battlefield"); sliderCardSizeMinBattlefield.setBorder(javax.swing.BorderFactory.createEtchedBorder()); sliderCardSizeMinBattlefield.setMinimumSize(new java.awt.Dimension(150, 40)); + sliderCardSizeMinBattlefield.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + sliderGUISizeStateChanged(evt); + } + }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 0; @@ -1197,6 +1244,11 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { sliderCardSizeMaxBattlefield.setToolTipText("The maximum size of permanents on the battlefield"); sliderCardSizeMaxBattlefield.setBorder(javax.swing.BorderFactory.createEtchedBorder()); sliderCardSizeMaxBattlefield.setMinimumSize(new java.awt.Dimension(150, 40)); + sliderCardSizeMaxBattlefield.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + sliderGUISizeStateChanged(evt); + } + }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 3; gridBagConstraints.gridy = 0; @@ -1227,6 +1279,11 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { sliderStackWidth.setValue(30); sliderStackWidth.setBorder(javax.swing.BorderFactory.createEtchedBorder()); sliderStackWidth.setMinimumSize(new java.awt.Dimension(150, 40)); + sliderStackWidth.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + sliderGUISizeStateChanged(evt); + } + }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 2; @@ -1258,6 +1315,11 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { sliderGameFeedbackArea.setToolTipText("The size of the game feedback area (buttons and messages above the hand area)"); sliderGameFeedbackArea.setBorder(javax.swing.BorderFactory.createEtchedBorder()); sliderGameFeedbackArea.setMinimumSize(new java.awt.Dimension(150, 40)); + sliderGameFeedbackArea.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + sliderGUISizeStateChanged(evt); + } + }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 2; @@ -1289,6 +1351,11 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { sliderTooltipSize.setValue(14); sliderTooltipSize.setBorder(javax.swing.BorderFactory.createEtchedBorder()); sliderTooltipSize.setMinimumSize(new java.awt.Dimension(150, 40)); + sliderTooltipSize.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + sliderGUISizeStateChanged(evt); + } + }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 2; @@ -2830,64 +2897,7 @@ private void saveButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FI } } - // GUI Size - boolean sizeGUIChanged = false; - if (getCachedValue(KEY_GUI_TABLE_FONT_SIZE, 14) != dialog.sliderFontSize.getValue()) { - save(prefs, dialog.sliderFontSize, KEY_GUI_TABLE_FONT_SIZE, "true", "false", UPDATE_CACHE_POLICY); - sizeGUIChanged = true; - } - if (getCachedValue(KEY_GUI_CHAT_FONT_SIZE, 14) != dialog.sliderChatFontSize.getValue()) { - save(prefs, dialog.sliderChatFontSize, KEY_GUI_CHAT_FONT_SIZE, "true", "false", UPDATE_CACHE_POLICY); - sizeGUIChanged = true; - } - if (getCachedValue(KEY_GUI_CARD_HAND_SIZE, 14) != dialog.sliderCardSizeHand.getValue()) { - save(prefs, dialog.sliderCardSizeHand, KEY_GUI_CARD_HAND_SIZE, "true", "false", UPDATE_CACHE_POLICY); - sizeGUIChanged = true; - } - if (getCachedValue(KEY_GUI_CARD_EDITOR_SIZE, 14) != dialog.sliderEditorCardSize.getValue()) { - save(prefs, dialog.sliderEditorCardSize, KEY_GUI_CARD_EDITOR_SIZE, "true", "false", UPDATE_CACHE_POLICY); - sizeGUIChanged = true; - } - if (getCachedValue(KEY_GUI_CARD_OFFSET_SIZE, 14) != dialog.sliderEditorCardOffset.getValue()) { - save(prefs, dialog.sliderEditorCardOffset, KEY_GUI_CARD_OFFSET_SIZE, "true", "false", UPDATE_CACHE_POLICY); - sizeGUIChanged = true; - } - if (getCachedValue(KEY_GUI_ENLARGED_IMAGE_SIZE, 20) != dialog.sliderEnlargedImageSize.getValue()) { - save(prefs, dialog.sliderEnlargedImageSize, KEY_GUI_ENLARGED_IMAGE_SIZE, "true", "false", UPDATE_CACHE_POLICY); - sizeGUIChanged = true; - } - if (getCachedValue(KEY_GUI_STACK_WIDTH, 30) != dialog.sliderStackWidth.getValue()) { - save(prefs, dialog.sliderStackWidth, KEY_GUI_STACK_WIDTH, "true", "false", UPDATE_CACHE_POLICY); - sizeGUIChanged = true; - } - if (getCachedValue(KEY_GUI_TOOLTIP_SIZE, 14) != dialog.sliderTooltipSize.getValue()) { - save(prefs, dialog.sliderTooltipSize, KEY_GUI_TOOLTIP_SIZE, "true", "false", UPDATE_CACHE_POLICY); - sizeGUIChanged = true; - } - if (getCachedValue(KEY_GUI_DIALOG_FONT_SIZE, 14) != dialog.sliderDialogFont.getValue()) { - save(prefs, dialog.sliderDialogFont, KEY_GUI_DIALOG_FONT_SIZE, "true", "false", UPDATE_CACHE_POLICY); - sizeGUIChanged = true; - } - if (getCachedValue(KEY_GUI_FEEDBACK_AREA_SIZE, 14) != dialog.sliderGameFeedbackArea.getValue()) { - save(prefs, dialog.sliderGameFeedbackArea, KEY_GUI_FEEDBACK_AREA_SIZE, "true", "false", UPDATE_CACHE_POLICY); - sizeGUIChanged = true; - } - if (getCachedValue(KEY_GUI_CARD_OTHER_ZONES_SIZE, 14) != dialog.sliderCardSizeOtherZones.getValue()) { - save(prefs, dialog.sliderCardSizeOtherZones, KEY_GUI_CARD_OTHER_ZONES_SIZE, "true", "false", UPDATE_CACHE_POLICY); - sizeGUIChanged = true; - } - if (getCachedValue(KEY_GUI_CARD_BATTLEFIELD_MIN_SIZE, 10) != dialog.sliderCardSizeMaxBattlefield.getValue()) { - save(prefs, dialog.sliderCardSizeMinBattlefield, KEY_GUI_CARD_BATTLEFIELD_MIN_SIZE, "true", "false", UPDATE_CACHE_POLICY); - sizeGUIChanged = true; - } - if (getCachedValue(KEY_GUI_CARD_BATTLEFIELD_MAX_SIZE, 14) != dialog.sliderCardSizeMaxBattlefield.getValue()) { - save(prefs, dialog.sliderCardSizeMaxBattlefield, KEY_GUI_CARD_BATTLEFIELD_MAX_SIZE, "true", "false", UPDATE_CACHE_POLICY); - sizeGUIChanged = true; - } - if (sizeGUIChanged) { - // do as worker job - GUISizeHelper.changeGUISize(); - } + saveGUISize(); // Phases & Priority save(prefs, dialog.checkBoxUpkeepYou, UPKEEP_YOU); @@ -2991,6 +3001,28 @@ private void saveButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FI dialog.setVisible(false); }//GEN-LAST:event_saveButtonActionPerformed + private void saveGUISize() { + Preferences prefs = MageFrame.getPreferences(); + + // GUI Size + save(prefs, dialog.sliderFontSize, KEY_GUI_TABLE_FONT_SIZE, "true", "false", UPDATE_CACHE_POLICY); + save(prefs, dialog.sliderChatFontSize, KEY_GUI_CHAT_FONT_SIZE, "true", "false", UPDATE_CACHE_POLICY); + save(prefs, dialog.sliderCardSizeHand, KEY_GUI_CARD_HAND_SIZE, "true", "false", UPDATE_CACHE_POLICY); + save(prefs, dialog.sliderEditorCardSize, KEY_GUI_CARD_EDITOR_SIZE, "true", "false", UPDATE_CACHE_POLICY); + save(prefs, dialog.sliderEditorCardOffset, KEY_GUI_CARD_OFFSET_SIZE, "true", "false", UPDATE_CACHE_POLICY); + save(prefs, dialog.sliderEnlargedImageSize, KEY_GUI_ENLARGED_IMAGE_SIZE, "true", "false", UPDATE_CACHE_POLICY); + save(prefs, dialog.sliderStackWidth, KEY_GUI_STACK_WIDTH, "true", "false", UPDATE_CACHE_POLICY); + save(prefs, dialog.sliderTooltipSize, KEY_GUI_TOOLTIP_SIZE, "true", "false", UPDATE_CACHE_POLICY); + save(prefs, dialog.sliderDialogFont, KEY_GUI_DIALOG_FONT_SIZE, "true", "false", UPDATE_CACHE_POLICY); + save(prefs, dialog.sliderGameFeedbackArea, KEY_GUI_FEEDBACK_AREA_SIZE, "true", "false", UPDATE_CACHE_POLICY); + save(prefs, dialog.sliderCardSizeOtherZones, KEY_GUI_CARD_OTHER_ZONES_SIZE, "true", "false", UPDATE_CACHE_POLICY); + save(prefs, dialog.sliderCardSizeMinBattlefield, KEY_GUI_CARD_BATTLEFIELD_MIN_SIZE, "true", "false", UPDATE_CACHE_POLICY); + save(prefs, dialog.sliderCardSizeMaxBattlefield, KEY_GUI_CARD_BATTLEFIELD_MAX_SIZE, "true", "false", UPDATE_CACHE_POLICY); + + // do as worker job + GUISizeHelper.changeGUISize(); + } + private void exitButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exitButtonActionPerformed dialog.setVisible(false); }//GEN-LAST:event_exitButtonActionPerformed @@ -3268,6 +3300,15 @@ private void cbThemeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST // TODO add your handling code here: }//GEN-LAST:event_cbThemeActionPerformed + private void sliderGUISizeStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_sliderGUISizeStateChanged + // This prevents this event from firing during the initial + // setting of the sliders from pref values + if (!ignoreGUISizeSliderStateChangedEvent) + { + saveGUISize(); + } + }//GEN-LAST:event_sliderGUISizeStateChanged + private void showProxySettings() { Connection.ProxyType proxyType = (Connection.ProxyType) cbProxyType.getSelectedItem(); switch (proxyType) { @@ -3428,6 +3469,7 @@ private static void loadPhases(Preferences prefs) { } private static void loadGuiSize(Preferences prefs) { + ignoreGUISizeSliderStateChangedEvent = true; load(prefs, dialog.sliderFontSize, KEY_GUI_TABLE_FONT_SIZE, "14"); load(prefs, dialog.sliderChatFontSize, KEY_GUI_CHAT_FONT_SIZE, "14"); load(prefs, dialog.sliderCardSizeHand, KEY_GUI_CARD_HAND_SIZE, "14"); @@ -3441,6 +3483,7 @@ private static void loadGuiSize(Preferences prefs) { load(prefs, dialog.sliderCardSizeOtherZones, KEY_GUI_CARD_OTHER_ZONES_SIZE, "14"); load(prefs, dialog.sliderCardSizeMinBattlefield, KEY_GUI_CARD_BATTLEFIELD_MIN_SIZE, "10"); load(prefs, dialog.sliderCardSizeMaxBattlefield, KEY_GUI_CARD_BATTLEFIELD_MAX_SIZE, "14"); + ignoreGUISizeSliderStateChangedEvent = false; } private static void loadImagesSettings(Preferences prefs) { diff --git a/Mage.Client/src/main/java/mage/client/dialog/RandomPacksSelectorDialog.java b/Mage.Client/src/main/java/mage/client/dialog/RandomPacksSelectorDialog.java index c74046b65e31..2102682b5ae8 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/RandomPacksSelectorDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/RandomPacksSelectorDialog.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.client.dialog; import java.awt.Component; diff --git a/Mage.Client/src/main/java/mage/client/game/GamePanel.java b/Mage.Client/src/main/java/mage/client/game/GamePanel.java index 937b2d1a816b..f33661e1226e 100644 --- a/Mage.Client/src/main/java/mage/client/game/GamePanel.java +++ b/Mage.Client/src/main/java/mage/client/game/GamePanel.java @@ -86,6 +86,7 @@ public final class GamePanel extends javax.swing.JPanel { GamePane gamePane; private ReplayTask replayTask; private final PickNumberDialog pickNumber; + private final PickMultiNumberDialog pickMultiNumber; private JLayeredPane jLayeredPane; private String chosenHandKey = "You"; private boolean smallMode = false; @@ -134,6 +135,9 @@ public GamePanel() { pickNumber = new PickNumberDialog(); MageFrame.getDesktop().add(pickNumber, JLayeredPane.MODAL_LAYER); + pickMultiNumber = new PickMultiNumberDialog(); + MageFrame.getDesktop().add(pickMultiNumber, JLayeredPane.MODAL_LAYER); + this.feedbackPanel.setConnectedChatPanel(this.userChatPanel); // Override layout (I can't edit generated code) @@ -238,6 +242,9 @@ public void cleanUp() { if (pickNumber != null) { pickNumber.removeDialog(); } + if (pickMultiNumber != null) { + pickMultiNumber.removeDialog(); + } for (CardInfoWindowDialog exileDialog : exiles.values()) { exileDialog.cleanUp(); exileDialog.removeDialog(); @@ -1617,6 +1624,11 @@ public void getAmount(int min, int max, String message) { } } + public void getMultiAmount(List messages, int min, int max, Map options) { + pickMultiNumber.showDialog(messages, min, max, options); + SessionHandler.sendPlayerString(gameId, pickMultiNumber.getMultiAmount()); + } + public void getChoice(Choice choice, UUID objectId) { hideAll(); // TODO: remember last choices and search incremental for same events? diff --git a/Mage.Client/src/main/java/mage/client/game/HelperPanel.java b/Mage.Client/src/main/java/mage/client/game/HelperPanel.java index b7f0c686db86..0f3a7e57a497 100644 --- a/Mage.Client/src/main/java/mage/client/game/HelperPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/HelperPanel.java @@ -310,6 +310,14 @@ public void setRight(String txtRight, boolean rightVisible) { public void setGameNeedFeedback(boolean need, TurnPhase gameTurnPhase) { this.gameNeedFeedback = need; this.gameTurnPhase = gameTurnPhase; + + if (this.gameNeedFeedback) { + // start notification sound timer + this.needFeedbackTimer.restart(); + } else { + // stop notification sound timer + this.needFeedbackTimer.stop(); + } } public void autoSizeButtonsAndFeedbackState() { @@ -343,9 +351,6 @@ public void autoSizeButtonsAndFeedbackState() { // color panel on player's feedback waiting if (this.gameNeedFeedback) { - // start notification sound timer - this.needFeedbackTimer.restart(); - // wait player's action switch (FEEDBACK_COLORIZING_MODE) { case Constants.BATTLEFIELD_FEEDBACK_COLORIZING_MODE_DISABLE: @@ -383,9 +388,6 @@ public void autoSizeButtonsAndFeedbackState() { break; } } else { - // stop notification sound timer - this.needFeedbackTimer.stop(); - // inform about other players this.mainPanel.setOpaque(false); } diff --git a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java index 7a12eda92e26..8b7e42099dd3 100644 --- a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java +++ b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java @@ -297,6 +297,18 @@ public synchronized void processCallback(final ClientCallback callback) { break; } + case GAME_GET_MULTI_AMOUNT: { + GameClientMessage message = (GameClientMessage) callback.getData(); + + GamePanel panel = MageFrame.getGame(callback.getObjectId()); + if (panel != null) { + appendJsonEvent("GAME_GET_MULTI_AMOUNT", callback.getObjectId(), message); + + panel.getMultiAmount(message.getMessages(), message.getMin(), message.getMax(), message.getOptions()); + } + break; + } + case GAME_UPDATE: { GamePanel panel = MageFrame.getGame(callback.getObjectId()); @@ -608,7 +620,7 @@ private void handleException(Exception ex) { logger.fatal("Client error\n", ex); String errorMessage = ex.getMessage(); if (errorMessage == null || errorMessage.isEmpty() || errorMessage.equals("Null")) { - errorMessage = ex.getClass().getSimpleName() + " - look server logs for more details"; + errorMessage = ex.getClass().getSimpleName() + " - look server or client logs for more details"; } frame.showError("Server's error: " + errorMessage); } diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java index b3902afefa2e..a898f36f9b85 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -896,7 +896,7 @@ public void setTableFilter() { formatFilterList.add(RowFilter.regexFilter("^Limited", TablesTableModel.COLUMN_DECK_TYPE)); } if (btnFormatOther.isSelected()) { - formatFilterList.add(RowFilter.regexFilter("^Momir Basic|^Constructed - Pauper|^Constructed - Frontier|^Constructed - Extended|^Constructed - Eternal|^Constructed - Historical|^Constructed - Super|^Constructed - Freeform|^Australian Highlander|^Canadian Highlander|^Constructed - Old|^Constructed - Historic", TablesTableModel.COLUMN_DECK_TYPE)); + formatFilterList.add(RowFilter.regexFilter("^Momir Basic|^Constructed - Pauper|^Constructed - Frontier|^Constructed - Extended|^Constructed - Eternal|^Constructed - Historical|^Constructed - Super|^Constructed - Freeform|^Australian Highlander|^European Highlander|^Canadian Highlander|^Constructed - Old|^Constructed - Historic", TablesTableModel.COLUMN_DECK_TYPE)); } // skill diff --git a/Mage.Client/src/main/java/mage/client/util/TransformedImageCache.java b/Mage.Client/src/main/java/mage/client/util/TransformedImageCache.java index ec878de7f273..eee76a8d0b5a 100644 --- a/Mage.Client/src/main/java/mage/client/util/TransformedImageCache.java +++ b/Mage.Client/src/main/java/mage/client/util/TransformedImageCache.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.client.util; import java.awt.*; diff --git a/Mage.Client/src/main/java/mage/client/util/URLHandler.java b/Mage.Client/src/main/java/mage/client/util/URLHandler.java index 19ee86f638da..e5ea0bc26751 100644 --- a/Mage.Client/src/main/java/mage/client/util/URLHandler.java +++ b/Mage.Client/src/main/java/mage/client/util/URLHandler.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.client.util; import java.awt.Desktop; diff --git a/Mage.Client/src/main/java/mage/client/util/comparators/CardViewColorIdentityComparator.java b/Mage.Client/src/main/java/mage/client/util/comparators/CardViewColorIdentityComparator.java index f644f69eee8a..c66f110877aa 100644 --- a/Mage.Client/src/main/java/mage/client/util/comparators/CardViewColorIdentityComparator.java +++ b/Mage.Client/src/main/java/mage/client/util/comparators/CardViewColorIdentityComparator.java @@ -24,7 +24,7 @@ public String getCategoryName(CardView sample) { } public static FilterMana calcIdentity(CardView cardView) { - return ManaUtil.getColorIdentity(cardView.getColor(), cardView.getManaCost(), cardView.getRules(), null); + return ManaUtil.getColorIdentity(cardView.getColor(), cardView.getManaCostStr(), cardView.getRules(), null); } public static int calcHash(CardView cardView) { diff --git a/Mage.Client/src/main/java/mage/client/util/comparators/CardViewCostComparator.java b/Mage.Client/src/main/java/mage/client/util/comparators/CardViewCostComparator.java index 4fdf0dc01f8f..a7ec40bec6c8 100644 --- a/Mage.Client/src/main/java/mage/client/util/comparators/CardViewCostComparator.java +++ b/Mage.Client/src/main/java/mage/client/util/comparators/CardViewCostComparator.java @@ -9,11 +9,11 @@ public class CardViewCostComparator implements CardViewComparator { @Override public int compare(CardView o1, CardView o2) { - return Integer.compare(o1.getConvertedManaCost(), o2.getConvertedManaCost()); + return Integer.compare(o1.getManaValue(), o2.getManaValue()); } @Override public String getCategoryName(CardView sample) { - return "CMC: " + sample.getConvertedManaCost(); + return "CMC: " + sample.getManaValue(); } } diff --git a/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java b/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java index 18ecf4b509c3..9e860593aa2d 100644 --- a/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java +++ b/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java @@ -97,7 +97,7 @@ public static JXPanel getDescription(CardView card, int width, int height) { j.add(cardText); TextLines textLines = GuiDisplayUtil.getTextLinesfromCardView(card); - cardText.setText(getRulefromCardView(card, textLines).toString()); + cardText.setText(getRulesFromCardView(card, textLines).toString()); descriptionPanel.add(j); @@ -227,11 +227,8 @@ public static String getHintIconHtml(String iconName, int symbolSize) { return "" + iconName + ""; } - public static StringBuilder getRulefromCardView(CardView card, TextLines textLines) { - String manaCost = ""; - for (String m : card.getManaCost()) { - manaCost += m; - } + public static StringBuilder getRulesFromCardView(CardView card, TextLines textLines) { + String manaCost = card.getManaCostStr(); String castingCost = UI.getDisplayManaCost(manaCost); castingCost = ManaSymbols.replaceSymbolsWithHTML(castingCost, ManaSymbols.Type.TOOLTIP); @@ -347,9 +344,9 @@ else if (prefs.fontFamily == CardFontFamily.verdana) { rule.append(""); rule.append(card.getLeftSplitName()); rule.append(""); - rule.append(card.getLeftSplitCosts().getText()); + rule.append(card.getLeftSplitCostsStr()); rule.append(""); for (String ruling : card.getLeftSplitRules()) { if (ruling != null && !ruling.replace(".", "").trim().isEmpty()) { @@ -362,9 +359,9 @@ else if (prefs.fontFamily == CardFontFamily.verdana) { rule.append(""); rule.append(card.getRightSplitName()); rule.append(""); - rule.append(card.getRightSplitCosts().getText()); + rule.append(card.getRightSplitCostsStr()); rule.append(""); for (String ruling : card.getRightSplitRules()) { if (ruling != null && !ruling.replace(".", "").trim().isEmpty()) { diff --git a/Mage.Client/src/main/java/mage/client/util/gui/TableUtil.java b/Mage.Client/src/main/java/mage/client/util/gui/TableUtil.java index 5ab04302863d..d422eb2847e0 100644 --- a/Mage.Client/src/main/java/mage/client/util/gui/TableUtil.java +++ b/Mage.Client/src/main/java/mage/client/util/gui/TableUtil.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.client.util.gui; import javax.swing.JTable; diff --git a/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryUtil.java b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryUtil.java index e167eff5b565..6154310bc08f 100644 --- a/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryUtil.java +++ b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryUtil.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.client.util.gui.countryBox; import java.awt.Image; diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java index 038abf71eb1e..3ecda635ac34 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java @@ -789,8 +789,8 @@ protected final String getText(String cardType, CardView card) { } } else { sb.append(card.getName()); - if (!card.getManaCost().isEmpty()) { - sb.append('\n').append(card.getManaCost()); + if (!card.getManaCostStr().isEmpty()) { + sb.append('\n').append(card.getManaCostStr()); } sb.append('\n').append(cardType); if (card.getColor().hasColor()) { diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeImage.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeImage.java index 65ee832b7a71..631f453327f8 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeImage.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeImage.java @@ -529,7 +529,7 @@ protected void paintChildren(Graphics g) { int symbolMarginX = 2; // 2 px between icons - String manaCost = ManaSymbols.getStringManaCost(getGameCard().getManaCost()); + String manaCost = ManaSymbols.getClearManaCost(getGameCard().getManaCostStr()); int manaWidth = getManaWidth(manaCost, symbolMarginX); // right top corner with margin (sizes from any sample card, length from black border to mana icon) @@ -563,11 +563,23 @@ private int getManaWidth(String manaCost, int symbolMarginX) { public void doLayout() { super.doLayout(); - int cardWidth = getCardLocation().getCardWidth(); // must use current panel sizes to scale real image - int cardHeight = getCardLocation().getCardHeight(); + int cardWidth; + int cardHeight; int cardXOffset = 0; int cardYOffset = 0; + // workaround to fix a rare NPE error with image loading + // reason: panel runs image load in another thread and that thread can be completed before top panel init, see updateArtImage + if (getTopPanelRef() == null) { + //noinspection deprecation - it's ok for workaround + cardWidth = this.getWidth(); + //noinspection deprecation - it's ok for workaround + cardHeight = this.getHeight(); + } else { + cardWidth = getCardLocation().getCardWidth(); // must use real card sizes to scale real image + cardHeight = getCardLocation().getCardHeight(); + } + CardSizes sizes = new CardSizes(getInsets(), cardXOffset, cardYOffset, cardWidth, cardHeight); // origin card without selection diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeMTGO.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeMTGO.java index 00315d0532ed..54426314c429 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeMTGO.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeMTGO.java @@ -56,7 +56,7 @@ private static boolean cardViewEquals(CardView a, CardView b) { if (!a.getSuperTypes().equals(b.getSuperTypes())) { return false; } - if (!a.getManaCost().equals(b.getManaCost())) { + if (!a.getManaCostStr().equals(b.getManaCostStr())) { return false; } if (!a.getRules().equals(b.getRules())) { @@ -166,9 +166,7 @@ private int hashCodeImpl() { for (SubType s : this.view.getSubTypes()) { sb.append(s); } - for (String s : this.view.getManaCost()) { - sb.append(s); - } + sb.append(this.view.getManaCostStr()); for (String s : this.view.getRules()) { sb.append(s); } diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java index bdb099bac931..5a2e2870d1d3 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.card.arcane; import mage.abilities.hint.HintUtils; diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java index 4c09becc729c..350d87fd3e85 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java @@ -550,18 +550,35 @@ public static void draw(Graphics g, String manaCost, int x, int y, int symbolWid } - public static String getStringManaCost(java.util.List manaCost) { + public static String getClearManaCost(java.util.List manaCost) { StringBuilder sb = new StringBuilder(); for (String s : manaCost) { sb.append(s); } - return sb.toString() + return getClearManaCost(sb.toString()); + } + + public static String getClearManaCost(String manaCost) { + return manaCost .replace("/", "") .replace("{", "") - .replace("}", " ") + .replace("}", " ") // each mana symbol splits by space .trim(); } + public static int getClearManaSymbolsCount(String manaCost) { + // find mana symbols amount in the cost + if (manaCost.isEmpty()) { + return 0; + } else { + String clearManaCost = getClearManaCost(manaCost); + String checkManaCost = clearManaCost.replace(" ", ""); + return clearManaCost.length() - checkManaCost.length() + 1; + } + } + + + public enum Type { TABLE, CHAT, diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index 3a737ee3964c..ef1ed073b71d 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -221,7 +221,7 @@ public ModernCardRenderer(CardView card) { super(card); // Mana cost string - manaCostString = ManaSymbols.getStringManaCost(cardView.getManaCost()); + manaCostString = ManaSymbols.getClearManaCost(cardView.getManaCostStr()); } @Override diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernSplitCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernSplitCardRenderer.java index 9b18efeee1e8..15d444889186 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernSplitCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernSplitCardRenderer.java @@ -1,7 +1,6 @@ package org.mage.card.arcane; import mage.ObjectColor; -import mage.abilities.costs.mana.ManaCosts; import mage.cards.ArtRect; import mage.constants.CardType; import mage.view.CardView; @@ -50,11 +49,11 @@ private class HalfCardProps { public ModernSplitCardRenderer(CardView view) { super(view); - rightHalf.manaCostString = ManaSymbols.getStringManaCost(cardView.getRightSplitCosts().getSymbols()); - leftHalf.manaCostString = ManaSymbols.getStringManaCost(cardView.getLeftSplitCosts().getSymbols()); + rightHalf.manaCostString = ManaSymbols.getClearManaCost(cardView.getRightSplitCostsStr()); + leftHalf.manaCostString = ManaSymbols.getClearManaCost(cardView.getLeftSplitCostsStr()); - rightHalf.color = getColorFromManaCostHack(cardView.getRightSplitCosts()); - leftHalf.color = getColorFromManaCostHack(cardView.getLeftSplitCosts()); + rightHalf.color = getColorFromManaCostHack(cardView.getRightSplitCostsStr()); + leftHalf.color = getColorFromManaCostHack(cardView.getLeftSplitCostsStr()); parseRules(view.getRightSplitRules(), rightHalf.keywords, rightHalf.rules); parseRules(view.getLeftSplitRules(), leftHalf.keywords, leftHalf.rules); @@ -129,21 +128,18 @@ protected void layout(int cardWidth, int cardHeight) { // Ugly hack used here because the card database doesn't actually store color // for each half of split cards separately. - private ObjectColor getColorFromManaCostHack(ManaCosts costs) { + private ObjectColor getColorFromManaCostHack(String costs) { ObjectColor c = new ObjectColor(); - List symbols = costs.getSymbols(); - for (String symbol : symbols) { - if (symbol.contains("W")) { - c.setWhite(true); - } else if (symbol.contains("U")) { - c.setBlue(true); - } else if (symbol.contains("B")) { - c.setBlack(true); - } else if (symbol.contains("R")) { - c.setRed(true); - } else if (symbol.contains("G")) { - c.setGreen(true); - } + if (costs.contains("W")) { + c.setWhite(true); + } else if (costs.contains("U")) { + c.setBlue(true); + } else if (costs.contains("B")) { + c.setBlack(true); + } else if (costs.contains("R")) { + c.setRed(true); + } else if (costs.contains("G")) { + c.setGreen(true); } return c; } diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxKeywordRule.java b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxKeywordRule.java index 30ce82323ae0..ed1ce9f2b9c1 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxKeywordRule.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxKeywordRule.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.card.arcane; import java.util.List; diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxLevelRule.java b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxLevelRule.java index 61e295b426ef..83c7be65ac09 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxLevelRule.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxLevelRule.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.card.arcane; import java.util.List; diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxLoyaltyRule.java b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxLoyaltyRule.java index b3d3f975c123..0d76d2e78137 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxLoyaltyRule.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxLoyaltyRule.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.card.arcane; import java.util.List; diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRule.java b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRule.java index 371c8158e211..90161b500d8d 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRule.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRule.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.card.arcane; import java.awt.Font; diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRuleType.java b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRuleType.java index 671fe25e7029..c81f133bfed3 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRuleType.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRuleType.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.card.arcane; /** diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/Util.java b/Mage.Client/src/main/java/org/mage/card/arcane/Util.java index cbc3af717bd4..ee057202355a 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/Util.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/Util.java @@ -22,7 +22,7 @@ public final class Util { public static final boolean isMac = System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("mac"); public static final boolean isWindows = !System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("windows"); - public static final ThreadPoolExecutor threadPool; + public static final ThreadPoolExecutor threadPool; // uses for card images loading static private int threadCount; static { diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java index c402da3aa3fe..41304232842e 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java @@ -489,10 +489,14 @@ public class ScryfallImageSupportCards { add("AKR"); // Amonkhet Remastered add("ZNR"); // Zendikar Rising add("ZNC"); // Zendikar Rising Commander + add("ZNE"); // Zendikar Rising Expeditions + add("KLR"); // Kaladesh Remastered add("CMR"); // Commander Legends add("CC1"); // Commander Collection: Green add("KHM"); // Kaldheim add("KHC"); // Kaldheim Commander + add("TSR"); // Time Spiral Remastered + add("STX"); // Strixhaven: School of Mages } }; diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java index bd7196c505f8..daf5714a77ad 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java @@ -520,7 +520,7 @@ public class ScryfallImageSupportTokens { put("KHM/Emblem Tibalt Cosmic Imposter", "https://api.scryfall.com/cards/tkhm/21/en?format=image"); put("KHM/Emblem Tyvar Kell", "https://api.scryfall.com/cards/tkhm/22/en?format=image"); - // TKHC + // KHC put("KHC/Bird", "https://api.scryfall.com/cards/tkhc/1/en?format=image"); put("KHC/Boar", "https://api.scryfall.com/cards/tkhc/5/en?format=image"); put("KHC/Elemental", "https://api.scryfall.com/cards/tkhc/6/en?format=image"); @@ -530,6 +530,23 @@ public class ScryfallImageSupportTokens { put("KHC/Soldier", "https://api.scryfall.com/cards/tkhc/4/en?format=image"); put("KHC/Thopter", "https://api.scryfall.com/cards/tkhc/8/en?format=image"); + // TSR + put("TSR/Ape", "https://api.scryfall.com/cards/ttsr/10/en?format=image"); + put("TSR/Assembly-Worker", "https://api.scryfall.com/cards/ttsr/14/en?format=image"); + put("TSR/Bat", "https://api.scryfall.com/cards/ttsr/4/en?format=image"); + put("TSR/Cloud Sprite", "https://api.scryfall.com/cards/ttsr/3/en?format=image"); + put("TSR/Giant", "https://api.scryfall.com/cards/ttsr/7/en?format=image"); + put("TSR/Goblin", "https://api.scryfall.com/cards/ttsr/8/en?format=image"); + put("TSR/Griffin", "https://api.scryfall.com/cards/ttsr/1/en?format=image"); + put("TSR/Insect", "https://api.scryfall.com/cards/ttsr/11/en?format=image"); + put("TSR/Knight", "https://api.scryfall.com/cards/ttsr/5/en?format=image"); + put("TSR/Kobolds of Kher Keep", "https://api.scryfall.com/cards/ttsr/9/en?format=image"); + put("TSR/Llanowar Elves", "https://api.scryfall.com/cards/ttsr/12/en?format=image"); + put("TSR/Metallic Sliver", "https://api.scryfall.com/cards/ttsr/15/en?format=image"); + put("TSR/Saproling", "https://api.scryfall.com/cards/ttsr/13/en?format=image"); + put("TSR/Soldier", "https://api.scryfall.com/cards/ttsr/2/en?format=image"); + put("TSR/Spider", "https://api.scryfall.com/cards/ttsr/6/en?format=image"); + // generate supported sets supportedSets.clear(); for (String cardName : this.keySet()) { diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/info/CardInfoPaneImpl.java b/Mage.Client/src/main/java/org/mage/plugins/card/info/CardInfoPaneImpl.java index 1ec644ec77fd..4cf531e0d517 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/info/CardInfoPaneImpl.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/info/CardInfoPaneImpl.java @@ -56,7 +56,7 @@ public void setCard(final CardView card, final Component container) { try { SwingUtilities.invokeLater(() -> { TextLines textLines = GuiDisplayUtil.getTextLinesfromCardView(card); - StringBuilder buffer = GuiDisplayUtil.getRulefromCardView(card, textLines); + StringBuilder buffer = GuiDisplayUtil.getRulesFromCardView(card, textLines); resizeTooltipIfNeeded(container, textLines.getBasicTextLength(), textLines.getLines().size()); setText(buffer.toString()); setCaretPosition(0); diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java b/Mage.Client/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java index 6349fe917286..b6573b0295e9 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java @@ -308,6 +308,10 @@ public static void checkAndFixImageFiles() { // search broken files and delete it (zero size files) // search temp files and delete it (.tmp files from zip library) Path rootPath = new File(CardImageUtils.getImagesDir()).toPath(); + if (!Files.exists(rootPath)) { + return; + } + Collection brokenFilesList = new ArrayList<>(); Collection tempFilesList = new ArrayList<>(); try { diff --git a/Mage.Client/src/main/resources/card-pictures-tok.txt b/Mage.Client/src/main/resources/card-pictures-tok.txt index 56002e85b0a5..0fc9a1e7ee5e 100644 --- a/Mage.Client/src/main/resources/card-pictures-tok.txt +++ b/Mage.Client/src/main/resources/card-pictures-tok.txt @@ -1,3 +1,4 @@ +#TODO: some sets (TOK:code) don't have tokens but recorded here - must be fixed and removed (from that file and from tokens) #Generate|TOK:TPR|Goblin|| #Generate|TOK:TPR|Pegasus|| #Generate|TOK:TPR|Rat|| @@ -305,8 +306,7 @@ |Generate|TOK:C14|Demon|2||ReignOfThePitToken| |Generate|TOK:C14|Elemental|||TitaniaProtectorOfArgothElementalToken| |Generate|TOK:C14|Elephant|||ElephantToken| -|Generate|TOK:C14|Elf Druid|||FreyaliseLlanowarsFuryToken| -|Generate|TOK:C14|Elf Druid|||LlanowarElvesToken| +|Generate|TOK:C14|Elf Druid|||ElfDruidToken| |Generate|TOK:C14|Elf Warrior|||ElfWarriorToken| |Generate|TOK:C14|Equipment|||NahiriTheLithomancerEquipmentToken| |Generate|TOK:C14|Fish|||ReefWormFishToken| @@ -444,8 +444,7 @@ |Generate|TOK:CMA|Drake|||LeafdrakeRoostDrakeToken| |Generate|TOK:CMA|Elemental|||TitaniaProtectorOfArgothElementalToken| |Generate|TOK:CMA|Elephant|||ElephantToken| -|Generate|TOK:C14|Elf Druid|||FreyaliseLlanowarsFuryToken| -|Generate|TOK:C14|Elf Druid|||LlanowarElvesToken| +|Generate|TOK:C14|Elf Druid|||ElfDruidToken| |Generate|TOK:CMA|Elf Warrior|| |Generate|TOK:CMA|Gargoyle|||GargoyleToken| |Generate|TOK:CMA|Kithkin Soldier|||KithkinSoldierToken| @@ -467,7 +466,6 @@ |Generate|TOK:CMD|Saproling|||SaprolingToken| |Generate|TOK:CMD|Snake|||SnakeToken| |Generate|TOK:CMD|Soldier|||SoldierToken| -|Generate|TOK:CMD|Spider|||PenumbraSpiderToken| |Generate|TOK:CMD|Spirit|||SpiritWhiteToken| |Generate|TOK:CMD|Squirrel|||SquirrelToken| |Generate|TOK:CMD|Triskelavite|||TriskelaviteToken| @@ -680,18 +678,6 @@ |Generate|TOK:FRF|Monk|||MonasteryMentorToken| |Generate|TOK:FRF|Spirit|||SpiritWhiteToken| |Generate|TOK:FRF|Warrior|||MarduStrikeLeaderWarriorToken| -|Generate|TOK:FUT|Elemental|||SparkElementalToken| -|Generate|TOK:FUT|Elf Druid|||FreyaliseLlanowarsFuryToken| -|Generate|TOK:FUT|Elf Druid|||LlanowarElvesToken| -|Generate|TOK:FUT|Giant|||GiantToken| -|Generate|TOK:FUT|Kithkin Soldier|||KithkinSoldierToken| -|Generate|TOK:FUT|Saproling|||SaprolingToken| -|Generate|TOK:FUT|Sliver|||SliversmithToken| -|Generate|TOK:FUT|Soldier|||SoldierToken| -|Generate|TOK:FUT|Wurm|||WurmCallingWurmToken| -|Generate|TOK:FUT|Zombie Goblin|||FesteringGoblinToken| -|Generate|TOK:FUT|Zombie|||ZombieToken| -|Generate|TOK:FUT|Faerie|||CloudSpriteToken| |Generate|TOK:GPT|Bat|||BatToken| |Generate|TOK:GPT|Pegasus|||PegasusToken| |Generate|TOK:GPT|Sand|||DuneBroodNephilimToken| @@ -709,7 +695,6 @@ |Generate|TOK:GTC|Soldier|1||SoldierToken| |Generate|TOK:GTC|Soldier|2||SoldierTokenWithHaste| |Generate|TOK:GTC|Spirit|||TeysaEnvoyOfGhostsToken| -|Generate|TOK:H09|Sliver|||SliversmithToken| |Generate|TOK:H17|Dragon|||DragonTokenGold| |Generate|TOK:HML|Plant Wall|||KelpToken| |Generate|TOK:HML|Serf|||SerfToken| @@ -792,10 +777,7 @@ |Generate|TOK:LEG|Demon|||MinorDemonToken| |Generate|TOK:LEG|Sand Warrior|||HazezonTamarSandWarriorToken| |Generate|TOK:LEG|Snake|||SerpentGeneratorSnakeToken| -|Generate|TOK:LGN|Bear|||BearToken| -|Generate|TOK:LGN|Goblin|||GoblinToken| -|Generate|TOK:LGN|Insect|||InsectToken| -|Generate|TOK:LGN|Sliver|||SliversmithToken| +# LGN don't have tokens, from wiki: A Sliver token for Brood Sliver and a Goblin token for Warbreak Trumpeter were featured as a Magic Player Reward. |Generate|TOK:LRW|Avatar|||AvatarToken| |Generate|TOK:LRW|Beast|||BeastToken| |Generate|TOK:LRW|Elemental Shaman|||ElementalShamanToken| @@ -1037,7 +1019,7 @@ |Generate|TOK:PCY|Squirrel|||SquirrelToken| |Generate|TOK:PLC|Ape|||PongifyApeToken| |Generate|TOK:PLC|Cat Warrior|||CatWarriorToken| -|Generate|TOK:PLC|Insect|||DeadlyGrubToken| +|Generate|TOK:PLC|Insect|||DeadlyGrubInsectToken| |Generate|TOK:PLC|Knight|||RiftmarkedKnightToken| |Generate|TOK:PLC|Saproling|||SaprolingToken| |Generate|TOK:PLC|Soldier|||SoldierToken| @@ -1127,12 +1109,6 @@ |Generate|TOK:SOM|Wolf|||WolfToken| |Generate|TOK:SOM|Wurm|1||WurmWithDeathtouchToken| |Generate|TOK:SOM|Wurm|2||WurmWithLifelinkToken| -|Generate|TOK:STH|Goblin|||GoblinToken| -|Generate|TOK:STH|Insect|||HornetToken| -|Generate|TOK:STH|Insect|||WaspToken| -|Generate|TOK:STH|Rat|||RatToken| -|Generate|TOK:STH|Sliver|||SliversmithToken| -|Generate|TOK:STH|Spike|||SpikeToken| |Generate|TOK:SWS|Ewok|||EwokToken| |Generate|TOK:SWS|B-Wing|||RebelStarshipToken| |Generate|TOK:SWS|Hunter|||HunterToken| @@ -1161,16 +1137,6 @@ |Generate|TOK:TMP|Spirit|||SpiritWhiteToken| |Generate|TOK:TMP|Zombie|||ZombieToken| |Generate|TOK:TOR|Squirrel|||SquirrelToken| -|Generate|TOK:TPR|Goblin|||GoblinToken| -|Generate|TOK:TPR|Pegasus|||PegasusToken| -|Generate|TOK:TPR|Rat|||RatToken| -|Generate|TOK:TPR|Reflection|||ReflectionToken| -|Generate|TOK:TPR|Saproling|||SaprolingToken| -|Generate|TOK:TPR|Sliver|||SliversmithToken| -|Generate|TOK:TPR|Spike|||SpikeToken| -|Generate|TOK:TPR|Spirit|||SpiritWhiteToken| -|Generate|TOK:TPR|Thopter|||ThopterColorlessToken| -|Generate|TOK:TPR|Zombie|||ZombieToken| |Generate|TOK:TSP|Assembly-Worker|||AssembleWorkerToken| |Generate|TOK:TSP|Bat|||SengirNosferatuBatToken| |Generate|TOK:TSP|Citizen|||CitizenToken| @@ -1512,4 +1478,21 @@ |Generate|TOK:KHC|Pegasus|||PegasusToken| |Generate|TOK:KHC|Servo|||ServoToken| |Generate|TOK:KHC|Soldier|||SoldierToken| -|Generate|TOK:KHC|Thopter|||ThopterColorlessToken| \ No newline at end of file +|Generate|TOK:KHC|Thopter|||ThopterColorlessToken| + +# TSR +|Generate|TOK:TSR|Ape|||PongifyApeToken| +|Generate|TOK:TSR|Assembly-Worker|||AssemblyWorkerToken| +|Generate|TOK:TSR|Bat|||SengirNosferatuBatToken| +|Generate|TOK:TSR|Cloud Sprite|||CloudSpriteToken| +|Generate|TOK:TSR|Giant|||GiantToken| +|Generate|TOK:TSR|Goblin|||GoblinToken| +|Generate|TOK:TSR|Griffin|||GriffinToken| +|Generate|TOK:TSR|Insect|||DeadlyGrubInsectToken| +|Generate|TOK:TSR|Knight|||RiftmarkedKnightToken| +|Generate|TOK:TSR|Kobolds of Kher Keep|||KherKeepKoboldToken| +|Generate|TOK:TSR|Llanowar Elves|||LlanowarElvesToken| +|Generate|TOK:TSR|Metallic Sliver|||MetallicSliverToken| +|Generate|TOK:TSR|Saproling|||SaprolingToken| +|Generate|TOK:TSR|Soldier|||SoldierToken| +|Generate|TOK:TSR|Spider|||PenumbraSpiderToken| \ No newline at end of file diff --git a/Mage.Common/src/main/java/mage/interfaces/callback/ClientCallbackMethod.java b/Mage.Common/src/main/java/mage/interfaces/callback/ClientCallbackMethod.java index 95f21ad8376c..5fff7629ebdd 100644 --- a/Mage.Common/src/main/java/mage/interfaces/callback/ClientCallbackMethod.java +++ b/Mage.Common/src/main/java/mage/interfaces/callback/ClientCallbackMethod.java @@ -44,6 +44,7 @@ public enum ClientCallbackMethod { GAME_PLAY_MANA("gamePlayMana"), GAME_PLAY_XMANA("gamePlayXMana"), GAME_GET_AMOUNT("gameSelectAmount"), + GAME_GET_MULTI_AMOUNT("gameSelectMultiAmount"), DRAFT_INIT("draftInit"), DRAFT_PICK("draftPick"), DRAFT_UPDATE("draftUpdate"); diff --git a/Mage.Common/src/main/java/mage/remote/ActionData.java b/Mage.Common/src/main/java/mage/remote/ActionData.java index c546ba2d240d..ce79520cba2e 100644 --- a/Mage.Common/src/main/java/mage/remote/ActionData.java +++ b/Mage.Common/src/main/java/mage/remote/ActionData.java @@ -67,7 +67,7 @@ static class CustomExclusionStrategy implements ExclusionStrategy { "paid", "hand", "stack", - "convertedManaCost", + "manaValue", "gameId", "canPlayInHand", "gameView", diff --git a/Mage.Common/src/main/java/mage/utils/DeckBuilder.java b/Mage.Common/src/main/java/mage/utils/DeckBuilder.java index 0223a2973df5..8e18829c2274 100644 --- a/Mage.Common/src/main/java/mage/utils/DeckBuilder.java +++ b/Mage.Common/src/main/java/mage/utils/DeckBuilder.java @@ -134,7 +134,7 @@ private static void addLandsToDeck(List allowedColors, List colorCount = new HashMap<>(); for (final Card card : deck.getCards()) { - for (String symbol : card.getManaCost().getSymbols()) { + for (String symbol : card.getManaCostSymbols()) { int count = 0; symbol = symbol.replace("{", "").replace("}", ""); if (isColoredMana(symbol)) { @@ -238,12 +238,12 @@ public MageScoredCard(Card card, List allowedColors, RateCall } private int getManaCostScore(Card card, List allowedColors) { - int converted = card.getManaCost().convertedManaCost(); + int converted = card.getManaValue(); final Map singleCount = new HashMap<>(); int maxSingleCount = 0; int multicolor = 0; Set colors = new HashSet<>(); - for (String symbol : card.getManaCost().getSymbols()) { + for (String symbol : card.getManaCostSymbols()) { int count = 0; symbol = symbol.replace("{", "").replace("}", ""); if (isColoredMana(symbol)) { @@ -276,7 +276,7 @@ public int getScore() { } public int getConvertedCost() { - return this.card.getManaCost().convertedManaCost(); + return this.card.getManaValue(); } public Card getCard() { diff --git a/Mage.Common/src/main/java/mage/utils/MageVersion.java b/Mage.Common/src/main/java/mage/utils/MageVersion.java index 1145daccf3f6..821bf4f7f194 100644 --- a/Mage.Common/src/main/java/mage/utils/MageVersion.java +++ b/Mage.Common/src/main/java/mage/utils/MageVersion.java @@ -9,56 +9,54 @@ */ public class MageVersion implements Serializable, Comparable { + // version must be compatible with maven version numbers + // launcher can update only to newest version + // example: 1.4.48-V1-beta3 + // * 1.4.48 compares as numbers + // * V1-beta3 is qualifier and compares as string + // * launcher gives priority to 1.4.48 instead 1.4.48-any-text, so don't use empty release info public static final int MAGE_VERSION_MAJOR = 1; public static final int MAGE_VERSION_MINOR = 4; - public static final int MAGE_VERSION_PATCH = 47; - public static final String MAGE_EDITION_INFO = ""; // set "-beta2" for 1.4.32V1-beta2 - public static final String MAGE_VERSION_MINOR_PATCH = "V1"; // default - // strict mode - private static final boolean MAGE_VERSION_MINOR_PATCH_MUST_BE_SAME = true; // set true on uncompatible github changes, set false after new major release (after MAGE_VERSION_PATCH changes) + public static final int MAGE_VERSION_RELEASE = 48; + public static final String MAGE_VERSION_RELEASE_INFO = "V2"; // V1 for releases, V1-beta3 for betas + // strict mode + // Each update requires a strict version + // If you disable it then server can accept multiple versions with same release number + // Since incompatible changes are no longer monitored - it must always be true + private static final boolean MAGE_VERSION_RELEASE_INFO_MUST_BE_SAME = true; + // build info public static final boolean MAGE_VERSION_SHOW_BUILD_TIME = true; + private final int major; private final int minor; - private final int patch; - private final String minorPatch; // doesn't matter for compatibility + private final int release; + private final String releaseInfo; private final String buildTime; - private final String editionInfo; public MageVersion(Class sourceClass) { - this(MAGE_VERSION_MAJOR, MAGE_VERSION_MINOR, MAGE_VERSION_PATCH, MAGE_VERSION_MINOR_PATCH, MAGE_EDITION_INFO, sourceClass); + this(MAGE_VERSION_MAJOR, MAGE_VERSION_MINOR, MAGE_VERSION_RELEASE, MAGE_VERSION_RELEASE_INFO, sourceClass); } - public MageVersion(int major, int minor, int patch, String minorPatch, String editionInfo, Class sourceClass) { + public MageVersion(int major, int minor, int release, String releaseInfo, Class sourceClass) { this.major = major; this.minor = minor; - this.patch = patch; - this.minorPatch = minorPatch; - this.editionInfo = editionInfo; + this.release = release; + this.releaseInfo = releaseInfo; + + if (!releaseInfo.startsWith("V")) { + // release: V1 + // beta: V1-beta3 + throw new IllegalArgumentException("ERROR, release info must be started from V."); + } // build time this.buildTime = JarVersion.getBuildTime(sourceClass); } - public int getMajor() { - return major; - } - - public int getMinor() { - return minor; - } - - public int getPatch() { - return patch; - } - - public String getMinorPatch() { - return minorPatch; - } - public String toString(boolean showBuildTime) { - // 1.4.32V1-beta2 (build: time) - String res = major + "." + minor + '.' + patch + minorPatch + editionInfo; + // 1.4.32-V1-beta2 (build: time) + String res = major + "." + minor + '.' + release + "-" + releaseInfo; if (showBuildTime && !this.buildTime.isEmpty()) { res += " (build: " + this.buildTime + ")"; } @@ -78,12 +76,15 @@ public int compareTo(MageVersion o) { if (minor != o.minor) { return minor - o.minor; } - if (patch != o.patch) { - return patch - o.patch; + if (release != o.release) { + return release - o.release; } - if (MAGE_VERSION_MINOR_PATCH_MUST_BE_SAME && !minorPatch.equals(o.minorPatch)) { - return minorPatch.compareTo(o.minorPatch); + + if (MAGE_VERSION_RELEASE_INFO_MUST_BE_SAME && !releaseInfo.equals(o.releaseInfo)) { + return releaseInfo.compareTo(o.releaseInfo); } - return editionInfo.compareTo(o.editionInfo); + + // all fine + return 0; } } diff --git a/Mage.Common/src/main/java/mage/view/AbilityView.java b/Mage.Common/src/main/java/mage/view/AbilityView.java index f6d20dfc3381..1ad8657c060b 100644 --- a/Mage.Common/src/main/java/mage/view/AbilityView.java +++ b/Mage.Common/src/main/java/mage/view/AbilityView.java @@ -31,8 +31,8 @@ public AbilityView(Ability ability, String sourceName, CardView sourceCard) { this.subTypes = new SubTypes(); this.superTypes = EnumSet.noneOf(SuperType.class); this.color = new ObjectColor(); - this.manaCostLeft = ability.getManaCosts().getSymbols(); - this.manaCostRight = new ArrayList<>(); + this.manaCostLeftStr = String.join("", ability.getManaCostSymbols()); + this.manaCostRightStr = ""; } public CardView getSourceCard() { diff --git a/Mage.Common/src/main/java/mage/view/CardView.java b/Mage.Common/src/main/java/mage/view/CardView.java index 4469f2df7f80..f648dfa0d6d0 100644 --- a/Mage.Common/src/main/java/mage/view/CardView.java +++ b/Mage.Common/src/main/java/mage/view/CardView.java @@ -7,7 +7,6 @@ import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.SpellAbility; -import mage.abilities.costs.mana.ManaCosts; import mage.abilities.effects.Effect; import mage.abilities.effects.Effects; import mage.abilities.icon.CardIcon; @@ -65,9 +64,9 @@ public class CardView extends SimpleCardView { protected ObjectColor frameColor; protected FrameStyle frameStyle; // can combine multiple costs for MockCard from deck editor or db (left/right, card/adventure) - protected List manaCostLeft; - protected List manaCostRight; - protected int convertedManaCost; + protected String manaCostLeftStr; + protected String manaCostRightStr; + protected int manaValue; protected Rarity rarity; protected MageObjectType mageObjectType = MageObjectType.NULL; @@ -91,11 +90,11 @@ public class CardView extends SimpleCardView { protected boolean isSplitCard; protected String leftSplitName; - protected ManaCosts leftSplitCosts; + protected String leftSplitCostsStr; protected List leftSplitRules; protected String leftSplitTypeLine; protected String rightSplitName; - protected ManaCosts rightSplitCosts; + protected String rightSplitCostsStr; protected List rightSplitRules; protected String rightSplitTypeLine; @@ -173,9 +172,9 @@ public CardView(final CardView cardView) { this.color = cardView.color; this.frameColor = cardView.frameColor; this.frameStyle = cardView.frameStyle; - this.manaCostLeft = new ArrayList<>(cardView.manaCostLeft); - this.manaCostRight = new ArrayList<>(cardView.manaCostRight); - this.convertedManaCost = cardView.convertedManaCost; + this.manaCostLeftStr = cardView.manaCostLeftStr; + this.manaCostRightStr = cardView.manaCostRightStr; + this.manaValue = cardView.manaValue; this.rarity = cardView.rarity; this.mageObjectType = cardView.mageObjectType; @@ -195,11 +194,11 @@ public CardView(final CardView cardView) { this.isSplitCard = cardView.isSplitCard; this.leftSplitName = cardView.leftSplitName; - this.leftSplitCosts = cardView.leftSplitCosts == null ? null : cardView.leftSplitCosts.copy(); + this.leftSplitCostsStr = cardView.leftSplitCostsStr; this.leftSplitRules = cardView.leftSplitRules == null ? null : new ArrayList<>(cardView.leftSplitRules); this.leftSplitTypeLine = cardView.leftSplitTypeLine; this.rightSplitName = cardView.rightSplitName; - this.rightSplitCosts = cardView.rightSplitCosts == null ? null : cardView.rightSplitCosts.copy(); + this.rightSplitCostsStr = cardView.rightSplitCostsStr; this.rightSplitRules = cardView.rightSplitRules == null ? null : new ArrayList<>(cardView.rightSplitRules); this.rightSplitTypeLine = cardView.rightSplitTypeLine; @@ -348,40 +347,39 @@ public CardView(Card card, Game game, boolean controlled, boolean showFaceDownCa if (splitCard != null) { this.isSplitCard = true; leftSplitName = splitCard.getLeftHalfCard().getName(); - leftSplitCosts = splitCard.getLeftHalfCard().getManaCost(); + leftSplitCostsStr = String.join("", splitCard.getLeftHalfCard().getManaCostSymbols()); leftSplitRules = splitCard.getLeftHalfCard().getRules(game); leftSplitTypeLine = getCardTypeLine(game, splitCard.getLeftHalfCard()); rightSplitName = splitCard.getRightHalfCard().getName(); - rightSplitCosts = splitCard.getRightHalfCard().getManaCost(); + rightSplitCostsStr = String.join("", splitCard.getRightHalfCard().getManaCostSymbols()); rightSplitRules = splitCard.getRightHalfCard().getRules(game); rightSplitTypeLine = getCardTypeLine(game, splitCard.getRightHalfCard()); fullCardName = card.getName(); // split card contains full name as normal - this.manaCostLeft = splitCard.getLeftHalfCard().getManaCost().getSymbols(); - this.manaCostRight = splitCard.getRightHalfCard().getManaCost().getSymbols(); + this.manaCostLeftStr = String.join("", splitCard.getLeftHalfCard().getManaCostSymbols()); + this.manaCostRightStr = String.join("", splitCard.getRightHalfCard().getManaCostSymbols()); } else if (card instanceof ModalDoubleFacesCard) { this.isModalDoubleFacesCard = true; ModalDoubleFacesCard mainCard = ((ModalDoubleFacesCard) card); fullCardName = mainCard.getLeftHalfCard().getName() + MockCard.MODAL_DOUBLE_FACES_NAME_SEPARATOR + mainCard.getRightHalfCard().getName(); - this.manaCostLeft = mainCard.getLeftHalfCard().getManaCost().getSymbols(); - this.manaCostRight = mainCard.getRightHalfCard().getManaCost().getSymbols(); + this.manaCostLeftStr = String.join("", mainCard.getLeftHalfCard().getManaCostSymbols()); + this.manaCostRightStr = String.join("", mainCard.getRightHalfCard().getManaCostSymbols()); } else if (card instanceof AdventureCard) { AdventureCard adventureCard = ((AdventureCard) card); AdventureCardSpell adventureCardSpell = ((AdventureCardSpell) adventureCard.getSpellCard()); fullCardName = adventureCard.getName() + MockCard.ADVENTURE_NAME_SEPARATOR + adventureCardSpell.getName(); - this.manaCostLeft = adventureCardSpell.getManaCost().getSymbols(); - this.manaCostRight = adventureCard.getManaCost().getSymbols(); + this.manaCostLeftStr = String.join("", adventureCardSpell.getManaCostSymbols()); + this.manaCostRightStr = String.join("", adventureCard.getManaCostSymbols()); } else if (card instanceof MockCard) { // deck editor cards fullCardName = ((MockCard) card).getFullName(true); - this.manaCostLeft = ((MockCard) card).getManaCost(CardInfo.ManaCostSide.LEFT).getSymbols(); - this.manaCostRight = ((MockCard) card).getManaCost(CardInfo.ManaCostSide.RIGHT).getSymbols(); + this.manaCostLeftStr = String.join("", ((MockCard) card).getManaCostStr(CardInfo.ManaCostSide.LEFT)); + this.manaCostRightStr = String.join("", ((MockCard) card).getManaCostStr(CardInfo.ManaCostSide.RIGHT)); } else { fullCardName = card.getName(); - this.manaCostLeft = card.getManaCost().getSymbols(); - this.manaCostRight = new ArrayList<>(); + this.manaCostLeftStr = String.join("", card.getManaCostSymbols()); + this.manaCostRightStr = ""; } - //this.manaCost = card.getManaCost().getSymbols(); this.name = card.getImageName(); this.displayName = card.getName(); @@ -391,7 +389,7 @@ public CardView(Card card, Game game, boolean controlled, boolean showFaceDownCa } else { this.rules = card.getRules(game); } - this.convertedManaCost = card.getManaCost().convertedManaCost(); + this.manaValue = card.getManaValue(); if (card instanceof Permanent) { this.mageObjectType = MageObjectType.PERMANENT; @@ -565,9 +563,9 @@ public CardView(MageObject object, Game game) { this.subTypes = object.getSubtype(game); this.superTypes = object.getSuperType(); this.color = object.getColor(game); - this.manaCostLeft = object.getManaCost().getSymbols(); - this.manaCostRight = new ArrayList<>(); - this.convertedManaCost = object.getManaCost().convertedManaCost(); + this.manaCostLeftStr = String.join("", object.getManaCostSymbols()); + this.manaCostRightStr = ""; + this.manaValue = object.getManaCost().manaValue(); if (object instanceof PermanentToken) { this.mageObjectType = MageObjectType.TOKEN; PermanentToken permanentToken = (PermanentToken) object; @@ -686,9 +684,9 @@ private void fillEmpty(Card card, boolean controlled) { this.color = new ObjectColor(); this.frameColor = new ObjectColor(); this.frameStyle = FrameStyle.M15_NORMAL; - this.manaCostLeft = new ArrayList<>(); - this.manaCostRight = new ArrayList<>(); - this.convertedManaCost = 0; + this.manaCostLeftStr = ""; + this.manaCostRightStr = ""; + this.manaValue = 0; // the controller can see more information (e.g. enlarged image) than other players for face down cards (e.g. Morph played face down) if (!controlled) { @@ -735,8 +733,8 @@ private void fillEmpty(Card card, boolean controlled) { this.color = token.getColor(game); this.frameColor = token.getFrameColor(game); this.frameStyle = token.getFrameStyle(); - this.manaCostLeft = token.getManaCost().getSymbols(); - this.manaCostRight = new ArrayList<>(); + this.manaCostLeftStr = String.join("", token.getManaCostSymbols()); + this.manaCostRightStr = ""; this.rarity = Rarity.SPECIAL; this.type = token.getTokenType(); this.tokenDescriptor = token.getTokenDescriptor(); @@ -846,12 +844,12 @@ public FrameStyle getFrameStyle() { return frameStyle; } - public List getManaCost() { - return CardUtil.concatManaSymbols(CardInfo.SPLIT_MANA_SEPARATOR_FULL, this.manaCostLeft, this.manaCostRight); + public String getManaCostStr() { + return CardUtil.concatManaSymbols(CardInfo.SPLIT_MANA_SEPARATOR_FULL, this.manaCostLeftStr, this.manaCostRightStr); } - public int getConvertedManaCost() { - return convertedManaCost; + public int getManaValue() { + return manaValue; } public Rarity getRarity() { @@ -963,8 +961,8 @@ public String getLeftSplitName() { return leftSplitName; } - public ManaCosts getLeftSplitCosts() { - return leftSplitCosts; + public String getLeftSplitCostsStr() { + return leftSplitCostsStr; } public List getLeftSplitRules() { @@ -979,8 +977,8 @@ public String getRightSplitName() { return rightSplitName; } - public ManaCosts getRightSplitCosts() { - return rightSplitCosts; + public String getRightSplitCostsStr() { + return rightSplitCostsStr; } public List getRightSplitRules() { diff --git a/Mage.Common/src/main/java/mage/view/ChatMessage.java b/Mage.Common/src/main/java/mage/view/ChatMessage.java index de0db28809e9..adf541553cb8 100644 --- a/Mage.Common/src/main/java/mage/view/ChatMessage.java +++ b/Mage.Common/src/main/java/mage/view/ChatMessage.java @@ -1,6 +1,7 @@ package mage.view; import mage.game.Game; +import mage.util.CardUtil; import java.io.Serializable; import java.util.Date; @@ -43,27 +44,12 @@ public ChatMessage(String username, String message, Date time, Game game, Messag this.username = username; this.message = message; this.time = time; - this.turnInfo = prepareTurnInfo(game); + this.turnInfo = CardUtil.getTurnInfo(game); this.color = color; this.messageType = messageType; this.soundToPlay = soundToPlay; } - private String prepareTurnInfo(Game game) { - // no turn info - if (game == null) { - return null; - } - - // not started game - if (game.getStep() == null) { - return "T0"; - } - - // normal game - return "T" + game.getTurnNum() + "." + game.getStep().getType().getStepShortText(); - } - public String getMessage() { return message; } diff --git a/Mage.Common/src/main/java/mage/view/GameClientMessage.java b/Mage.Common/src/main/java/mage/view/GameClientMessage.java index bf02f2522dfa..092079bf7cc8 100644 --- a/Mage.Common/src/main/java/mage/view/GameClientMessage.java +++ b/Mage.Common/src/main/java/mage/view/GameClientMessage.java @@ -3,6 +3,7 @@ package mage.view; import java.io.Serializable; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; @@ -42,6 +43,8 @@ public class GameClientMessage implements Serializable { private Map options; @Expose private Choice choice; + @Expose + private List messages; public GameClientMessage(GameView gameView) { this.gameView = gameView; @@ -93,6 +96,13 @@ public GameClientMessage(CardsView cardView, String name) { this.message = name; } + public GameClientMessage(List messages, int min, int max, Map options) { + this.messages = messages; + this.min = min; + this.max = max; + this.options = options; + } + public GameClientMessage(Choice choice) { this.choice = choice; } @@ -145,6 +155,10 @@ public Choice getChoice() { return choice; } + public List getMessages() { + return messages; + } + public String toJson() { Gson gson = new GsonBuilder() .excludeFieldsWithoutExposeAnnotation() diff --git a/Mage.Common/src/main/java/mage/view/StackAbilityView.java b/Mage.Common/src/main/java/mage/view/StackAbilityView.java index cfe29e4786aa..177e6225ec65 100644 --- a/Mage.Common/src/main/java/mage/view/StackAbilityView.java +++ b/Mage.Common/src/main/java/mage/view/StackAbilityView.java @@ -44,8 +44,8 @@ public StackAbilityView(Game game, StackAbility ability, String sourceName, Card this.subTypes = ability.getSubtype(game); this.superTypes = ability.getSuperType(); this.color = ability.getColor(game); - this.manaCostLeft = ability.getManaCost().getSymbols(); - this.manaCostRight = new ArrayList<>(); + this.manaCostLeftStr = String.join("", ability.getManaCostSymbols()); + this.manaCostRightStr = ""; this.cardTypes = ability.getCardType(); this.subTypes = ability.getSubtype(game); this.superTypes = ability.getSuperType(); @@ -60,8 +60,8 @@ public StackAbilityView(Game game, StackAbility ability, String sourceName, Card tmpSourceCard.subTypes.clear(); tmpSourceCard.cardTypes.clear(); tmpSourceCard.cardTypes.add(CardType.CREATURE); - tmpSourceCard.manaCostLeft.clear(); - tmpSourceCard.manaCostRight.clear(); + tmpSourceCard.manaCostLeftStr = ""; + tmpSourceCard.manaCostRightStr = ""; tmpSourceCard.power = "2"; tmpSourceCard.toughness = "2"; nameToShow = "creature without name"; diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java index 0a5dd23ddfa0..b7319820254f 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java @@ -386,14 +386,14 @@ public int getEdhPowerLevel(Deck deck) { whenYouCast |= s.contains("when you cast") || s.contains("whenever you cast"); } - for (ManaCost cost : card.getManaCost()) { - if (cost.getText().contains("X")) { + for (String s : card.getManaCostSymbols()) { + if (s.contains("X")) { xCost = true; } } for (Ability a : card.getAbilities()) { - for (ManaCost cost : a.getManaCosts()) { - if (cost.getText().contains("X")) { + for (String s : a.getManaCostSymbols()) { + if (s.contains("X")) { xCost = true; } } diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/DuelCommander.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/DuelCommander.java index c3ef190dd918..d0fb815ef78d 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/DuelCommander.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/DuelCommander.java @@ -1,4 +1,3 @@ - package mage.deck; /** @@ -24,7 +23,6 @@ public DuelCommander() { banned.add("Fastbond"); banned.add("Field of the Dead"); banned.add("Fierce Guardianship"); - banned.add("Fireblast"); banned.add("Food Chain"); banned.add("Gaea's Cradle"); banned.add("Genesis Storm"); @@ -33,6 +31,7 @@ public DuelCommander() { banned.add("High Tide"); banned.add("Humility"); banned.add("Imperial Seal"); + banned.add("Jeweled Lotus"); banned.add("Karakas"); banned.add("Library of Alexandria"); banned.add("Lion's Eye Diamond"); @@ -70,10 +69,13 @@ public DuelCommander() { banned.add("Tinker"); banned.add("Tolarian Academy"); banned.add("Treasure Cruise"); + banned.add("Uro, Titan of Nature's Wrath"); banned.add("Vampiric Tutor"); banned.add("Wasteland"); + bannedCommander.add("Akiri, Line-Slinger"); bannedCommander.add("Arahbo, Roar of the World"); + bannedCommander.add("Ardenn, Intrepid Archaeologist"); bannedCommander.add("Baral, Chief of Compliance"); bannedCommander.add("Breya, Etherium Shaper"); bannedCommander.add("Bruse Tarl, Boorish Herder"); @@ -81,18 +83,27 @@ public DuelCommander() { bannedCommander.add("Edgar Markov"); bannedCommander.add("Edric, Spymaster of Trest"); bannedCommander.add("Emry, Lurker of the Loch"); + bannedCommander.add("Esior, Wardwing Familiar"); bannedCommander.add("Geist of Saint Traft"); + bannedCommander.add("Jeska, Thrice Reborn"); + bannedCommander.add("Keleth, Sunmane Familiar"); + bannedCommander.add("Krark, the Thumbless"); + bannedCommander.add("Ludevic, Necro-Alchemist"); bannedCommander.add("Marath, Will of the Wild"); bannedCommander.add("Najeela, the Blade-Blossom"); bannedCommander.add("Oloro, Ageless Ascetic"); + bannedCommander.add("Omnath, Locus of Creation"); bannedCommander.add("Prime Speaker Vannifar"); + bannedCommander.add("Reyhan, Last of the Abzan"); bannedCommander.add("Rofellos, Llanowar Emissary"); + bannedCommander.add("Rograkh, Son of Rohgahh"); bannedCommander.add("Tasigur, the Golden Fang"); bannedCommander.add("Teferi, Temporal Archmage"); + bannedCommander.add("Thrasios, Triton Hero"); + bannedCommander.add("Tymna, the Weaver"); bannedCommander.add("Urza, Lord High Artificer"); bannedCommander.add("Vial Smasher the Fierce"); bannedCommander.add("Yuriko, the Tiger's Shadow"); bannedCommander.add("Zurgo Bellstriker"); } - } diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Historic.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Historic.java index 95f6fc1bafa8..7987d36582cd 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Historic.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Historic.java @@ -23,14 +23,26 @@ public Historic() { setCodes.add(set.getCode()); } } + // Additional sets + setCodes.add("JMP"); // Jumpstart (replacements below) + setCodes.add("STA"); // Strixhaven Mystical Archive + // Regular ban list banned.add("Agent of Treachery"); + banned.add("Channel"); + banned.add("Counterspell"); + banned.add("Dark Ritual"); + banned.add("Demonic Tutor"); banned.add("Fires of Invention"); + banned.add("Lightning Bolt"); + banned.add("Natural Order"); banned.add("Nexus of Fate"); banned.add("Oko, Thief of Crowns"); banned.add("Omnath, Locus of Creation"); banned.add("Once Upon a Time"); + banned.add("Swords to Plowshares"); banned.add("Teferi, Time Raveler"); + banned.add("Uro, Titan of Nature's Wrath"); banned.add("Veil of Summer"); banned.add("Wilderness Reclamation"); banned.add("Winota, Joiner of Forces"); @@ -58,8 +70,7 @@ public Historic() { singleCards.add("The Gitrog Monster"); singleCards.add("Talrand, Sky Summoner"); - // Jumpstart with replacements - setCodes.add("JMP"); + // Jumpstart replacements singleCards.add("Archon of Sun's Grace"); singleCards.add("Banishing Light"); singleCards.add("Gadwick, the Wizened"); @@ -95,7 +106,6 @@ public Historic() { banned.add("Draconic Roar"); banned.add("Flametongue Kavu"); banned.add("Goblin Lore"); - banned.add("Lightning Bolt"); banned.add("Fa'adiyah Seer"); banned.add("Scrounging Bandar"); banned.add("Time to Feed"); diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java index d297bdfb8e64..259d87971553 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java @@ -17,6 +17,7 @@ public Legacy() { } } banned.add("Advantageous Proclamation"); + banned.add("Arcum's Astrolabe"); banned.add("Amulet of Quoz"); banned.add("Ancestral Recall"); banned.add("Backup Plan"); @@ -35,6 +36,7 @@ public Legacy() { banned.add("Demonic Tutor"); banned.add("Dig Through Time"); banned.add("Double Stroke"); + banned.add("Dreadhorde Arcanist"); banned.add("Earthcraft"); banned.add("Falling Star"); banned.add("Fastbond"); @@ -67,6 +69,7 @@ public Legacy() { banned.add("Mystical Tutor"); banned.add("Necropotence"); banned.add("Oath of Druids"); + banned.add("Oko, Thief of Crowns"); banned.add("Power Play"); banned.add("Rebirth"); banned.add("Secret Summoning"); diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Modern.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Modern.java index 0e8f646e56f3..f56006a3cd0c 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Modern.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Modern.java @@ -36,6 +36,7 @@ public Modern() { banned.add("Dread Return"); banned.add("Eye of Ugin"); banned.add("Faithless Looting"); + banned.add("Field of the Dead"); banned.add("Gitaxian Probe"); banned.add("Glimpse of Nature"); banned.add("Golgari Grave-Troll"); @@ -47,6 +48,7 @@ public Modern() { banned.add("Mental Misstep"); banned.add("Mox Opal"); banned.add("Mycosynth Lattice"); + banned.add("Mystic Sanctuary"); banned.add("Oko, Thief of Crowns"); banned.add("Once Upon a Time"); banned.add("Ponder"); @@ -57,12 +59,15 @@ public Modern() { banned.add("Second Sunrise"); banned.add("Seething Song"); banned.add("Sensei's Divining Top"); + banned.add("Simian Spirit Guide"); banned.add("Skullclamp"); banned.add("Splinter Twin"); banned.add("Summer Bloom"); + banned.add("Tibalt's Trickery"); banned.add("Treasure Cruise"); banned.add("Tree of Tales"); banned.add("Umezawa's Jitte"); + banned.add("Uro, Titan of Nature's Wrath"); banned.add("Vault of Whispers"); } } diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Pioneer.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Pioneer.java index ff914bf2d967..77f3a4ced81c 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Pioneer.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Pioneer.java @@ -28,6 +28,7 @@ public Pioneer() { banned.add("Polluted Delta"); banned.add("Windswept Heath"); banned.add("Wooded Foothills"); + banned.add("Balustrade Spy"); banned.add("Felidar Guardian"); banned.add("Field of the Dead"); banned.add("Inverter of Truth"); @@ -37,8 +38,12 @@ public Pioneer() { banned.add("Oko, Thief of Crowns"); banned.add("Once Upon a Time"); banned.add("Smuggler's Copter"); + banned.add("Teferi, Time Raveler"); + banned.add("Undercity Informer"); banned.add("Underworld Breach"); + banned.add("Uro, Titan of Nature's Wrath"); banned.add("Veil of Summer"); banned.add("Walking Ballista"); + banned.add("Wilderness Reclamation"); } } diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/StarWarsBlock.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/StarWarsBlock.java index 37d129c61c3a..f7b5fb20c7ac 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/StarWarsBlock.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/StarWarsBlock.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.deck; import mage.cards.decks.Constructed; diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java index b4b0582418ef..444b837f1592 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java @@ -130,7 +130,7 @@ public boolean validate(Deck deck) { * would be legal independently. */ - if (commander == null || commander.getManaCost().convertedManaCost() > 3) { + if (commander == null || commander.getManaValue() > 3) { if (commander == null) { if (deck.getName() == null) { addError(DeckValidatorErrorType.PRIMARY, "Leader", "You have to save your deck with the leader card name entered to the DECK NAME field of the DECK EDITOR (top left) so that XMage knows your leader." @@ -141,8 +141,8 @@ public boolean validate(Deck deck) { } } - if (commander != null && commander.getManaCost().convertedManaCost() > 3) { - addError(DeckValidatorErrorType.PRIMARY, "Leader", "Commanders converted mana cost is greater than 3"); + if (commander != null && commander.getManaValue() > 3) { + addError(DeckValidatorErrorType.PRIMARY, "Leader", "Commanders mana value is greater than 3"); } return false; } @@ -201,13 +201,13 @@ private boolean isCardFormatValid(Card card, Card commander, FilterMana color) { // as zero for this purpose. Split cards are legal only if both of their halves would be legal independently. List costs = new ArrayList<>(); if (card instanceof SplitCard) { - costs.add(((SplitCard) card).getLeftHalfCard().getManaCost().convertedManaCost()); - costs.add(((SplitCard) card).getRightHalfCard().getManaCost().convertedManaCost()); + costs.add(((SplitCard) card).getLeftHalfCard().getManaValue()); + costs.add(((SplitCard) card).getRightHalfCard().getManaValue()); } else if (card instanceof ModalDoubleFacesCard) { - costs.add(((ModalDoubleFacesCard) card).getLeftHalfCard().getManaCost().convertedManaCost()); - costs.add(((ModalDoubleFacesCard) card).getRightHalfCard().getManaCost().convertedManaCost()); + costs.add(((ModalDoubleFacesCard) card).getLeftHalfCard().getManaValue()); + costs.add(((ModalDoubleFacesCard) card).getRightHalfCard().getManaValue()); } else { - costs.add(card.getManaCost().convertedManaCost()); + costs.add(card.getManaValue()); } return costs.stream().allMatch(cost -> { diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Vintage.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Vintage.java index a6cacdf8559f..a01a05ced37c 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Vintage.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Vintage.java @@ -30,7 +30,6 @@ public Vintage() { banned.add("Immediate Action"); banned.add("Iterative Analysis"); banned.add("Jeweled Bird"); - banned.add("Lurrus of the Dream-Den"); banned.add("Muzzio's Preparations"); banned.add("Power Play"); banned.add("Rebirth"); diff --git a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerMatch.java b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerMatch.java index 720ae4fd2425..64100769d16e 100644 --- a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerMatch.java +++ b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerMatch.java @@ -1,4 +1,3 @@ - package mage.game; import mage.game.match.MatchImpl; @@ -17,7 +16,12 @@ public TwoPlayerMatch(MatchOptions options) { @Override public void startGame() throws GameException { Mulligan mulligan = options.getMulliganType().getMulligan(options.getFreeMulligans()); - TwoPlayerDuel game = new TwoPlayerDuel(options.getAttackOption(), options.getRange(), mulligan, 20, options.isLimited() ? 40 : 60); + + // workaround to enable limited deck size in limited set as deck type (Yorion, Sky Nomad) + // see comments from https://github.com/magefree/mage/commit/4874ad31c199ea573187ea2790268be3a4d4c95a + boolean isLimitedDeck = options.isLimited() || "Limited".equals(options.getDeckType()); + + TwoPlayerDuel game = new TwoPlayerDuel(options.getAttackOption(), options.getRange(), mulligan, 20, isLimitedDeck ? 40 : 60); // Sets a start message about the match score game.setStartMessage(this.createGameStartMessage()); initGame(game); diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java index 5f03d178230e..dd8ee09aa408 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java @@ -118,7 +118,7 @@ protected void calculateActions(Game game) { boolean doThis = true; if (root.abilities.size() == 1) { for (Ability ability : root.abilities) { - if (ability.getManaCosts().convertedManaCost() == 0 + if (ability.getManaCosts().manaValue() == 0 && ability.getCosts().isEmpty()) { if (actionCache.contains(ability.getRule() + '_' + ability.getSourceId())) { doThis = false; // don't do it again diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java index 833c18210f09..3f7f2205420d 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java @@ -117,7 +117,7 @@ protected void simulateOptions(Game game) { @Override protected void addVariableXOptions(List options, Ability ability, int targetNum, Game game) { // calculate the mana that can be used for the x part - int numAvailable = getAvailableManaProducers(game).size() - ability.getManaCosts().convertedManaCost(); + int numAvailable = getAvailableManaProducers(game).size() - ability.getManaCosts().manaValue(); Card card = game.getCard(ability.getSourceId()); if (card != null && numAvailable > 0) { diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ma/ArtificialScoringSystem.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ma/ArtificialScoringSystem.java index 355688cbb831..8954f642cac3 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ma/ArtificialScoringSystem.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ma/ArtificialScoringSystem.java @@ -39,7 +39,7 @@ public static int getCardDefinitionScore(final Game game, final Card card) { return score; } - final int score = value * 100 - card.getManaCost().convertedManaCost() * 20; + final int score = value * 100 - card.getManaCost().manaValue() * 20; if (card.getCardType().contains(CardType.CREATURE)) { return score + (card.getPower().getValue() + card.getToughness().getValue()) * 10; } else { diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ma/optimizers/impl/OutcomeOptimizer.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ma/optimizers/impl/OutcomeOptimizer.java index c6aed09cfad9..56b3ca9f4d70 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ma/optimizers/impl/OutcomeOptimizer.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ma/optimizers/impl/OutcomeOptimizer.java @@ -1,9 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - package mage.player.ai.ma.optimizers.impl; import mage.abilities.Ability; diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index a07ef825857f..4ccf02ddfe76 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -44,6 +44,7 @@ import mage.player.ai.simulators.CombatGroupSimulator; import mage.player.ai.simulators.CombatSimulator; import mage.player.ai.simulators.CreatureSimulator; +import mage.players.ManaPoolItem; import mage.players.Player; import mage.players.PlayerImpl; import mage.players.net.UserData; @@ -108,8 +109,9 @@ public ComputerPlayer(final ComputerPlayer player) { public boolean chooseMulligan(Game game) { log.debug("chooseMulligan"); if (hand.size() < 6 - || isTestMode() - || game.getClass().getName().contains("Momir")) { + || isTestsMode() // ignore mulligan in tests + || game.getClass().getName().contains("Momir") // ignore mulligan in Momir games + ) { return false; } Set lands = hand.getCards(new FilterLandCard(), game); @@ -1423,6 +1425,9 @@ protected boolean playManaHandling(Ability ability, ManaCost unpaid, final Game if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) { continue; } + if (approvingObject != null && !canUseAsThoughManaToPayManaCost(cost, ability, netMana, manaAbility, mageObject, game)) { + continue; + } if (activateAbility(manaAbility, game)) { return true; } @@ -1441,6 +1446,9 @@ protected boolean playManaHandling(Ability ability, ManaCost unpaid, final Game if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) { continue; } + if (approvingObject != null && !canUseAsThoughManaToPayManaCost(cost, ability, netMana, manaAbility, mageObject, game)) { + continue; + } if (activateAbility(manaAbility, game)) { return true; } @@ -1456,6 +1464,9 @@ protected boolean playManaHandling(Ability ability, ManaCost unpaid, final Game if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) { continue; } + if (approvingObject != null && !canUseAsThoughManaToPayManaCost(cost, ability, netMana, manaAbility, mageObject, game)) { + continue; + } if (activateAbility(manaAbility, game)) { return true; } @@ -1471,6 +1482,9 @@ protected boolean playManaHandling(Ability ability, ManaCost unpaid, final Game if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) { continue; } + if (approvingObject != null && !canUseAsThoughManaToPayManaCost(cost, ability, netMana, manaAbility, mageObject, game)) { + continue; + } if (activateAbility(manaAbility, game)) { return true; } @@ -1486,6 +1500,9 @@ protected boolean playManaHandling(Ability ability, ManaCost unpaid, final Game if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) { continue; } + if (approvingObject != null && !canUseAsThoughManaToPayManaCost(cost, ability, netMana, manaAbility, mageObject, game)) { + continue; + } if (activateAbility(manaAbility, game)) { return true; } @@ -1501,6 +1518,9 @@ protected boolean playManaHandling(Ability ability, ManaCost unpaid, final Game if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) { continue; } + if (approvingObject != null && !canUseAsThoughManaToPayManaCost(cost, ability, netMana, manaAbility, mageObject, game)) { + continue; + } if (activateAbility(manaAbility, game)) { return true; } @@ -1516,6 +1536,9 @@ protected boolean playManaHandling(Ability ability, ManaCost unpaid, final Game if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) { continue; } + if (approvingObject != null && !canUseAsThoughManaToPayManaCost(cost, ability, netMana, manaAbility, mageObject, game)) { + continue; + } if (activateAbility(manaAbility, game)) { return true; } @@ -1555,6 +1578,56 @@ protected boolean playManaHandling(Ability ability, ManaCost unpaid, final Game return false; } + boolean canUseAsThoughManaToPayManaCost(ManaCost checkCost, Ability abilityToPay, Mana manaOption, Ability manaAbility, MageObject manaProducer, Game game) { + // asThoughMana can change producing mana type, so you must check it here + // cause some effects adds additional checks in getAsThoughManaType (example: Draugr Necromancer with snow mana sources) + + // simulate real asThoughMana usage + ManaPoolItem possiblePoolItem; + if (manaOption instanceof ConditionalMana) { + ConditionalMana conditionalNetMana = (ConditionalMana) manaOption; + possiblePoolItem = new ManaPoolItem( + conditionalNetMana, + manaAbility.getSourceObject(game), + conditionalNetMana.getManaProducerOriginalId() != null ? conditionalNetMana.getManaProducerOriginalId() : manaAbility.getOriginalId() + ); + } else { + possiblePoolItem = new ManaPoolItem( + manaOption.getRed(), + manaOption.getGreen(), + manaOption.getBlue(), + manaOption.getWhite(), + manaOption.getBlack(), + manaOption.getGeneric() + manaOption.getColorless(), + manaProducer, + manaAbility.getOriginalId(), + manaOption.getFlag() + ); + } + + // cost can contains multiple mana types, must check each type (is it possible to pay a cost) + for (ManaType checkType : ManaUtil.getManaTypesInCost(checkCost)) { + // affected asThoughMana effect must fit a checkType with pool mana + ManaType possibleAsThoughPoolManaType = game.getContinuousEffects().asThoughMana(checkType, possiblePoolItem, abilityToPay.getSourceId(), abilityToPay, abilityToPay.getControllerId(), game); + if (possibleAsThoughPoolManaType == null) { + continue; // no affected asThough effects + } + boolean canPay; + if (possibleAsThoughPoolManaType == ManaType.COLORLESS) { + // colorless can be payed by any color from the pool + canPay = possiblePoolItem.count() > 0; + } else { + // colored must be payed by specific color from the pool (AsThough already changed it to fit with mana pool) + canPay = possiblePoolItem.get(possibleAsThoughPoolManaType) > 0; + } + if (canPay) { + return true; + } + } + + return false; + } + private Abilities getManaAbilitiesSortedByManaCount(MageObject mageObject, final Game game) { Abilities manaAbilities = mageObject.getAbilities().getAvailableActivatedManaAbilities(Zone.BATTLEFIELD, playerId, game); if (manaAbilities.size() > 1) { @@ -1646,7 +1719,7 @@ public int announceXMana(int min, int max, int multiplier, String message, Game //TODO: improve this int xMin = min * multiplier; int xMax = (max == Integer.MAX_VALUE ? max : max * multiplier); - int numAvailable = getAvailableManaProducers(game).size() - ability.getManaCosts().convertedManaCost(); + int numAvailable = getAvailableManaProducers(game).size() - ability.getManaCosts().manaValue(); if (numAvailable < 0) { numAvailable = 0; } else { @@ -1955,6 +2028,7 @@ public void assignDamage(int damage, List targets, String singleTargetName } @Override + // TODO: add AI support with outcome and replace random with min/max public int getAmount(int min, int max, String message, Game game) { log.debug("getAmount"); if (message.startsWith("Assign damage to ")) { @@ -1966,6 +2040,29 @@ public int getAmount(int min, int max, String message, Game game) { return min; } + @Override + public List getMultiAmount(Outcome outcome, List messages, int min, int max, MultiAmountType type, Game game) { + log.debug("getMultiAmount"); + + int needCount = messages.size(); + List defaultList = MultiAmountType.prepareDefaltValues(needCount, min, max); + if (needCount == 0) { + return defaultList; + } + + // BAD effect + // default list uses minimum possible values, so return it on bad effect + // TODO: need something for damage target and mana logic here, current version is useless but better than random + if (!outcome.isGood()) { + return defaultList; + } + + // GOOD effect + // values must be stable, so AI must able to simulate it and choose correct actions + // fill max values as much as possible + return MultiAmountType.prepareMaxValues(needCount, min, max); + } + @Override public UUID chooseAttackerOrder(List attackers, Game game) { //TODO: improve this @@ -2316,7 +2413,7 @@ public int compare(PickedCard o1, PickedCard o2) { if (differentColorsInCost > 0 && differentColorsInCost < 3) { // if some colors were already chosen, total amount shouldn't be more than 3 if (chosenSymbols.size() + differentColorsInCost < 4) { - for (String symbol : picked.card.getManaCost().getSymbols()) { + for (String symbol : picked.card.getManaCostSymbols()) { symbol = symbol.replace("{", "").replace("}", ""); if (RateCard.isColoredMana(symbol)) { chosenSymbols.add(symbol); @@ -2781,6 +2878,12 @@ private UUID getRandomOpponent(UUID abilityControllerId, Game game) { return randomOpponentId; } + @Override + public SpellAbility chooseAbilityForCast(Card card, Game game, boolean noMana) { + Map useable = PlayerImpl.getSpellAbilities(this.getId(), card, game.getState().getZone(card.getId()), game); + return (SpellAbility) useable.values().stream().findFirst().orElse(null); + } + @Override public boolean equals(Object o) { if (this == o) { @@ -2802,9 +2905,10 @@ public boolean equals(Object o) { @Override public boolean isHuman() { if (human) { - log.error("computer must be not human", new Throwable()); + throw new IllegalStateException("Computer player can't be Human"); + } else { + return false; } - return human; } @Override diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/MCTSPlayer.java b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/MCTSPlayer.java index 36cec441e62e..743809b8a6de 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/MCTSPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/MCTSPlayer.java @@ -80,7 +80,7 @@ public List getPlayableOptions(Game game) { } protected void simulateVariableCosts(Ability ability, List options, Game game) { - int numAvailable = getAvailableManaProducers(game).size() - ability.getManaCosts().convertedManaCost(); + int numAvailable = getAvailableManaProducers(game).size() - ability.getManaCosts().manaValue(); int start = 0; if (!(ability instanceof SpellAbility)) { //only use x=0 on spell abilities diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java index 031eaf16f451..dee2778f8836 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java +++ b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java @@ -90,7 +90,7 @@ private Ability getAction(Game game) { } } if (!ability.getManaCosts().getVariableCosts().isEmpty()) { - int amount = getAvailableManaProducers(game).size() - ability.getManaCosts().convertedManaCost(); + int amount = getAvailableManaProducers(game).size() - ability.getManaCosts().manaValue(); if (amount > 0) { ability = ability.copy(); ability.getManaCostsToPay().add(new GenericManaCost(RandomUtil.nextInt(amount))); @@ -118,7 +118,7 @@ private Ability getAction(Game game) { @Override public boolean triggerAbility(TriggeredAbility source, Game game) { // logger.info("trigger"); - if (source != null && source.canChooseTarget(game)) { + if (source != null && source.canChooseTarget(game, playerId)) { Ability ability; List options = getPlayableOptions(source, game); if (options.isEmpty()) { diff --git a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/GameStateEvaluator.java b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/GameStateEvaluator.java index 20db31c084ba..64a522c954db 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/GameStateEvaluator.java +++ b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/GameStateEvaluator.java @@ -96,7 +96,7 @@ public static int evaluatePermanent(Permanent permanent, Game game, boolean igno } value += permanent.getAbilities().getStaticAbilities(Zone.BATTLEFIELD).size(); value += permanent.getAbilities().getTriggeredAbilities(Zone.BATTLEFIELD).size(); - value += permanent.getManaCost().convertedManaCost(); + value += permanent.getManaCost().manaValue(); //TODO: add a difficulty to calculation to ManaCost - sort permanents by difficulty for casting when evaluating game states return value; } diff --git a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java index 06063ae92eca..f0f11fb19d52 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java @@ -105,7 +105,7 @@ protected void simulateOptions(Game game, ActivatedAbility previousActions) { //add a generic mana cost for each amount possible protected void simulateVariableCosts(Ability ability, Game game) { - int numAvailable = getAvailableManaProducers(game).size() - ability.getManaCosts().convertedManaCost(); + int numAvailable = getAvailableManaProducers(game).size() - ability.getManaCosts().manaValue(); int start = 0; if (!(ability instanceof SpellAbility)) { //only use x=0 on spell abilities diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index a110c7c609d1..9ff3a57443fc 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -50,6 +50,7 @@ import java.awt.*; import java.io.Serializable; import java.util.*; +import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.stream.Collectors; @@ -668,7 +669,7 @@ public boolean choose(Outcome outcome, Cards cards, TargetCard target, Game game options.put("choosable", (Serializable) choosable); } - // if nothing to choose then show window (user must see non selectable items and click on any of them) + // if nothing to choose then show dialog (user must see non selectable items and click on any of them) if (required && choosable.isEmpty()) { required = false; } @@ -743,7 +744,7 @@ public boolean chooseTarget(Outcome outcome, Cards cards, TargetCard target, Abi options.put("choosable", (Serializable) choosable); } - // if nothing to choose then show window (user must see non selectable items and click on any of them) + // if nothing to choose then show dialog (user must see non selectable items and click on any of them) if (required && choosable.isEmpty()) { required = false; } @@ -781,6 +782,7 @@ public boolean chooseTarget(Outcome outcome, Cards cards, TargetCard target, Abi @Override public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Ability source, Game game) { // choose amount + // human can choose or un-choose MULTIPLE targets at once if (gameInCheckPlayableState(game)) { return true; } @@ -790,55 +792,106 @@ public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Ability abilityControllerId = target.getAbilityController(); } + int amountTotal = target.getAmountTotal(game, source); + + // Two steps logic: + // 1. Select targets + // 2. Distribute amount between selected targets + + // 1. Select targets while (canRespond()) { Set possibleTargets = target.possibleTargets(source == null ? null : source.getSourceId(), abilityControllerId, game); boolean required = target.isRequired(source != null ? source.getSourceId() : null, game); if (possibleTargets.isEmpty() - || target.getTargets().size() >= target.getNumberOfTargets()) { + || target.getSize() >= target.getNumberOfTargets()) { + required = false; + } + + // selected + Map options = getOptions(target, null); + java.util.List chosen = target.getTargets(); + options.put("chosen", (Serializable) chosen); + // selectable + java.util.List choosable = new ArrayList<>(); + for (UUID targetId : possibleTargets) { + if (target.canTarget(abilityControllerId, targetId, source, game)) { + choosable.add(targetId); + } + } + if (!choosable.isEmpty()) { + options.put("choosable", (Serializable) choosable); + } + + // if nothing to choose then show dialog (user must see non selectable items and click on any of them) + if (required && choosable.isEmpty()) { required = false; } updateGameStatePriority("chooseTargetAmount", game); prepareForResponse(game); if (!isExecutingMacro()) { - String selectedNames = target.getTargetedName(game); - game.fireSelectTargetEvent(playerId, new MessageToClient(target.getMessage() - + "
Amount remaining: " + target.getAmountRemaining() - + (selectedNames.isEmpty() ? "" : ", selected: " + selectedNames), - getRelatedObjectName(source, game)), - possibleTargets, - required, - getOptions(target, null)); + // target amount uses for damage only, if you see another use case then message must be changed here and on getMultiAmount call + String message = String.format("Select targets to distribute %d damage (selected %d)", amountTotal, target.getTargets().size()); + game.fireSelectTargetEvent(playerId, new MessageToClient(message, getRelatedObjectName(source, game)), possibleTargets, required, options); } waitForResponse(game); UUID responseId = getFixedResponseUUID(game); if (responseId != null) { - if (target.canTarget(abilityControllerId, responseId, source, game)) { - UUID targetId = responseId; - MageObject targetObject = game.getObject(targetId); + if (target.contains(responseId)) { + // unselect + target.remove(responseId); + } else if (possibleTargets.contains(responseId) && target.canTarget(abilityControllerId, responseId, source, game)) { + // select + target.addTarget(responseId, source, game); + } + } else if (!required) { + break; + } + } - boolean removeMode = target.getTargets().contains(targetId) - && chooseUse(outcome, "What do you want to do with " + (targetObject != null ? targetObject.getLogName() : " target") + "?", "", - "Remove from selected", "Add extra amount (remaining " + target.getAmountRemaining() + ")", source, game); + // no targets to choose or disconnected + List targets = target.getTargets(); + if (targets.isEmpty()) { + return false; + } - if (removeMode) { - target.remove(targetId); - } else { - if (target.getAmountRemaining() > 0) { - int amountSelected = getAmount(1, target.getAmountRemaining(), "Select amount", game); - target.addTarget(targetId, amountSelected, source, game); - } - } + // 2. Distribute amount between selected targets - return true; + // prepare targets list with p/t or life stats (cause that's dialog used for damage distribute) + List targetNames = new ArrayList<>(); + for (UUID targetId : targets) { + MageObject targetObject = game.getObject(targetId); + if (targetObject != null) { + targetNames.add(String.format("%s, P/T: %d/%d", + targetObject.getIdName(), + targetObject.getPower().getValue(), + targetObject.getToughness().getValue() + )); + } else { + Player player = game.getPlayer(targetId); + if (player != null) { + targetNames.add(String.format("%s, life: %d", player.getName(), player.getLife())); + } else { + targetNames.add("ERROR, unknown target " + targetId.toString()); } - } else if (!required) { - return false; } } - return false; + // ask and assign new amount + List targetValues = getMultiAmount(outcome, targetNames, 1, amountTotal, MultiAmountType.DAMAGE, game); + for (int i = 0; i < targetValues.size(); i++) { + int newAmount = targetValues.get(i); + UUID targetId = targets.get(i); + if (newAmount <= 0) { + // remove target + target.remove(targetId); + } else { + // set amount + target.setTargetAmount(targetId, newAmount, source, game); + } + } + return true; } @Override @@ -1880,6 +1933,52 @@ public int getAmount(int min, int max, String message, Game game) { } } + @Override + public List getMultiAmount(Outcome outcome, List messages, int min, int max, MultiAmountType type, Game game) { + int needCount = messages.size(); + List defaultList = MultiAmountType.prepareDefaltValues(needCount, min, max); + if (needCount == 0) { + return defaultList; + } + + if (gameInCheckPlayableState(game)) { + return defaultList; + } + + List answer = null; + while (canRespond()) { + updateGameStatePriority("getMultiAmount", game); + prepareForResponse(game); + if (!isExecutingMacro()) { + Map options = new HashMap<>(2); + options.put("title", type.getTitle()); + options.put("header", type.getHeader()); + game.fireGetMultiAmountEvent(playerId, messages, min, max, options); + } + waitForResponse(game); + + // waiting correct values only + if (response.getString() != null) { + answer = MultiAmountType.parseAnswer(response.getString(), needCount, min, max, false); + if (MultiAmountType.isGoodValues(answer, needCount, min, max)) { + break; + } else { + // it's not normal: can be cheater or a wrong GUI checks + answer = null; + logger.error(String.format("GUI return wrong MultiAmountType values: %d %d %d - %s", needCount, min, max, response.getString())); + game.informPlayer(this, "Error, you must enter correct values."); + } + } + } + + if (answer != null) { + return answer; + } else { + // something wrong, e.g. player disconnected + return defaultList; + } + } + @Override public void sideboard(Match match, Deck deck) { match.fireSideboardEvent(playerId, deck); @@ -2019,7 +2118,7 @@ private boolean suppressAbilityPicker(ActivatedAbility ability, Game game) { // hide on alternative cost activated if (!getCastSourceIdWithAlternateMana().contains(ability.getSourceId()) - && ability.getManaCostsToPay().convertedManaCost() > 0) { + && ability.getManaCostsToPay().manaValue() > 0) { return true; } @@ -2040,7 +2139,7 @@ public SpellAbility chooseAbilityForCast(Card card, Game game, boolean nonMana) return null; } - MageObject object = game.getObject(card.getId()); + MageObject object = game.getObject(card.getId()); // must be object to find real abilities (example: commander) if (object != null) { String message = "Choose ability to cast" + (nonMana ? " for FREE" : "") + "
" + object.getLogName(); LinkedHashMap useableAbilities = getSpellAbilities(playerId, object, game.getState().getZone(object.getId()), game); @@ -2065,6 +2164,8 @@ public SpellAbility chooseAbilityForCast(Card card, Game game, boolean nonMana) } } } + + // default ability (example: on disconnect or cancel) return card.getSpellAbility(); } diff --git a/Mage.Server/src/main/java/mage/server/game/GameController.java b/Mage.Server/src/main/java/mage/server/game/GameController.java index f6ea6a032abc..8317ea91cd10 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameController.java +++ b/Mage.Server/src/main/java/mage/server/game/GameController.java @@ -219,6 +219,9 @@ private void init() { case AMOUNT: amount(event.getPlayerId(), event.getMessage(), event.getMin(), event.getMax()); break; + case MULTI_AMOUNT: + multiAmount(event.getPlayerId(), event.getMessages(), event.getMin(), event.getMax(), event.getOptions()); + break; case PERSONAL_MESSAGE: informPersonal(event.getPlayerId(), event.getMessage()); break; @@ -844,6 +847,10 @@ private synchronized void amount(UUID playerId, final String message, final int perform(playerId, playerId1 -> getGameSession(playerId1).getAmount(message, min, max)); } + private synchronized void multiAmount(UUID playerId, final List messages, final int min, final int max, final Map options) throws MageException { + perform(playerId, playerId1 -> getGameSession(playerId1).getMultiAmount(messages, min, max, options)); + } + private void informOthers(UUID playerId) throws MageException { StringBuilder message = new StringBuilder(); if (game.getStep() != null) { diff --git a/Mage.Server/src/main/java/mage/server/game/GameSessionPlayer.java b/Mage.Server/src/main/java/mage/server/game/GameSessionPlayer.java index b7f3c9aae9a9..0939c26eb6a8 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameSessionPlayer.java +++ b/Mage.Server/src/main/java/mage/server/game/GameSessionPlayer.java @@ -114,6 +114,13 @@ public void getAmount(final String message, final int min, final int max) { } } + public void getMultiAmount(final List messages, final int min, final int max, final Map options) { + if (!killed) { + userManager.getUser(userId).ifPresent(user + -> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_GET_MULTI_AMOUNT, game.getId(), new GameClientMessage(messages, min, max, options)))); + } + } + public void endGameInfo(Table table) { if (!killed) { userManager.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.END_GAME_INFO, game.getId(), getGameEndView(playerId, table)))); diff --git a/Mage.Sets/src/mage/cards/a/Abeyance.java b/Mage.Sets/src/mage/cards/a/Abeyance.java index 94ba0ba76d73..f1ce9ca85f2e 100644 --- a/Mage.Sets/src/mage/cards/a/Abeyance.java +++ b/Mage.Sets/src/mage/cards/a/Abeyance.java @@ -87,7 +87,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { return false; } if (event.getType() == GameEvent.EventType.CAST_SPELL - && (object.isInstant() || object.isSorcery())) { + && object.isInstantOrSorcery()) { return true; } if (event.getType() == GameEvent.EventType.ACTIVATE_ABILITY) { diff --git a/Mage.Sets/src/mage/cards/a/AbruptDecay.java b/Mage.Sets/src/mage/cards/a/AbruptDecay.java index 4c1e7f3ebf3c..5e10f06f22ca 100644 --- a/Mage.Sets/src/mage/cards/a/AbruptDecay.java +++ b/Mage.Sets/src/mage/cards/a/AbruptDecay.java @@ -13,7 +13,7 @@ import mage.constants.ComparisonType; import mage.constants.Zone; import mage.filter.common.FilterNonlandPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetNonlandPermanent; /** @@ -22,10 +22,10 @@ */ public final class AbruptDecay extends CardImpl { - private static final FilterNonlandPermanent filter = new FilterNonlandPermanent("nonland permanent with converted mana cost 3 or less"); + private static final FilterNonlandPermanent filter = new FilterNonlandPermanent("nonland permanent with mana value 3 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public AbruptDecay(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/a/AbundantHarvest.java b/Mage.Sets/src/mage/cards/a/AbundantHarvest.java new file mode 100644 index 000000000000..cb2b947dde3a --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AbundantHarvest.java @@ -0,0 +1,84 @@ +package mage.cards.a; + +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.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AbundantHarvest extends CardImpl { + + public AbundantHarvest(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{G}"); + + // Choose land or nonland. Reveal cards from the top of your library until you reveal a card of the chosen kind. Put that card into your hand and the rest on the bottom of your library in a random order. + this.getSpellAbility().addEffect(new AbundantHarvestEffect()); + } + + private AbundantHarvest(final AbundantHarvest card) { + super(card); + } + + @Override + public AbundantHarvest copy() { + return new AbundantHarvest(this); + } +} + +class AbundantHarvestEffect extends OneShotEffect { + + AbundantHarvestEffect() { + super(Outcome.Benefit); + staticText = "Choose land or nonland. Reveal cards from the top of your library " + + "until you reveal a card of the chosen kind. Put that card into your hand " + + "and the rest on the bottom of your library in a random order."; + } + + private AbundantHarvestEffect(final AbundantHarvestEffect effect) { + super(effect); + } + + @Override + public AbundantHarvestEffect copy() { + return new AbundantHarvestEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + boolean land = player.chooseUse( + Outcome.Neutral, "Choose land or nonland", null, + "Land", "Nonland", source, game + ); + Cards toReveal = new CardsImpl(); + Card toHand = null; + for (Card card : player.getLibrary().getCards(game)) { + if (card == null) { + continue; + } + toReveal.add(card); + if (card.isLand() == land) { + toHand = card; + break; + } + } + player.revealCards(source, toReveal, game); + if (toHand != null) { + toReveal.remove(toHand); + player.moveCards(toHand, Zone.HAND, source, game); + } + player.putCardsOnBottomOfLibrary(toReveal, game, source, false); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/a/AcademicDispute.java b/Mage.Sets/src/mage/cards/a/AcademicDispute.java new file mode 100644 index 000000000000..2e35b10d99f4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AcademicDispute.java @@ -0,0 +1,80 @@ +package mage.cards.a; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.LearnEffect; +import mage.abilities.effects.common.combat.BlocksIfAbleTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author weirddan455 + */ +public final class AcademicDispute extends CardImpl { + + public AcademicDispute(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}"); + + // Target creature blocks this turn if able. You may have that creature gain reach until end of turn. + this.getSpellAbility().addEffect(new BlocksIfAbleTargetEffect(Duration.EndOfTurn)); + this.getSpellAbility().addEffect(new AcademicDisputeEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + + // Learn. + this.getSpellAbility().addEffect(new LearnEffect().concatBy("
")); + } + + private AcademicDispute(final AcademicDispute card) { + super(card); + } + + @Override + public AcademicDispute copy() { + return new AcademicDispute(this); + } +} + +class AcademicDisputeEffect extends OneShotEffect { + + AcademicDisputeEffect() { + super(Outcome.AddAbility); + this.staticText = "You may have it gain reach until end of turn"; + } + + private AcademicDisputeEffect(final AcademicDisputeEffect effect) { + super(effect); + } + + @Override + public AcademicDisputeEffect copy() { + return new AcademicDisputeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player != null) { + Permanent permanent = game.getPermanent(this.targetPointer.getFirst(game, source)); + if (permanent != null) { + if (player.chooseUse(outcome, "Have " + permanent.getLogName() + " gain reach until end of turn?", source, game)) { + GainAbilityTargetEffect effect = new GainAbilityTargetEffect(ReachAbility.getInstance(), Duration.EndOfTurn); + game.addEffect(effect, source); + return true; + } + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/a/AcademicProbation.java b/Mage.Sets/src/mage/cards/a/AcademicProbation.java new file mode 100644 index 000000000000..229bc44c3428 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AcademicProbation.java @@ -0,0 +1,96 @@ +package mage.cards.a; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.Effect; +import mage.abilities.effects.RestrictionEffect; +import mage.abilities.effects.common.OpponentsCantCastChosenUntilNextTurnEffect; +import mage.abilities.effects.common.ChooseACardNameEffect; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetNonlandPermanent; + +/** + * + * @author htrajan + */ +public final class AcademicProbation extends CardImpl { + + public AcademicProbation(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{W}"); + + this.subtype.add(SubType.LESSON); + + // Choose one — + // • Choose a nonland card name. Opponents can't cast spells with the chosen name until your next turn. + Effect effect = new ChooseACardNameEffect(ChooseACardNameEffect.TypeOfName.NON_LAND_NAME); + effect.setText("Choose a nonland card name"); + this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addEffect(new OpponentsCantCastChosenUntilNextTurnEffect().setText("opponents can't cast spells with the chosen name until your next turn")); + + // • Choose target nonland permanent. Until your next turn, it can't attack or block, and its activated abilities can't be activated. + Mode restrictMode = new Mode(); + restrictMode.addEffect(new AcademicProbationRestrictionEffect()); + restrictMode.addTarget(new TargetNonlandPermanent()); + this.getSpellAbility().addMode(restrictMode); + } + + private AcademicProbation(final AcademicProbation card) { + super(card); + } + + @Override + public AcademicProbation copy() { + return new AcademicProbation(this); + } +} + +class AcademicProbationRestrictionEffect extends RestrictionEffect { + + AcademicProbationRestrictionEffect() { + super(Duration.UntilYourNextTurn, Outcome.UnboostCreature); + staticText = "choose target nonland permanent. Until your next turn, it can't attack or block, and its activated abilities can't be activated"; + } + + AcademicProbationRestrictionEffect(final AcademicProbationRestrictionEffect effect) { + super(effect); + } + + @Override + public AcademicProbationRestrictionEffect copy() { + return new AcademicProbationRestrictionEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + return this.targetPointer.getTargets(game, source).contains(permanent.getId()); + } + + @Override + public boolean canAttack(Game game, boolean canUseChooseDialogs) { + return false; + } + + @Override + public boolean canBlock(Permanent attacker, Permanent blocker, Ability source, Game game, boolean canUseChooseDialogs) { + return false; + } + + @Override + public boolean canUseActivatedAbilities(Permanent permanent, Ability source, Game game, boolean canUseChooseDialogs) { + return false; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/a/AcademyResearchers.java b/Mage.Sets/src/mage/cards/a/AcademyResearchers.java index 1b02dcd3a121..b6646bf014ed 100644 --- a/Mage.Sets/src/mage/cards/a/AcademyResearchers.java +++ b/Mage.Sets/src/mage/cards/a/AcademyResearchers.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.other.AuraCardCanAttachToPermanentId; +import mage.filter.predicate.card.AuraCardCanAttachToPermanentId; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/a/AcceleratedMutation.java b/Mage.Sets/src/mage/cards/a/AcceleratedMutation.java index 593f149d3e0e..cea4ac187420 100644 --- a/Mage.Sets/src/mage/cards/a/AcceleratedMutation.java +++ b/Mage.Sets/src/mage/cards/a/AcceleratedMutation.java @@ -3,7 +3,7 @@ import java.util.UUID; import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.common.HighestConvertedManaCostValue; +import mage.abilities.dynamicvalue.common.HighestManaValueCount; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -21,7 +21,7 @@ public AcceleratedMutation(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{G}{G}"); // Target creature gets +X/+X until end of turn, where X is the highest converted mana cost among permanents you control. - DynamicValue amount = new HighestConvertedManaCostValue(); + DynamicValue amount = new HighestManaValueCount(); this.getSpellAbility().addEffect(new BoostTargetEffect(amount, amount, Duration.EndOfTurn, true)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/cards/a/AccessTunnel.java b/Mage.Sets/src/mage/cards/a/AccessTunnel.java new file mode 100644 index 000000000000..604568f491e2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AccessTunnel.java @@ -0,0 +1,55 @@ +package mage.cards.a; + +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.common.combat.CantBeBlockedTargetEffect; +import mage.abilities.mana.ColorlessManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Duration; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.PowerPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AccessTunnel extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("creature with power 3 or less"); + + static { + filter.add(new PowerPredicate(ComparisonType.FEWER_THAN, 4)); + } + + public AccessTunnel(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // {T}: Add {C}. + this.addAbility(new ColorlessManaAbility()); + + // {3}, {T}: Target creature with power 3 or less can't be blocked this turn. + Ability ability = new SimpleActivatedAbility( + new CantBeBlockedTargetEffect(Duration.EndOfTurn), new GenericManaCost(3) + ); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private AccessTunnel(final AccessTunnel card) { + super(card); + } + + @Override + public AccessTunnel copy() { + return new AccessTunnel(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AcclaimedContender.java b/Mage.Sets/src/mage/cards/a/AcclaimedContender.java index 7d77b6799d0f..9ffedd216559 100644 --- a/Mage.Sets/src/mage/cards/a/AcclaimedContender.java +++ b/Mage.Sets/src/mage/cards/a/AcclaimedContender.java @@ -17,7 +17,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/a/AccomplishedAlchemist.java b/Mage.Sets/src/mage/cards/a/AccomplishedAlchemist.java new file mode 100644 index 000000000000..3ea63de1b045 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AccomplishedAlchemist.java @@ -0,0 +1,49 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.Mana; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.dynamicvalue.common.ControllerGotLifeCount; +import mage.abilities.mana.AnyColorManaAbility; +import mage.abilities.mana.DynamicManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.watchers.common.PlayerGainedLifeWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AccomplishedAlchemist extends CardImpl { + + public AccomplishedAlchemist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.ELF); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(2); + this.toughness = new MageInt(5); + + // {T}: Add one mana of any color. + this.addAbility(new AnyColorManaAbility()); + + // {T}: Add X mana of any one color, where X is the amount of life you gained this turn. + this.addAbility(new DynamicManaAbility( + new Mana(0, 0, 0, 0, 0, 0, 1, 0), + ControllerGotLifeCount.instance, new TapSourceCost(), "Add X mana " + + "of any one color, where X is the amount of life you gained this turn", true + ).addHint(ControllerGotLifeCount.getHint()), new PlayerGainedLifeWatcher()); + } + + private AccomplishedAlchemist(final AccomplishedAlchemist card) { + super(card); + } + + @Override + public AccomplishedAlchemist copy() { + return new AccomplishedAlchemist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AchHansRun.java b/Mage.Sets/src/mage/cards/a/AchHansRun.java index db086fa1e6cf..1c6eb303acdf 100644 --- a/Mage.Sets/src/mage/cards/a/AchHansRun.java +++ b/Mage.Sets/src/mage/cards/a/AchHansRun.java @@ -51,7 +51,7 @@ class AchHansRunEffect extends OneShotEffect { AchHansRunEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "you may say \"Ach! Hans, run! It's the …\" and the name of a creature card. If you do, search your library for a card with that name, put it onto the battlefield, then shuffle your library. That creature gains haste. Exile it at the beginning of the next end step"; + this.staticText = "you may say \"Ach! Hans, run! It's the …\" and the name of a creature card. If you do, search your library for a card with that name, put it onto the battlefield, then shuffle. That creature gains haste. Exile it at the beginning of the next end step"; } private AchHansRunEffect(final AchHansRunEffect effect) { diff --git a/Mage.Sets/src/mage/cards/a/AcidSpewerDragon.java b/Mage.Sets/src/mage/cards/a/AcidSpewerDragon.java index 73aeea63b52b..81047f0d9ed8 100644 --- a/Mage.Sets/src/mage/cards/a/AcidSpewerDragon.java +++ b/Mage.Sets/src/mage/cards/a/AcidSpewerDragon.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/a/Acquire.java b/Mage.Sets/src/mage/cards/a/Acquire.java index 58b3781e877f..da195cb4b49c 100644 --- a/Mage.Sets/src/mage/cards/a/Acquire.java +++ b/Mage.Sets/src/mage/cards/a/Acquire.java @@ -49,7 +49,7 @@ class AcquireEffect extends OneShotEffect { public AcquireEffect() { super(Outcome.PutCardInPlay); - staticText = "Search target opponent's library for an artifact card and put that card onto the battlefield under your control. Then that player shuffles their library"; + staticText = "Search target opponent's library for an artifact card and put that card onto the battlefield under your control. Then that player shuffles"; } public AcquireEffect(final AcquireEffect effect) { diff --git a/Mage.Sets/src/mage/cards/a/AdNauseam.java b/Mage.Sets/src/mage/cards/a/AdNauseam.java index 818c72e95bbc..b9bb000fd867 100644 --- a/Mage.Sets/src/mage/cards/a/AdNauseam.java +++ b/Mage.Sets/src/mage/cards/a/AdNauseam.java @@ -40,7 +40,7 @@ class AdNauseamEffect extends OneShotEffect { public AdNauseamEffect() { super(Outcome.DrawCard); - this.staticText = "Reveal the top card of your library and put that card into your hand. You lose life equal to its converted mana cost. You may repeat this process any number of times"; + this.staticText = "Reveal the top card of your library and put that card into your hand. You lose life equal to its mana value. You may repeat this process any number of times"; } public AdNauseamEffect(final AdNauseamEffect effect) { @@ -54,7 +54,7 @@ public AdNauseamEffect copy() { @Override public boolean apply(Game game, Ability source) { - String message = "Reveal the top card of your library and put that card into your hand? You lose life equal to its converted mana cost."; + String message = "Reveal the top card of your library and put that card into your hand? You lose life equal to its mana value."; Card sourceCard = game.getCard(source.getSourceId()); Player controller = game.getPlayer(source.getControllerId()); if (controller == null || sourceCard == null) { @@ -66,14 +66,14 @@ public boolean apply(Game game, Ability source) { break; } controller.moveCards(card, Zone.HAND, source, game); - int cmc = card.getConvertedManaCost(); + int cmc = card.getManaValue(); if (cmc > 0) { controller.loseLife(cmc, game, source, false); } controller.revealCards(sourceCard.getIdName() + " put into hand", new CardsImpl(card), game); // AI workaround to stop infinite choose (only one card allows) - if (!controller.isHuman() && !controller.isTestMode()) { + if (controller.isComputer()) { break; } } diff --git a/Mage.Sets/src/mage/cards/a/AdarkarValkyrie.java b/Mage.Sets/src/mage/cards/a/AdarkarValkyrie.java index 9056ecd306bb..57f8d3231d39 100644 --- a/Mage.Sets/src/mage/cards/a/AdarkarValkyrie.java +++ b/Mage.Sets/src/mage/cards/a/AdarkarValkyrie.java @@ -16,10 +16,9 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/a/AdmonitionAngel.java b/Mage.Sets/src/mage/cards/a/AdmonitionAngel.java index bf6968130217..b40d865ae975 100644 --- a/Mage.Sets/src/mage/cards/a/AdmonitionAngel.java +++ b/Mage.Sets/src/mage/cards/a/AdmonitionAngel.java @@ -17,7 +17,7 @@ import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; /** diff --git a/Mage.Sets/src/mage/cards/a/AdrixAndNevTwincasters.java b/Mage.Sets/src/mage/cards/a/AdrixAndNevTwincasters.java new file mode 100644 index 000000000000..f246d7a95e2f --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AdrixAndNevTwincasters.java @@ -0,0 +1,45 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.replacement.CreateTwiceThatManyTokensEffect; +import mage.abilities.keyword.WardAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AdrixAndNevTwincasters extends CardImpl { + + public AdrixAndNevTwincasters(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.MERFOLK); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Ward {2} + this.addAbility(new WardAbility(new ManaCostsImpl<>("{2}"))); + + // If one or more tokens would be created under your control, twice that many of those tokens are created instead. + this.addAbility(new SimpleStaticAbility(new CreateTwiceThatManyTokensEffect())); + } + + private AdrixAndNevTwincasters(final AdrixAndNevTwincasters card) { + super(card); + } + + @Override + public AdrixAndNevTwincasters copy() { + return new AdrixAndNevTwincasters(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AegarTheFreezingFlame.java b/Mage.Sets/src/mage/cards/a/AegarTheFreezingFlame.java index 7cb7b5feb7cf..8a321b2b29b5 100644 --- a/Mage.Sets/src/mage/cards/a/AegarTheFreezingFlame.java +++ b/Mage.Sets/src/mage/cards/a/AegarTheFreezingFlame.java @@ -56,12 +56,7 @@ private AegarTheFreezingFlameTriggeredAbility(final AegarTheFreezingFlameTrigger @Override public boolean checkEventType(GameEvent event, Game game) { - switch (event.getType()) { - case DAMAGED_CREATURE: - case DAMAGED_PLANESWALKER: - return true; - } - return false; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override @@ -98,12 +93,8 @@ class AegarTheFreezingFlameWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { - switch (event.getType()) { - case DAMAGED_CREATURE: - case DAMAGED_PLANESWALKER: - break; - default: - return; + if (event.getType() != GameEvent.EventType.DAMAGED_PERMANENT) { + return; } DamagedEvent dEvent = (DamagedEvent) event; MageObject sourceObject = game.getObject(event.getSourceId()); diff --git a/Mage.Sets/src/mage/cards/a/AegisAngel.java b/Mage.Sets/src/mage/cards/a/AegisAngel.java index 644190bba827..7a7d812249ff 100644 --- a/Mage.Sets/src/mage/cards/a/AegisAngel.java +++ b/Mage.Sets/src/mage/cards/a/AegisAngel.java @@ -15,7 +15,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import mage.watchers.common.LostControlWatcher; diff --git a/Mage.Sets/src/mage/cards/a/AegisAutomaton.java b/Mage.Sets/src/mage/cards/a/AegisAutomaton.java index 2127d13412f8..9fc1e09aea84 100644 --- a/Mage.Sets/src/mage/cards/a/AegisAutomaton.java +++ b/Mage.Sets/src/mage/cards/a/AegisAutomaton.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/a/AerialGuide.java b/Mage.Sets/src/mage/cards/a/AerialGuide.java index 31a7fcae5732..11e1ffbad403 100644 --- a/Mage.Sets/src/mage/cards/a/AerialGuide.java +++ b/Mage.Sets/src/mage/cards/a/AerialGuide.java @@ -13,7 +13,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.common.FilterAttackingCreature; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetAttackingCreature; /** diff --git a/Mage.Sets/src/mage/cards/a/AetherHelix.java b/Mage.Sets/src/mage/cards/a/AetherHelix.java new file mode 100644 index 000000000000..df27bab0ac85 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AetherHelix.java @@ -0,0 +1,36 @@ +package mage.cards.a; + +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AetherHelix extends CardImpl { + + public AetherHelix(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{G}{U}"); + + // Return target permanent to its owner's hand. Return target permanent card from your graveyard to your hand. + this.getSpellAbility().addEffect(new ReturnToHandTargetEffect(true).setText("return target " + + "permanent to its owner's hand. Return target permanent card from your graveyard to your hand")); + this.getSpellAbility().addTarget(new TargetPermanent()); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_PERMANENT)); + } + + private AetherHelix(final AetherHelix card) { + super(card); + } + + @Override + public AetherHelix copy() { + return new AetherHelix(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AetherMeltdown.java b/Mage.Sets/src/mage/cards/a/AetherMeltdown.java index cd738baba4a5..5fe1e7abe7ba 100644 --- a/Mage.Sets/src/mage/cards/a/AetherMeltdown.java +++ b/Mage.Sets/src/mage/cards/a/AetherMeltdown.java @@ -24,7 +24,7 @@ */ public final class AetherMeltdown extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("creature or vehicle"); + private static final FilterPermanent filter = new FilterPermanent("creature or Vehicle"); static { filter.add(Predicates.or(CardType.CREATURE.getPredicate(), SubType.VEHICLE.getPredicate())); @@ -47,7 +47,6 @@ public AetherMeltdown(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new EntersBattlefieldTriggeredAbility(new GetEnergyCountersControllerEffect(2))); // Enchanted permanent gets -4/-0. Effect effect = new BoostEnchantedEffect(-4, 0, Duration.WhileOnBattlefield); - effect.setText("Enchanted permanent gets -4/-0"); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); } diff --git a/Mage.Sets/src/mage/cards/a/AetherMutation.java b/Mage.Sets/src/mage/cards/a/AetherMutation.java index af8a59c3be30..e0a53ae9dbe6 100644 --- a/Mage.Sets/src/mage/cards/a/AetherMutation.java +++ b/Mage.Sets/src/mage/cards/a/AetherMutation.java @@ -2,7 +2,7 @@ package mage.cards.a; import java.util.UUID; -import mage.abilities.dynamicvalue.common.TargetConvertedManaCost; +import mage.abilities.dynamicvalue.common.TargetManaValue; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.CardImpl; @@ -25,7 +25,7 @@ public AetherMutation(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); // create X 1/1 green Saproling creature tokens, where X is that creature's converted mana cost. - this.getSpellAbility().addEffect(new CreateTokenEffect(new SaprolingToken(), TargetConvertedManaCost.instance)); + this.getSpellAbility().addEffect(new CreateTokenEffect(new SaprolingToken(), TargetManaValue.instance)); } private AetherMutation(final AetherMutation card) { diff --git a/Mage.Sets/src/mage/cards/a/AetherVial.java b/Mage.Sets/src/mage/cards/a/AetherVial.java index 3a4ce2c02fb1..b5a9f24d6146 100644 --- a/Mage.Sets/src/mage/cards/a/AetherVial.java +++ b/Mage.Sets/src/mage/cards/a/AetherVial.java @@ -18,7 +18,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -53,7 +53,7 @@ class AetherVialEffect extends OneShotEffect { public AetherVialEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "You may put a creature card with converted mana cost equal to the number of charge counters on {this} from your hand onto the battlefield"; + this.staticText = "You may put a creature card with mana value equal to the number of charge counters on {this} from your hand onto the battlefield"; } public AetherVialEffect(final AetherVialEffect effect) { @@ -76,8 +76,8 @@ public boolean apply(Game game, Ability source) { } int count = permanent.getCounters(game).getCount(CounterType.CHARGE); - FilterCreatureCard filter = new FilterCreatureCard("creature card with converted mana cost equal to " + count); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, count)); + FilterCreatureCard filter = new FilterCreatureCard("creature card with mana value equal to " + count); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, count)); String choiceText = "Put a " + filter.getMessage() + " from your hand onto the battlefield?"; Player controller = game.getPlayer(source.getControllerId()); diff --git a/Mage.Sets/src/mage/cards/a/AetherbornMarauder.java b/Mage.Sets/src/mage/cards/a/AetherbornMarauder.java index 2307965db850..e2ef434a5705 100644 --- a/Mage.Sets/src/mage/cards/a/AetherbornMarauder.java +++ b/Mage.Sets/src/mage/cards/a/AetherbornMarauder.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/a/AethergeodeMiner.java b/Mage.Sets/src/mage/cards/a/AethergeodeMiner.java index c76c420988e4..442fba4163a2 100644 --- a/Mage.Sets/src/mage/cards/a/AethergeodeMiner.java +++ b/Mage.Sets/src/mage/cards/a/AethergeodeMiner.java @@ -1,7 +1,5 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; @@ -18,9 +16,11 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; + +import java.util.UUID; /** - * * @author fireshoes */ public final class AethergeodeMiner extends CardImpl { @@ -37,7 +37,7 @@ public AethergeodeMiner(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new AttacksTriggeredAbility(new GetEnergyCountersControllerEffect(2), false)); // Pay {E}{E}: Exile Aethergeode Miner, then return it to the battlefield under its owner's control. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new AethergeodeMinerEffect(), new PayEnergyCost(2))); + this.addAbility(new SimpleActivatedAbility(new AethergeodeMinerEffect(), new PayEnergyCost(2))); } private AethergeodeMiner(final AethergeodeMiner card) { @@ -52,12 +52,12 @@ public AethergeodeMiner copy() { class AethergeodeMinerEffect extends OneShotEffect { - public AethergeodeMinerEffect() { + AethergeodeMinerEffect() { super(Outcome.Neutral); - this.staticText = "Exile {this}, then return it to the battlefield under its owner's control"; + this.staticText = "exile {this}, then return it to the battlefield under its owner's control"; } - public AethergeodeMinerEffect(final AethergeodeMinerEffect effect) { + private AethergeodeMinerEffect(final AethergeodeMinerEffect effect) { super(effect); } @@ -68,15 +68,14 @@ public AethergeodeMinerEffect copy() { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - if (permanent.moveToExile(source.getSourceId(), "Aethergeode Miner", source, game)) { - Card card = game.getExile().getCard(source.getSourceId(), game); - if (card != null) { - return card.moveToZone(Zone.BATTLEFIELD, source, game, false); - } - } + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (player == null || permanent == null) { + return false; } - return false; + Card card = permanent.getMainCard(); + player.moveCards(permanent, Zone.EXILED, source, game); + player.moveCards(card, Zone.BATTLEFIELD, source, game, false, false, true, null); + return true; } } diff --git a/Mage.Sets/src/mage/cards/a/AethersquallAncient.java b/Mage.Sets/src/mage/cards/a/AethersquallAncient.java index 0bf7d49f8a1b..cd95c0822cff 100644 --- a/Mage.Sets/src/mage/cards/a/AethersquallAncient.java +++ b/Mage.Sets/src/mage/cards/a/AethersquallAncient.java @@ -16,7 +16,7 @@ import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/a/AfflictedDeserter.java b/Mage.Sets/src/mage/cards/a/AfflictedDeserter.java index fe81fcd96f41..d340c1ace390 100644 --- a/Mage.Sets/src/mage/cards/a/AfflictedDeserter.java +++ b/Mage.Sets/src/mage/cards/a/AfflictedDeserter.java @@ -1,20 +1,14 @@ - package mage.cards.a; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** * @author BetaSteward @@ -34,8 +28,7 @@ public AfflictedDeserter(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each upkeep, if no spells were cast last turn, transform Afflicted Deserter. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private AfflictedDeserter(final AfflictedDeserter card) { diff --git a/Mage.Sets/src/mage/cards/a/AgadeemOccultist.java b/Mage.Sets/src/mage/cards/a/AgadeemOccultist.java index 359eedf26d7d..99b3654a0e5a 100644 --- a/Mage.Sets/src/mage/cards/a/AgadeemOccultist.java +++ b/Mage.Sets/src/mage/cards/a/AgadeemOccultist.java @@ -55,7 +55,7 @@ class AgadeemOccultistEffect extends OneShotEffect { public AgadeemOccultistEffect() { super(Outcome.GainControl); - this.staticText = "Put target creature card from an opponent's graveyard onto the battlefield under your control if its converted mana cost is less than or equal to the number of Allies you control"; + this.staticText = "Put target creature card from an opponent's graveyard onto the battlefield under your control if its mana value is less than or equal to the number of Allies you control"; } public AgadeemOccultistEffect(final AgadeemOccultistEffect effect) { @@ -86,7 +86,7 @@ public boolean apply(Game game, Ability source) { if (!target.getTargets().isEmpty()) { Card card = game.getCard(target.getFirstTarget()); if (card != null) { - if (card.getConvertedManaCost() <= allycount) { + if (card.getManaValue() <= allycount) { return controller.moveCards(card, Zone.BATTLEFIELD, source, game); } } diff --git a/Mage.Sets/src/mage/cards/a/AgadeemsAwakening.java b/Mage.Sets/src/mage/cards/a/AgadeemsAwakening.java index 40a3c19d78ff..eb16de9bef99 100644 --- a/Mage.Sets/src/mage/cards/a/AgadeemsAwakening.java +++ b/Mage.Sets/src/mage/cards/a/AgadeemsAwakening.java @@ -40,7 +40,7 @@ public AgadeemsAwakening(UUID ownerId, CardSetInfo setInfo) { // Return from your graveyard to the battlefield any number of target creature cards that each have a different converted mana cost X or less. this.getLeftHalfCard().getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect().setText( "return from your graveyard to the battlefield any number of target creature cards " + - "that each have a different converted mana cost X or less" + "that each have a different mana value X or less" )); this.getLeftHalfCard().getSpellAbility().setTargetAdjuster(AgadeemsAwakeningAdjuster.instance); @@ -81,7 +81,7 @@ public void adjustTargets(Ability ability, Game game) { class AgadeemsAwakeningTarget extends TargetCardInYourGraveyard { private static final FilterCard filter - = new FilterCreatureCard("creature cards that each have a different converted mana cost X or less"); + = new FilterCreatureCard("creature cards that each have a different mana value X or less"); private final int xValue; AgadeemsAwakeningTarget(int xValue) { @@ -105,11 +105,11 @@ public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game ga Set cmcs = this.getTargets() .stream() .map(game::getCard) - .map(MageObject::getConvertedManaCost) + .map(MageObject::getManaValue) .collect(Collectors.toSet()); possibleTargets.removeIf(uuid -> { Card card = game.getCard(uuid); - return card != null && (cmcs.contains(card.getConvertedManaCost()) || card.getConvertedManaCost() > xValue); + return card != null && (cmcs.contains(card.getManaValue()) || card.getManaValue() > xValue); }); return possibleTargets; } diff --git a/Mage.Sets/src/mage/cards/a/AgelessGuardian.java b/Mage.Sets/src/mage/cards/a/AgelessGuardian.java new file mode 100644 index 000000000000..7ea667edf1d0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AgelessGuardian.java @@ -0,0 +1,33 @@ +package mage.cards.a; + +import mage.MageInt; +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 AgelessGuardian extends CardImpl { + + public AgelessGuardian(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + } + + private AgelessGuardian(final AgelessGuardian card) { + super(card); + } + + @Override + public AgelessGuardian copy() { + return new AgelessGuardian(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AhnCropInvader.java b/Mage.Sets/src/mage/cards/a/AhnCropInvader.java index 91b1646bdf73..c23fbdf0b894 100644 --- a/Mage.Sets/src/mage/cards/a/AhnCropInvader.java +++ b/Mage.Sets/src/mage/cards/a/AhnCropInvader.java @@ -20,7 +20,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/a/AjaniAdversaryOfTyrants.java b/Mage.Sets/src/mage/cards/a/AjaniAdversaryOfTyrants.java index ad0dac5417b5..c6efc37d1691 100644 --- a/Mage.Sets/src/mage/cards/a/AjaniAdversaryOfTyrants.java +++ b/Mage.Sets/src/mage/cards/a/AjaniAdversaryOfTyrants.java @@ -15,7 +15,7 @@ import mage.constants.ComparisonType; import mage.counters.CounterType; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.command.emblems.AjaniAdversaryOfTyrantsEmblem; import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetCreaturePermanent; @@ -27,10 +27,10 @@ public final class AjaniAdversaryOfTyrants extends CardImpl { private static final FilterCreatureCard filter - = new FilterCreatureCard("creature card with converted mana cost 2 or less from your graveyard"); + = new FilterCreatureCard("creature card with mana value 2 or less from your graveyard"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); } public AjaniAdversaryOfTyrants(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/a/AjaniSteadfast.java b/Mage.Sets/src/mage/cards/a/AjaniSteadfast.java index 9c438edab0ac..c951d1b4f028 100644 --- a/Mage.Sets/src/mage/cards/a/AjaniSteadfast.java +++ b/Mage.Sets/src/mage/cards/a/AjaniSteadfast.java @@ -22,7 +22,7 @@ import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterPlaneswalkerPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.command.emblems.AjaniSteadfastEmblem; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/a/AjaniTheGreathearted.java b/Mage.Sets/src/mage/cards/a/AjaniTheGreathearted.java index fe392e8e5048..837660006db7 100644 --- a/Mage.Sets/src/mage/cards/a/AjaniTheGreathearted.java +++ b/Mage.Sets/src/mage/cards/a/AjaniTheGreathearted.java @@ -18,7 +18,7 @@ import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledPlaneswalkerPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/a/AjaniUnyielding.java b/Mage.Sets/src/mage/cards/a/AjaniUnyielding.java index e4cf26fc5f09..50a5472824eb 100644 --- a/Mage.Sets/src/mage/cards/a/AjaniUnyielding.java +++ b/Mage.Sets/src/mage/cards/a/AjaniUnyielding.java @@ -19,7 +19,7 @@ import mage.filter.common.FilterPermanentCard; import mage.filter.common.FilterPlaneswalkerPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/a/AkiriFearlessVoyager.java b/Mage.Sets/src/mage/cards/a/AkiriFearlessVoyager.java index 84db60a03cd9..cf08f48cc2ce 100644 --- a/Mage.Sets/src/mage/cards/a/AkiriFearlessVoyager.java +++ b/Mage.Sets/src/mage/cards/a/AkiriFearlessVoyager.java @@ -16,16 +16,16 @@ import mage.filter.common.FilterEquipmentPermanent; import mage.filter.predicate.ObjectPlayer; import mage.filter.predicate.ObjectPlayerPredicate; -import mage.filter.predicate.permanent.EquippedPredicate; import mage.game.Game; +import mage.game.events.DefenderAttackedEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; import mage.target.targetpointer.FixedTarget; -import java.util.HashSet; -import java.util.Set; +import java.util.Collection; +import java.util.Objects; import java.util.UUID; /** @@ -63,14 +63,6 @@ public AkiriFearlessVoyager copy() { class AkiriFearlessVoyagerTriggeredAbility extends TriggeredAbilityImpl { - private static final FilterPermanent filter = new FilterPermanent(); - - static { - filter.add(EquippedPredicate.instance); - } - - private final Set attackedPlayerIds = new HashSet<>(); - AkiriFearlessVoyagerTriggeredAbility() { super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), false); } @@ -86,28 +78,21 @@ public AkiriFearlessVoyagerTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ATTACKER_DECLARED - || event.getType() == GameEvent.EventType.DECLARE_ATTACKERS_STEP_POST; + return event.getType() == GameEvent.EventType.DEFENDER_ATTACKED; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.DECLARE_ATTACKERS_STEP_POST) { - attackedPlayerIds.clear(); - return false; - } - if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED) { - Permanent creature = game.getPermanent(event.getSourceId()); - if (creature != null - && creature.isControlledBy(controllerId) - && filter.match(creature, game) - && game.getPlayer(event.getTargetId()) != null - && !attackedPlayerIds.contains(event.getTargetId())) { - attackedPlayerIds.add(event.getTargetId()); - return true; - } - } - return false; + return isControlledBy(event.getPlayerId()) + && game.getPlayer(event.getTargetId()) != null + && ((DefenderAttackedEvent) event) + .getAttackers(game) + .stream() + .map(Permanent::getAttachments) + .flatMap(Collection::stream) + .map(game::getPermanent) + .filter(Objects::nonNull) + .anyMatch(permanent -> permanent.hasSubtype(SubType.EQUIPMENT, game)); } @Override diff --git a/Mage.Sets/src/mage/cards/a/AkroanConscriptor.java b/Mage.Sets/src/mage/cards/a/AkroanConscriptor.java index cffa80c32153..0c960f0b56c4 100644 --- a/Mage.Sets/src/mage/cards/a/AkroanConscriptor.java +++ b/Mage.Sets/src/mage/cards/a/AkroanConscriptor.java @@ -16,7 +16,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/a/AlAbarasCarpet.java b/Mage.Sets/src/mage/cards/a/AlAbarasCarpet.java index 03ed6b52480d..2c49f2269473 100644 --- a/Mage.Sets/src/mage/cards/a/AlAbarasCarpet.java +++ b/Mage.Sets/src/mage/cards/a/AlAbarasCarpet.java @@ -74,7 +74,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { DamagePlayerEvent damageEvent = (DamagePlayerEvent) event; if (event.getTargetId().equals(source.getControllerId())) { Permanent permanent = game.getPermanentOrLKIBattlefield(damageEvent.getSourceId()); - if (permanent != null && filter.match(permanent, game)) { + if (filter.match(permanent, game)) { return true; } } diff --git a/Mage.Sets/src/mage/cards/a/AlhammarretsArchive.java b/Mage.Sets/src/mage/cards/a/AlhammarretsArchive.java index b1c0077a47d4..c0ccf5faa9e2 100644 --- a/Mage.Sets/src/mage/cards/a/AlhammarretsArchive.java +++ b/Mage.Sets/src/mage/cards/a/AlhammarretsArchive.java @@ -77,7 +77,7 @@ class AlhammarretsArchiveReplacementEffect extends ReplacementEffectImpl { AlhammarretsArchiveReplacementEffect() { super(Duration.WhileOnBattlefield, Outcome.Neutral); - staticText = "If you draw a card except the first one you draw in each of your draw steps, draw two cards instead"; + staticText = "If you would draw a card except the first one you draw in each of your draw steps, draw two cards instead"; } private AlhammarretsArchiveReplacementEffect(final AlhammarretsArchiveReplacementEffect effect) { diff --git a/Mage.Sets/src/mage/cards/a/AlharuSolemnRitualist.java b/Mage.Sets/src/mage/cards/a/AlharuSolemnRitualist.java index 3c28abda5d00..30ebc945e01e 100644 --- a/Mage.Sets/src/mage/cards/a/AlharuSolemnRitualist.java +++ b/Mage.Sets/src/mage/cards/a/AlharuSolemnRitualist.java @@ -17,7 +17,7 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.permanent.token.SpiritWhiteToken; import mage.target.TargetPermanent; diff --git a/Mage.Sets/src/mage/cards/a/AlibouAncientWitness.java b/Mage.Sets/src/mage/cards/a/AlibouAncientWitness.java new file mode 100644 index 000000000000..f118c1717d13 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AlibouAncientWitness.java @@ -0,0 +1,116 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AlibouAncientWitness extends CardImpl { + + public AlibouAncientWitness(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}{R}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.GOLEM); + this.power = new MageInt(4); + this.toughness = new MageInt(5); + + // Other artifact creatures you control have haste. + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + HasteAbility.getInstance(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENTS_ARTIFACT_CREATURE, true + ))); + + // Whenever one or more artifact creatures you control attack, Alibou, Ancient Witness deals X damage to any target and you scry X, where X is the number of tapped artifacts you control. + Ability ability = new AttacksWithCreaturesTriggeredAbility( + new AlibouAncientWitnessEffect(), 1, + StaticFilters.FILTER_PERMANENTS_ARTIFACT_CREATURE + ); + ability.addTarget(new TargetAnyTarget()); + this.addAbility(ability.addHint(AlibouAncientWitnessEffect.getHint())); + } + + private AlibouAncientWitness(final AlibouAncientWitness card) { + super(card); + } + + @Override + public AlibouAncientWitness copy() { + return new AlibouAncientWitness(this); + } +} + +class AlibouAncientWitnessEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterControlledArtifactPermanent(); + + static { + filter.add(TappedPredicate.instance); + } + + private static final Hint hint = new ValueHint( + "Tapped artifacts you control", new PermanentsOnBattlefieldCount(filter) + ); + + AlibouAncientWitnessEffect() { + super(Outcome.Benefit); + staticText = "{this} deals X damage to any target and you scry X, " + + "where X is the number of tapped artifacts you control"; + } + + private AlibouAncientWitnessEffect(final AlibouAncientWitnessEffect effect) { + super(effect); + } + + @Override + public AlibouAncientWitnessEffect copy() { + return new AlibouAncientWitnessEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int xValue = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game); + if (xValue < 1) { + return false; + } + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent != null) { + permanent.damage(xValue, source.getSourceId(), source, game); + } + Player player = game.getPlayer(source.getFirstTarget()); + if (player != null) { + player.damage(xValue, source.getSourceId(), source, game); + } + player = game.getPlayer(source.getControllerId()); + if (player != null) { + player.scry(xValue, source, game); + } + return true; + } + + public static Hint getHint() { + return hint; + } +} diff --git a/Mage.Sets/src/mage/cards/a/AllHallowsEve.java b/Mage.Sets/src/mage/cards/a/AllHallowsEve.java index e34ac95fd5dc..825b8ec75082 100644 --- a/Mage.Sets/src/mage/cards/a/AllHallowsEve.java +++ b/Mage.Sets/src/mage/cards/a/AllHallowsEve.java @@ -1,24 +1,25 @@ package mage.cards.a; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileSpellEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; +import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.TargetController; import mage.constants.Zone; import mage.counters.CounterType; -import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; +import java.util.Collection; +import java.util.Objects; import java.util.UUID; /** @@ -30,14 +31,13 @@ public AllHallowsEve(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}{B}"); // Exile All Hallow's Eve with two scream counters on it. - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); - Effect effect = new AddCountersSourceEffect(CounterType.SCREAM.createInstance(), StaticValue.get(2), true, true); - effect.setText("with 2 scream counters on it"); - this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addEffect(new ExileSpellEffect()); + this.getSpellAbility().addEffect(new AddCountersSourceEffect( + CounterType.SCREAM.createInstance(), StaticValue.get(2), true, true + ).setText("with 2 scream counters on it")); // At the beginning of your upkeep, if All Hallow's Eve is exiled with a scream counter on it, remove a scream counter from it. If there are no more scream counters on it, put it into your graveyard and each player returns all creature cards from their graveyard to the battlefield. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.EXILED, new AllHallowsEveEffect(), TargetController.YOU, false)); - + this.addAbility(new ConditionalInterveningIfTriggeredAbility(new BeginningOfUpkeepTriggeredAbility(Zone.EXILED, new AllHallowsEveEffect(), TargetController.YOU, false), AllHallowsEveCondition.instance, "At the beginning of your upkeep, if {this} is exiled with a scream counter on it, remove a scream counter from it. If there are no more scream counters on it, put it into your graveyard and each player returns all creature cards from their graveyard to the battlefield.")); } private AllHallowsEve(final AllHallowsEve card) { @@ -50,14 +50,26 @@ public AllHallowsEve copy() { } } +enum AllHallowsEveCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + MageObject sourceObject = source.getSourceObjectIfItStillExists(game); + return sourceObject != null + && game.getState().getZone(source.getSourceId()) == Zone.EXILED + && sourceObject instanceof Card + && ((Card) sourceObject).getMainCard().getCounters(game).getCount(CounterType.SCREAM) > 0; + } +} + class AllHallowsEveEffect extends OneShotEffect { - public AllHallowsEveEffect() { + AllHallowsEveEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "if {this} is exiled with a scream counter on it, remove a scream counter from it. If there are no more scream counters on it, put it into your graveyard and each player returns all creature cards from their graveyard to the battlefield"; } - public AllHallowsEveEffect(final AllHallowsEveEffect effect) { + private AllHallowsEveEffect(final AllHallowsEveEffect effect) { super(effect); } @@ -68,23 +80,24 @@ public AllHallowsEveEffect copy() { @Override public boolean apply(Game game, Ability source) { - Card allHallowsEve = game.getCard(source.getSourceId()); + Card card = (Card) source.getSourceObject(game); Player controller = game.getPlayer(source.getControllerId()); - if (allHallowsEve != null - && controller != null - && game.getExile().getCard(allHallowsEve.getId(), game) != null) { - allHallowsEve.removeCounters(CounterType.SCREAM.getName(), 1, source, game); - if (allHallowsEve.getCounters(game).getCount(CounterType.SCREAM) == 0) { - allHallowsEve.moveToZone(Zone.GRAVEYARD, source, game, false); - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - player.moveCards(player.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURE, game), Zone.BATTLEFIELD, source, game); - } - } - } - return true; + if (card == null || controller == null) { + return false; } - return false; + controller.moveCards(card, Zone.GRAVEYARD, source, game); + Cards cards = new CardsImpl(); + game.getState() + .getPlayersInRange(source.getControllerId(), game) + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .map(Player::getGraveyard) + .map(g -> g.getCards(game)) + .flatMap(Collection::stream) + .filter(MageObject::isCreature) + .forEach(cards::add); + controller.moveCards(card, Zone.BATTLEFIELD, source, game, false, false, true, null); + return true; } } diff --git a/Mage.Sets/src/mage/cards/a/AllSunsDawn.java b/Mage.Sets/src/mage/cards/a/AllSunsDawn.java index 2a882a21f784..d714cf51f8f9 100644 --- a/Mage.Sets/src/mage/cards/a/AllSunsDawn.java +++ b/Mage.Sets/src/mage/cards/a/AllSunsDawn.java @@ -48,7 +48,7 @@ public AllSunsDawn(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 1, filterBlack)); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 1, filterWhite)); // Exile All Suns' Dawn. - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private AllSunsDawn(final AllSunsDawn card) { diff --git a/Mage.Sets/src/mage/cards/a/AlpineHoundmaster.java b/Mage.Sets/src/mage/cards/a/AlpineHoundmaster.java index 431d697ef0bd..33100f83b552 100644 --- a/Mage.Sets/src/mage/cards/a/AlpineHoundmaster.java +++ b/Mage.Sets/src/mage/cards/a/AlpineHoundmaster.java @@ -17,7 +17,7 @@ import mage.filter.common.FilterAttackingCreature; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.NamePredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; @@ -69,7 +69,7 @@ class AlpineHoundmasterEffect extends OneShotEffect { AlpineHoundmasterEffect() { super(Outcome.Benefit); staticText = "search your library for a card named Alpine Watchdog and/or a card named Igneous Cur, " + - "reveal them, put them into your hand, then shuffle your library"; + "reveal them, put them into your hand, then shuffle"; } private AlpineHoundmasterEffect(final AlpineHoundmasterEffect effect) { diff --git a/Mage.Sets/src/mage/cards/a/AlrundGodOfTheCosmos.java b/Mage.Sets/src/mage/cards/a/AlrundGodOfTheCosmos.java index 4275d8574d36..4ea247f568c8 100644 --- a/Mage.Sets/src/mage/cards/a/AlrundGodOfTheCosmos.java +++ b/Mage.Sets/src/mage/cards/a/AlrundGodOfTheCosmos.java @@ -26,7 +26,7 @@ import mage.cards.CardsImpl; import mage.filter.FilterCard; import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.ExileZone; import mage.players.Player; import mage.util.CardUtil; diff --git a/Mage.Sets/src/mage/cards/a/AlrundsEpiphany.java b/Mage.Sets/src/mage/cards/a/AlrundsEpiphany.java index 8e582f23c7cf..9a3b9216d561 100644 --- a/Mage.Sets/src/mage/cards/a/AlrundsEpiphany.java +++ b/Mage.Sets/src/mage/cards/a/AlrundsEpiphany.java @@ -22,7 +22,7 @@ public AlrundsEpiphany(UUID ownerId, CardSetInfo setInfo) { // Create two 1/1 blue Bird creature tokens with flying. Take an extra turn after this one. Exile Alrund's Epiphany. this.getSpellAbility().addEffect(new CreateTokenEffect(new OwlToken(), 2)); this.getSpellAbility().addEffect(new AddExtraTurnControllerEffect()); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); // Foretell {4}{U}{U} this.addAbility(new ForetellAbility(this, "{4}{U}{U}")); diff --git a/Mage.Sets/src/mage/cards/a/AltarOfTheBrood.java b/Mage.Sets/src/mage/cards/a/AltarOfTheBrood.java index 273411f3d60b..5353f2b626a6 100644 --- a/Mage.Sets/src/mage/cards/a/AltarOfTheBrood.java +++ b/Mage.Sets/src/mage/cards/a/AltarOfTheBrood.java @@ -9,7 +9,7 @@ import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/a/AlteredEgo.java b/Mage.Sets/src/mage/cards/a/AlteredEgo.java index 29fcc368edf8..1573b57df86f 100644 --- a/Mage.Sets/src/mage/cards/a/AlteredEgo.java +++ b/Mage.Sets/src/mage/cards/a/AlteredEgo.java @@ -1,32 +1,30 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.common.EntersBattlefieldAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.CopyPermanentEffect; import mage.abilities.effects.common.EntersBattlefieldWithXCountersEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.counters.Counter; import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.util.functions.CopyApplier; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class AlteredEgo extends CardImpl { public AlteredEgo(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{X}{2}{G}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{X}{2}{G}{U}"); this.subtype.add(SubType.SHAPESHIFTER); this.power = new MageInt(0); this.toughness = new MageInt(0); @@ -35,13 +33,10 @@ public AlteredEgo(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new CantBeCounteredSourceAbility()); // You may have Altered Ego enter the battlefield as a copy of any creature on the battlefield, except it enters with an additional X +1/+1 counters on it. - Effect effect = new CopyPermanentEffect(StaticFilters.FILTER_PERMANENT_CREATURE, null); - effect.setText("a copy of any creature on the battlefield"); - EntersBattlefieldAbility ability = new EntersBattlefieldAbility(effect, true); - effect = new AlteredEgoAddCountersEffect(CounterType.P1P1.createInstance()); - effect.setText(", except it enters with an additional X +1/+1 counters on it"); - ability.addEffect(effect); - this.addAbility(ability); + this.addAbility(new EntersBattlefieldAbility( + new CopyPermanentEffect(StaticFilters.FILTER_PERMANENT_CREATURE, new AlteredEgoCopyApplier()), + true + )); } private AlteredEgo(final AlteredEgo card) { @@ -54,31 +49,29 @@ public AlteredEgo copy() { } } -class AlteredEgoAddCountersEffect extends EntersBattlefieldWithXCountersEffect { - - public AlteredEgoAddCountersEffect(Counter counter) { - super(counter); - } +class AlteredEgoCopyApplier extends CopyApplier { - public AlteredEgoAddCountersEffect(EntersBattlefieldWithXCountersEffect effect) { - super(effect); + @Override + public String getText() { + return ", except it enters with an additional X +1/+1 counters on it"; } @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentEntering(source.getSourceId()); - if (permanent != null) { - // except only takes place if something was copied - if (permanent.isCopy()) { - return super.apply(game, source); - } + public boolean apply(Game game, MageObject blueprint, Ability source, UUID copyToObjectId) { + // counters only for original card, not copies, see rules: + // 706.9e + // Some replacement effects that generate copy effects include an exception that’s an additional + // effect rather than a modification of the affected object’s characteristics. If another copy + // effect is applied to that object after applying the copy effect with that exception, the + // exception’s effect doesn’t happen. + + if (!isCopyOfCopy(source, blueprint, copyToObjectId)) { + // except it enters with an additional X +1/+1 counters on it + blueprint.getAbilities().add( + new EntersBattlefieldAbility(new EntersBattlefieldWithXCountersEffect(CounterType.P1P1.createInstance())) + ); } - return false; - } - @Override - public EntersBattlefieldWithXCountersEffect copy() { - return new AlteredEgoAddCountersEffect(this); + return true; } - -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/a/Aluren.java b/Mage.Sets/src/mage/cards/a/Aluren.java index c97a71d0afeb..7256af5a5ff0 100644 --- a/Mage.Sets/src/mage/cards/a/Aluren.java +++ b/Mage.Sets/src/mage/cards/a/Aluren.java @@ -11,7 +11,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; @@ -32,10 +32,10 @@ */ public final class Aluren extends CardImpl { - private static final FilterCreatureCard filter = new FilterCreatureCard("creature cards with converted mana cost 3 or less"); + private static final FilterCreatureCard filter = new FilterCreatureCard("creature cards with mana value 3 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public Aluren(UUID ownerId, CardSetInfo setInfo) { @@ -64,17 +64,17 @@ public Aluren copy() { class AlurenRuleEffect extends ContinuousEffectImpl { - private static final FilterCreatureCard filter = new FilterCreatureCard("creature cards with converted mana cost 3 or less"); + private static final FilterCreatureCard filter = new FilterCreatureCard("creature cards with mana value 3 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } private final AlternativeCostSourceAbility alternativeCastingCostAbility = new AlternativeCostSourceAbility(null, SourceIsSpellCondition.instance, null, filter, true); public AlurenRuleEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); - staticText = "Any player may cast creature cards with converted mana cost 3 or less without paying their mana cost"; + staticText = "Any player may cast creature cards with mana value 3 or less without paying their mana cost"; } public AlurenRuleEffect(final AlurenRuleEffect effect) { diff --git a/Mage.Sets/src/mage/cards/a/AmaranthineWall.java b/Mage.Sets/src/mage/cards/a/AmaranthineWall.java index fa17ba5d92e7..361ee75b4491 100644 --- a/Mage.Sets/src/mage/cards/a/AmaranthineWall.java +++ b/Mage.Sets/src/mage/cards/a/AmaranthineWall.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.cards.a; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/a/AmarethTheLustrous.java b/Mage.Sets/src/mage/cards/a/AmarethTheLustrous.java index e624fd885159..f495459367cf 100644 --- a/Mage.Sets/src/mage/cards/a/AmarethTheLustrous.java +++ b/Mage.Sets/src/mage/cards/a/AmarethTheLustrous.java @@ -11,7 +11,7 @@ import mage.cards.CardsImpl; import mage.constants.*; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/a/AmbushKrotiq.java b/Mage.Sets/src/mage/cards/a/AmbushKrotiq.java index c9c462ba189d..19085fbbe140 100644 --- a/Mage.Sets/src/mage/cards/a/AmbushKrotiq.java +++ b/Mage.Sets/src/mage/cards/a/AmbushKrotiq.java @@ -11,7 +11,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/a/AminatouTheFateshifter.java b/Mage.Sets/src/mage/cards/a/AminatouTheFateshifter.java index 1d32032fd9d5..54c6abcf7002 100644 --- a/Mage.Sets/src/mage/cards/a/AminatouTheFateshifter.java +++ b/Mage.Sets/src/mage/cards/a/AminatouTheFateshifter.java @@ -17,7 +17,7 @@ import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterNonlandPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/a/AmrouScout.java b/Mage.Sets/src/mage/cards/a/AmrouScout.java index 49b1c178094b..1d6280703bb1 100644 --- a/Mage.Sets/src/mage/cards/a/AmrouScout.java +++ b/Mage.Sets/src/mage/cards/a/AmrouScout.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; /** @@ -23,10 +23,10 @@ */ public final class AmrouScout extends CardImpl { - private static final FilterPermanentCard filter = new FilterPermanentCard("Rebel permanent card with converted mana cost 3 or less"); + private static final FilterPermanentCard filter = new FilterPermanentCard("Rebel permanent card with mana value 3 or less"); static { filter.add(SubType.REBEL.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public AmrouScout(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/a/AnabaAncestor.java b/Mage.Sets/src/mage/cards/a/AnabaAncestor.java index 27f02bd72561..0612ecaced59 100644 --- a/Mage.Sets/src/mage/cards/a/AnabaAncestor.java +++ b/Mage.Sets/src/mage/cards/a/AnabaAncestor.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/a/AnafenzaKinTreeSpirit.java b/Mage.Sets/src/mage/cards/a/AnafenzaKinTreeSpirit.java index 406e8bf655ab..7e081ac81fe7 100644 --- a/Mage.Sets/src/mage/cards/a/AnafenzaKinTreeSpirit.java +++ b/Mage.Sets/src/mage/cards/a/AnafenzaKinTreeSpirit.java @@ -13,7 +13,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; /** diff --git a/Mage.Sets/src/mage/cards/a/AnafenzaTheForemost.java b/Mage.Sets/src/mage/cards/a/AnafenzaTheForemost.java index 0dcae5be7026..84ae8cc0f94b 100644 --- a/Mage.Sets/src/mage/cards/a/AnafenzaTheForemost.java +++ b/Mage.Sets/src/mage/cards/a/AnafenzaTheForemost.java @@ -13,7 +13,7 @@ import mage.constants.*; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.events.GameEvent; diff --git a/Mage.Sets/src/mage/cards/a/AnaraWolvidFamiliar.java b/Mage.Sets/src/mage/cards/a/AnaraWolvidFamiliar.java index 51b00062ab00..5f54c803ffbb 100644 --- a/Mage.Sets/src/mage/cards/a/AnaraWolvidFamiliar.java +++ b/Mage.Sets/src/mage/cards/a/AnaraWolvidFamiliar.java @@ -15,7 +15,7 @@ import mage.constants.SuperType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.CommanderPredicate; +import mage.filter.predicate.mageobject.CommanderPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/a/AncestralMask.java b/Mage.Sets/src/mage/cards/a/AncestralMask.java index 471e88e844d8..bbeea358e107 100644 --- a/Mage.Sets/src/mage/cards/a/AncestralMask.java +++ b/Mage.Sets/src/mage/cards/a/AncestralMask.java @@ -11,7 +11,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterEnchantmentPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/a/AncientCraving.java b/Mage.Sets/src/mage/cards/a/AncientCraving.java index cb26817c319e..f59303ffb5ef 100644 --- a/Mage.Sets/src/mage/cards/a/AncientCraving.java +++ b/Mage.Sets/src/mage/cards/a/AncientCraving.java @@ -18,8 +18,8 @@ public AncientCraving(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{B}"); // You draw three cards and you lose 3 life. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(3)); - this.getSpellAbility().addEffect(new LoseLifeSourceControllerEffect(3)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(3).setText("you draw three cards")); + this.getSpellAbility().addEffect(new LoseLifeSourceControllerEffect(3).concatBy("and")); } private AncientCraving(final AncientCraving card) { diff --git a/Mage.Sets/src/mage/cards/a/AncientOoze.java b/Mage.Sets/src/mage/cards/a/AncientOoze.java index cf37db0ab72a..9e720aac5d63 100644 --- a/Mage.Sets/src/mage/cards/a/AncientOoze.java +++ b/Mage.Sets/src/mage/cards/a/AncientOoze.java @@ -33,7 +33,7 @@ public AncientOoze(UUID ownerId, CardSetInfo setInfo) { // Ancient Ooze's power and toughness are each equal to the total converted mana cost of other creatures you control. this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerToughnessSourceEffect(new AncientOozePowerToughnessValue(), Duration.EndOfGame) - .setText("{this}'s power and toughness are each equal to the total converted mana cost of other creatures you control.") + .setText("{this}'s power and toughness are each equal to the total mana value of other creatures you control.") )); } @@ -54,7 +54,7 @@ public int calculate(Game game, Ability sourceAbility, Effect effect) { int value = 0; for (Permanent creature : game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), sourceAbility.getControllerId(), game)) { if (creature != null && !sourceAbility.getSourceId().equals(creature.getId())) { - value += creature.getConvertedManaCost(); + value += creature.getManaValue(); } } return value; @@ -72,6 +72,6 @@ public String toString() { @Override public String getMessage() { - return "total converted mana cost of other creatures you control"; + return "total mana value of other creatures you control"; } } diff --git a/Mage.Sets/src/mage/cards/a/AngelOfCondemnation.java b/Mage.Sets/src/mage/cards/a/AngelOfCondemnation.java index 67721b9e874f..d1740013eb48 100644 --- a/Mage.Sets/src/mage/cards/a/AngelOfCondemnation.java +++ b/Mage.Sets/src/mage/cards/a/AngelOfCondemnation.java @@ -22,7 +22,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/a/AngelOfDeliverance.java b/Mage.Sets/src/mage/cards/a/AngelOfDeliverance.java index eaffb945ed69..db59364eb9ad 100644 --- a/Mage.Sets/src/mage/cards/a/AngelOfDeliverance.java +++ b/Mage.Sets/src/mage/cards/a/AngelOfDeliverance.java @@ -83,8 +83,7 @@ public AngelOfDeliveranceDealsDamageTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || event.getType() == GameEvent.EventType.DAMAGED_CREATURE - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + || event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/a/AngelOfSerenity.java b/Mage.Sets/src/mage/cards/a/AngelOfSerenity.java index 656f869934fb..9c03356a1673 100644 --- a/Mage.Sets/src/mage/cards/a/AngelOfSerenity.java +++ b/Mage.Sets/src/mage/cards/a/AngelOfSerenity.java @@ -15,7 +15,7 @@ import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCardInGraveyardOrBattlefield; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/a/AngelOfTheRuins.java b/Mage.Sets/src/mage/cards/a/AngelOfTheRuins.java new file mode 100644 index 000000000000..36d54c69027c --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AngelOfTheRuins.java @@ -0,0 +1,55 @@ +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.ExileTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.PlainscyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AngelOfTheRuins extends CardImpl { + + private static final FilterPermanent filter + = new FilterArtifactOrEnchantmentPermanent("artifacts and/or enchantments"); + + public AngelOfTheRuins(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}{W}{W}"); + + this.subtype.add(SubType.ANGEL); + this.power = new MageInt(5); + this.toughness = new MageInt(7); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Angel of the Ruins enters the battlefield, exile up to two target artifacts and/or enchantments. + Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetEffect()); + ability.addTarget(new TargetPermanent(0, 2, filter, false)); + this.addAbility(ability); + + // Plainscycling {2} + this.addAbility(new PlainscyclingAbility(new ManaCostsImpl<>("{2}"))); + } + + private AngelOfTheRuins(final AngelOfTheRuins card) { + super(card); + } + + @Override + public AngelOfTheRuins copy() { + return new AngelOfTheRuins(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AngelOfVitality.java b/Mage.Sets/src/mage/cards/a/AngelOfVitality.java index f18ef3fa56bc..9692190bcab3 100644 --- a/Mage.Sets/src/mage/cards/a/AngelOfVitality.java +++ b/Mage.Sets/src/mage/cards/a/AngelOfVitality.java @@ -89,7 +89,7 @@ public boolean checksEventType(GameEvent event, Game game) { @Override public boolean applies(GameEvent event, Ability source, Game game) { - return event.getPlayerId().equals(source.getControllerId()) && (source.getControllerId() != null); + return source.isControlledBy(event.getPlayerId()); } } diff --git a/Mage.Sets/src/mage/cards/a/AngelicAccord.java b/Mage.Sets/src/mage/cards/a/AngelicAccord.java index dbdb8a8a6129..64c40fc36dcb 100644 --- a/Mage.Sets/src/mage/cards/a/AngelicAccord.java +++ b/Mage.Sets/src/mage/cards/a/AngelicAccord.java @@ -2,6 +2,7 @@ import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.condition.common.YouGainedLifeCondition; +import mage.abilities.dynamicvalue.common.ControllerGotLifeCount; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -23,9 +24,10 @@ public AngelicAccord(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}"); // At the beginning of each end step, if you gained 4 or more life this turn, create a 4/4 white Angel creature token with flying. - this.addAbility(new BeginningOfEndStepTriggeredAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new AngelToken()), TargetController.ANY, - new YouGainedLifeCondition(ComparisonType.MORE_THAN, 3), false), - new PlayerGainedLifeWatcher()); + this.addAbility(new BeginningOfEndStepTriggeredAbility( + Zone.BATTLEFIELD, new CreateTokenEffect(new AngelToken()), TargetController.ANY, + new YouGainedLifeCondition(ComparisonType.MORE_THAN, 3), false + ).addHint(ControllerGotLifeCount.getHint()), new PlayerGainedLifeWatcher()); } private AngelicAccord(final AngelicAccord card) { diff --git a/Mage.Sets/src/mage/cards/a/AngelicCaptain.java b/Mage.Sets/src/mage/cards/a/AngelicCaptain.java index 1d1b2df90b46..d9f8f3f4e398 100644 --- a/Mage.Sets/src/mage/cards/a/AngelicCaptain.java +++ b/Mage.Sets/src/mage/cards/a/AngelicCaptain.java @@ -13,7 +13,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.common.FilterAttackingCreature; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/a/AngelsHerald.java b/Mage.Sets/src/mage/cards/a/AngelsHerald.java index 17afd863669c..9947d5e8b4c9 100644 --- a/Mage.Sets/src/mage/cards/a/AngelsHerald.java +++ b/Mage.Sets/src/mage/cards/a/AngelsHerald.java @@ -1,9 +1,6 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeTargetCost; @@ -14,13 +11,12 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.mageobject.ColorPredicate; import mage.filter.predicate.mageobject.NamePredicate; import mage.target.common.TargetCardInLibrary; -import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetControlledCreatureEachColor; + +import java.util.UUID; /** * @author nantuko @@ -28,19 +24,13 @@ public final class AngelsHerald extends CardImpl { private static final FilterCard filter = new FilterCard("card named Empyrial Archangel"); - private static final FilterControlledCreaturePermanent filterGreen = new FilterControlledCreaturePermanent("a green creature"); - private static final FilterControlledCreaturePermanent filterWhite = new FilterControlledCreaturePermanent("a white creature"); - private static final FilterControlledCreaturePermanent filterBlue = new FilterControlledCreaturePermanent("a blue creature"); static { filter.add(new NamePredicate("Empyrial Archangel")); - filterGreen.add(new ColorPredicate(ObjectColor.GREEN)); - filterWhite.add(new ColorPredicate(ObjectColor.WHITE)); - filterBlue.add(new ColorPredicate(ObjectColor.BLUE)); } public AngelsHerald(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.CLERIC); @@ -49,13 +39,11 @@ public AngelsHerald(UUID ownerId, CardSetInfo setInfo) { // {2}{W}, {tap}, Sacrifice a green creature, a white creature, and a blue creature: // Search your library for a card named Empyrial Archangel and put it onto the battlefield. Then shuffle your library. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(1, 1, new FilterCard(filter))), - new ManaCostsImpl("{2}{W}")); + Ability ability = new SimpleActivatedAbility(new SearchLibraryPutInPlayEffect( + new TargetCardInLibrary(filter)), new ManaCostsImpl<>("{2}{W}") + ); ability.addCost(new TapSourceCost()); - ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filterGreen, false))); - ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filterWhite, false))); - ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filterBlue, false))); + ability.addCost(new SacrificeTargetCost(new TargetControlledCreatureEachColor("GWU"))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/AngrathTheFlameChained.java b/Mage.Sets/src/mage/cards/a/AngrathTheFlameChained.java index fd35c3a5410b..294fdd7e7aa5 100644 --- a/Mage.Sets/src/mage/cards/a/AngrathTheFlameChained.java +++ b/Mage.Sets/src/mage/cards/a/AngrathTheFlameChained.java @@ -103,7 +103,7 @@ class AngrathTheFlameCreateDelayedTriggerEffect extends OneShotEffect { public AngrathTheFlameCreateDelayedTriggerEffect() { super(Outcome.Sacrifice); - staticText = "Sacrifice it at the beginning of the next end step if it has converted mana cost 3 or less"; + staticText = "Sacrifice it at the beginning of the next end step if it has mana value 3 or less"; } public AngrathTheFlameCreateDelayedTriggerEffect(final AngrathTheFlameCreateDelayedTriggerEffect effect) { @@ -149,7 +149,7 @@ public boolean checkTrigger(GameEvent event, Game game) { Permanent permanent = game.getPermanent(((FixedTarget) getEffects().get(0).getTargetPointer()).getTarget()); return permanent != null && permanent.getZoneChangeCounter(game) == ((FixedTarget) getEffects().get(0).getTargetPointer()).getZoneChangeCounter() - && permanent.getConvertedManaCost() <= 3; + && permanent.getManaValue() <= 3; } @Override @@ -159,6 +159,6 @@ public AngrathTheFlameChainedDelayedTriggeredAbility copy() { @Override public String getRule() { - return "Sacrifice it at the beginning of the next end step if it has converted mana cost 3 or less."; + return "Sacrifice it at the beginning of the next end step if it has mana value 3 or less."; } } diff --git a/Mage.Sets/src/mage/cards/a/AngrathsFury.java b/Mage.Sets/src/mage/cards/a/AngrathsFury.java index 4d40f188b60d..3e43be283792 100644 --- a/Mage.Sets/src/mage/cards/a/AngrathsFury.java +++ b/Mage.Sets/src/mage/cards/a/AngrathsFury.java @@ -40,7 +40,7 @@ public AngrathsFury(UUID ownerId, CardSetInfo setInfo) { // You may search your library and/or graveyard for a card named Angrath, Minotaur Pirate, reveal it, and put it into your hand. If you search your library this way, shuffle it. this.getSpellAbility().addEffect(new SearchLibraryGraveyardPutInHandEffect(filter) - .setText("You may search your library and/or graveyard for a card named Angrath, Minotaur Pirate, reveal it, and put it into your hand. If you search your library this way, shuffle it")); + .setText("You may search your library and/or graveyard for a card named Angrath, Minotaur Pirate, reveal it, and put it into your hand. If you search your library this way, shuffle")); } diff --git a/Mage.Sets/src/mage/cards/a/AngrathsMarauders.java b/Mage.Sets/src/mage/cards/a/AngrathsMarauders.java index 7886fec8fb25..31dc9eb06c22 100644 --- a/Mage.Sets/src/mage/cards/a/AngrathsMarauders.java +++ b/Mage.Sets/src/mage/cards/a/AngrathsMarauders.java @@ -64,8 +64,7 @@ public AngrathsMaraudersEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType().equals(EventType.DAMAGE_PLAYER) - || event.getType().equals(EventType.DAMAGE_CREATURE) - || event.getType().equals(EventType.DAMAGE_PLANESWALKER); + || event.getType().equals(EventType.DAMAGE_PERMANENT); } @Override diff --git a/Mage.Sets/src/mage/cards/a/AnimateArtifact.java b/Mage.Sets/src/mage/cards/a/AnimateArtifact.java index 35a13f4b8172..4a93d6f179b0 100644 --- a/Mage.Sets/src/mage/cards/a/AnimateArtifact.java +++ b/Mage.Sets/src/mage/cards/a/AnimateArtifact.java @@ -59,7 +59,7 @@ class AnimateArtifactContinuousEffect extends ContinuousEffectImpl { public AnimateArtifactContinuousEffect(Duration duration) { super(duration, Outcome.Benefit); - staticText = "As long as enchanted artifact isn't a creature, it's an artifact creature with power and toughness each equal to its converted mana cost"; + staticText = "As long as enchanted artifact isn't a creature, it's an artifact creature with power and toughness each equal to its mana value"; } public AnimateArtifactContinuousEffect(final AnimateArtifactContinuousEffect effect) { @@ -80,8 +80,8 @@ public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) if (permanent != null && !permanent.isCreature()) { if (sublayer == SubLayer.NA) { permanent.addCardType(CardType.CREATURE); - permanent.getPower().setValue(permanent.getConvertedManaCost()); - permanent.getToughness().setValue(permanent.getConvertedManaCost()); + permanent.getPower().setValue(permanent.getManaValue()); + permanent.getToughness().setValue(permanent.getManaValue()); } } return true; diff --git a/Mage.Sets/src/mage/cards/a/AnthemOfRakdos.java b/Mage.Sets/src/mage/cards/a/AnthemOfRakdos.java index 432cce25788f..5dfc1256330b 100644 --- a/Mage.Sets/src/mage/cards/a/AnthemOfRakdos.java +++ b/Mage.Sets/src/mage/cards/a/AnthemOfRakdos.java @@ -69,9 +69,8 @@ public AnthemOfRakdosHellbentEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE - || event.getType() == GameEvent.EventType.DAMAGE_PLAYER - || event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER; + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT + || event.getType() == GameEvent.EventType.DAMAGE_PLAYER; } @Override diff --git a/Mage.Sets/src/mage/cards/a/ApexOfPower.java b/Mage.Sets/src/mage/cards/a/ApexOfPower.java index f55edc55ee22..854213f17ae0 100644 --- a/Mage.Sets/src/mage/cards/a/ApexOfPower.java +++ b/Mage.Sets/src/mage/cards/a/ApexOfPower.java @@ -33,7 +33,7 @@ public ApexOfPower(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new ApexOfPowerSpellEffect()); // If this spell was cast from your hand, add ten mana of any one color. - this.getSpellAbility().addEffect(new ApexOfPowerManaEffect()); + this.getSpellAbility().addEffect(new ApexOfPowerManaEffect().concatBy("
")); } private ApexOfPower(final ApexOfPower card) { @@ -50,7 +50,8 @@ class ApexOfPowerSpellEffect extends OneShotEffect { public ApexOfPowerSpellEffect() { super(Outcome.Benefit); - this.staticText = "exile the top seven cards of your library. Until end of turn, you may cast nonland cards exiled this way"; + this.staticText = "exile the top seven cards of your library. " + + "Until end of turn, you may cast spells from among them"; } public ApexOfPowerSpellEffect(final ApexOfPowerSpellEffect effect) { diff --git a/Mage.Sets/src/mage/cards/a/ApocalypseChime.java b/Mage.Sets/src/mage/cards/a/ApocalypseChime.java index fdbc0a04871c..1493ab752805 100644 --- a/Mage.Sets/src/mage/cards/a/ApocalypseChime.java +++ b/Mage.Sets/src/mage/cards/a/ApocalypseChime.java @@ -14,7 +14,7 @@ import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.other.ExpansionSetPredicate; +import mage.filter.predicate.card.ExpansionSetPredicate; import mage.filter.predicate.permanent.TokenPredicate; /** diff --git a/Mage.Sets/src/mage/cards/a/ApocalypseDemon.java b/Mage.Sets/src/mage/cards/a/ApocalypseDemon.java index 2c3192e18f3c..9d63be36d5c4 100644 --- a/Mage.Sets/src/mage/cards/a/ApocalypseDemon.java +++ b/Mage.Sets/src/mage/cards/a/ApocalypseDemon.java @@ -13,7 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledPermanent; /** diff --git a/Mage.Sets/src/mage/cards/a/ApothecaryGeist.java b/Mage.Sets/src/mage/cards/a/ApothecaryGeist.java index 92495d54208b..7d09799e70f4 100644 --- a/Mage.Sets/src/mage/cards/a/ApothecaryGeist.java +++ b/Mage.Sets/src/mage/cards/a/ApothecaryGeist.java @@ -13,7 +13,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/a/AppetiteForBrains.java b/Mage.Sets/src/mage/cards/a/AppetiteForBrains.java index 8e19b550ab86..a2f8d8964ef8 100644 --- a/Mage.Sets/src/mage/cards/a/AppetiteForBrains.java +++ b/Mage.Sets/src/mage/cards/a/AppetiteForBrains.java @@ -8,7 +8,7 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetOpponent; /** @@ -17,10 +17,10 @@ */ public final class AppetiteForBrains extends CardImpl { - private static final FilterCard filter = new FilterCard("a card from it with converted mana cost 4 or greater"); + private static final FilterCard filter = new FilterCard("a card from it with mana value 4 or greater"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 3)); + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 3)); } public AppetiteForBrains(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/a/ApproachOfTheSecondSun.java b/Mage.Sets/src/mage/cards/a/ApproachOfTheSecondSun.java index 6c19eadc8e99..6daf1c6c1fde 100644 --- a/Mage.Sets/src/mage/cards/a/ApproachOfTheSecondSun.java +++ b/Mage.Sets/src/mage/cards/a/ApproachOfTheSecondSun.java @@ -64,12 +64,11 @@ public ApproachOfTheSecondSunEffect copy() { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Spell spell = game.getStack().getSpell(source.getSourceId()); + Spell spell = game.getStack().getSpell(source.getSourceId(), false); if (controller != null && spell != null) { ApproachOfTheSecondSunWatcher watcher = game.getState().getWatcher(ApproachOfTheSecondSunWatcher.class); if (watcher != null - && !spell.isCopy() && watcher.getApproachesCast(controller.getId()) > 1 && spell.getFromZone() == Zone.HAND) { // Win the game @@ -79,10 +78,7 @@ public boolean apply(Game game, Ability source) { controller.gainLife(7, game, source); // Put this into the library as the 7th from the top - if (spell.isCopy()) { - return true; - } - Card spellCard = game.getStack().getSpell(source.getSourceId()).getCard(); + Card spellCard = game.getStack().getSpell(source.getSourceId(), false).getCard(); if (spellCard != null) { controller.putCardOnTopXOfLibrary(spellCard, game, source, 7, true); } diff --git a/Mage.Sets/src/mage/cards/a/AquitectsWill.java b/Mage.Sets/src/mage/cards/a/AquitectsWill.java index a8e854002cb1..2076f905abad 100644 --- a/Mage.Sets/src/mage/cards/a/AquitectsWill.java +++ b/Mage.Sets/src/mage/cards/a/AquitectsWill.java @@ -1,12 +1,15 @@ - package mage.cards.a; import mage.abilities.Ability; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.abilities.effects.common.continuous.BecomesBasicLandTargetEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.mana.BlueManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -16,22 +19,21 @@ import mage.game.permanent.Permanent; import mage.target.common.TargetLandPermanent; +import java.util.List; +import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; /** - * * @author ilcartographer */ public final class AquitectsWill extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("Merfolk"); - - static { - filter.add(SubType.MERFOLK.getPredicate()); - } + private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.MERFOLK); + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); public AquitectsWill(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.TRIBAL,CardType.SORCERY},"{U}"); + super(ownerId, setInfo, new CardType[]{CardType.TRIBAL, CardType.SORCERY}, "{U}"); this.subtype.add(SubType.MERFOLK); // Put a flood counter on target land. @@ -39,13 +41,13 @@ public AquitectsWill(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addTarget(new TargetLandPermanent()); // That land is an Island in addition to its other types for as long as it has a flood counter on it. - this.getSpellAbility().addEffect(new AquitectsWillEffect(Duration.Custom, false, false, SubType.ISLAND)); + this.getSpellAbility().addEffect(new AquitectsWillEffect()); // If you control a Merfolk, draw a card. this.getSpellAbility().addEffect(new ConditionalOneShotEffect( - new DrawCardSourceControllerEffect(1), - new PermanentsOnTheBattlefieldCondition(filter), - "If you control a Merfolk, draw a card")); + new DrawCardSourceControllerEffect(1), condition, + "If you control a Merfolk, draw a card" + )); } private AquitectsWill(final AquitectsWill card) { @@ -58,32 +60,44 @@ public AquitectsWill copy() { } } -class AquitectsWillEffect extends BecomesBasicLandTargetEffect { +class AquitectsWillEffect extends ContinuousEffectImpl { - public AquitectsWillEffect(Duration duration, boolean chooseLandType, boolean loseType, SubType... landNames) { - super(duration, chooseLandType, loseType, landNames); + AquitectsWillEffect() { + super(Duration.EndOfGame, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit); staticText = "That land is an Island in addition to its other types for as long as it has a flood counter on it"; } - public AquitectsWillEffect(final AquitectsWillEffect effect) { + private AquitectsWillEffect(final AquitectsWillEffect effect) { super(effect); } @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + public AquitectsWillEffect copy() { + return new AquitectsWillEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { Permanent land = game.getPermanent(this.targetPointer.getFirst(game, source)); - if (land == null) { - // if permanent left battlefield the effect can be removed because it was only valid for that object - this.discard(); - } else if (land.getCounters(game).getCount(CounterType.FLOOD) > 0) { - // only if Flood counter is on the object it becomes an Island.(it would be possible to remove and return the counters with e.g. Fate Transfer if the land becomes a creature too) - super.apply(layer, sublayer, source, game); + if (land == null + || land.getCounters(game).getCount(CounterType.FLOOD) < 1) { + discard(); + return false; + } + // The land is an island intrinsically so the ability is added at layer 4, not layer 6 + land.addSubType(game, SubType.ISLAND); + if (!land.getAbilities(game).containsClass(BlueManaAbility.class)) { + land.addAbility(new BlueManaAbility(), source.getSourceId(), game); } return true; } @Override - public AquitectsWillEffect copy() { - return new AquitectsWillEffect(this); + public Set isDependentTo(List allEffectsInLayer) { + return allEffectsInLayer + .stream() + .filter(effect -> effect.getDependencyTypes().contains(DependencyType.BecomeIsland)) + .map(Effect::getId) + .collect(Collectors.toSet()); } } diff --git a/Mage.Sets/src/mage/cards/a/ArachnusSpinner.java b/Mage.Sets/src/mage/cards/a/ArachnusSpinner.java index 5eb67c64b100..73798802e1cb 100644 --- a/Mage.Sets/src/mage/cards/a/ArachnusSpinner.java +++ b/Mage.Sets/src/mage/cards/a/ArachnusSpinner.java @@ -74,7 +74,7 @@ public ArachnusSpinnerEffect() { super(Outcome.PutCardInPlay); this.staticText = "Search your graveyard and/or library for a card named Arachnus Web " + "and put it onto the battlefield attached to target creature. " - + "If you search your library this way, shuffle it"; + + "If you search your library this way, shuffle"; } public ArachnusSpinnerEffect(final ArachnusSpinnerEffect effect) { diff --git a/Mage.Sets/src/mage/cards/a/ArahboRoarOfTheWorld.java b/Mage.Sets/src/mage/cards/a/ArahboRoarOfTheWorld.java index d290d67c6597..d6da19886023 100644 --- a/Mage.Sets/src/mage/cards/a/ArahboRoarOfTheWorld.java +++ b/Mage.Sets/src/mage/cards/a/ArahboRoarOfTheWorld.java @@ -19,7 +19,7 @@ import mage.constants.*; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/a/ArashinForemost.java b/Mage.Sets/src/mage/cards/a/ArashinForemost.java index 374edd8b91a2..943af6b45563 100644 --- a/Mage.Sets/src/mage/cards/a/ArashinForemost.java +++ b/Mage.Sets/src/mage/cards/a/ArashinForemost.java @@ -13,7 +13,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/a/ArashinWarBeast.java b/Mage.Sets/src/mage/cards/a/ArashinWarBeast.java index 0c7f52fbb49c..5d41985e6ee8 100644 --- a/Mage.Sets/src/mage/cards/a/ArashinWarBeast.java +++ b/Mage.Sets/src/mage/cards/a/ArashinWarBeast.java @@ -14,9 +14,8 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.BlockingPredicate; import mage.game.Game; -import mage.game.events.DamagedCreatureEvent; +import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; /** @@ -73,14 +72,14 @@ public ArashinWarBeastTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE || event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_POST ; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT || event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_POST ; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.DAMAGED_CREATURE && + if (event.getType() == GameEvent.EventType.DAMAGED_PERMANENT && event.getSourceId().equals(this.sourceId) && - ((DamagedCreatureEvent) event).isCombatDamage() && + ((DamagedEvent) event).isCombatDamage() && !usedForCombatDamageStep) { Permanent creature = game.getPermanentOrLKIBattlefield(event.getTargetId()); if (creature == null || !filter.match(creature, getSourceId(), getControllerId(), game)) { diff --git a/Mage.Sets/src/mage/cards/a/ArcBlade.java b/Mage.Sets/src/mage/cards/a/ArcBlade.java index c39b9abd343a..9944aea415d8 100644 --- a/Mage.Sets/src/mage/cards/a/ArcBlade.java +++ b/Mage.Sets/src/mage/cards/a/ArcBlade.java @@ -27,10 +27,10 @@ public ArcBlade(UUID ownerId, CardSetInfo setInfo) { // Arc Blade deals 2 damage to any target. this.getSpellAbility().addEffect(new DamageTargetEffect(2)); // Exile Arc Blade - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); // with three time counters on it. Effect effect = new AddCountersSourceEffect(CounterType.TIME.createInstance(), StaticValue.get(3), false, true); - effect.setText("with 3 time counters on it"); + effect.setText("with three time counters on it"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetAnyTarget()); diff --git a/Mage.Sets/src/mage/cards/a/ArcTrail.java b/Mage.Sets/src/mage/cards/a/ArcTrail.java index e16796dccb0d..1ff4c47d4737 100644 --- a/Mage.Sets/src/mage/cards/a/ArcTrail.java +++ b/Mage.Sets/src/mage/cards/a/ArcTrail.java @@ -8,7 +8,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.filter.common.FilterCreaturePlayerOrPlaneswalker; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/a/ArcadesTheStrategist.java b/Mage.Sets/src/mage/cards/a/ArcadesTheStrategist.java index 0dbc363239be..c632eb2b98b2 100644 --- a/Mage.Sets/src/mage/cards/a/ArcadesTheStrategist.java +++ b/Mage.Sets/src/mage/cards/a/ArcadesTheStrategist.java @@ -25,7 +25,7 @@ public final class ArcadesTheStrategist extends CardImpl { private static final FilterControlledCreaturePermanent filter - = new FilterControlledCreaturePermanent("creature with defender"); + = new FilterControlledCreaturePermanent("a creature with defender"); private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent(); static { diff --git a/Mage.Sets/src/mage/cards/a/ArcaneSubtraction.java b/Mage.Sets/src/mage/cards/a/ArcaneSubtraction.java new file mode 100644 index 000000000000..91310eeee2a6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/ArcaneSubtraction.java @@ -0,0 +1,36 @@ +package mage.cards.a; + +import mage.abilities.effects.common.LearnEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ArcaneSubtraction extends CardImpl { + + public ArcaneSubtraction(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); + + // Target creature gets -4/-0 until end of turn. + this.getSpellAbility().addEffect(new BoostTargetEffect(-4, 0)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + + // Learn. + this.getSpellAbility().addEffect(new LearnEffect().concatBy("
")); + } + + private ArcaneSubtraction(final ArcaneSubtraction card) { + super(card); + } + + @Override + public ArcaneSubtraction copy() { + return new ArcaneSubtraction(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/Arcbond.java b/Mage.Sets/src/mage/cards/a/Arcbond.java index 65a0487f7cbc..23a888afdd0d 100644 --- a/Mage.Sets/src/mage/cards/a/Arcbond.java +++ b/Mage.Sets/src/mage/cards/a/Arcbond.java @@ -85,7 +85,7 @@ public boolean isInactive(Game game) { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/a/ArcboundCrusher.java b/Mage.Sets/src/mage/cards/a/ArcboundCrusher.java index cbfd65239e8e..593398d4f158 100644 --- a/Mage.Sets/src/mage/cards/a/ArcboundCrusher.java +++ b/Mage.Sets/src/mage/cards/a/ArcboundCrusher.java @@ -14,7 +14,7 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterArtifactPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/a/ArchaeomancersMap.java b/Mage.Sets/src/mage/cards/a/ArchaeomancersMap.java new file mode 100644 index 000000000000..39918d139174 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/ArchaeomancersMap.java @@ -0,0 +1,81 @@ +package mage.cards.a; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.PutCardFromHandOntoBattlefieldEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +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.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterLandPermanent; +import mage.game.Game; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ArchaeomancersMap extends CardImpl { + + private static final FilterCard filter = new FilterCard("basic Plains cards"); + private static final FilterPermanent filter2 = new FilterLandPermanent(); + + static { + filter.add(SubType.PLAINS.getPredicate()); + filter.add(SuperType.BASIC.getPredicate()); + filter2.add(TargetController.OPPONENT.getControllerPredicate()); + } + + public ArchaeomancersMap(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{W}"); + + // When Archaeomancer's Map enters the battlefield, search your library for up to two basic Plains cards, reveal them, put them into your hand, then shuffle. + this.addAbility(new EntersBattlefieldTriggeredAbility( + new SearchLibraryPutInHandEffect(new TargetCardInLibrary(2, filter), true) + )); + + // Whenever a land enters the battlefield under an opponent's control, if that player controls more lands than you, you may put a land card from your hand onto the battlefield. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new EntersBattlefieldAllTriggeredAbility( + new PutCardFromHandOntoBattlefieldEffect(StaticFilters.FILTER_CARD_LAND_A), filter2 + ), ArchaeomancersMapCondition.instance, "Whenever a land enters the battlefield " + + "under an opponent's control, if that player controls more lands than you, " + + "you may put a land card from your hand onto the battlefield." + )); + } + + private ArchaeomancersMap(final ArchaeomancersMap card) { + super(card); + } + + @Override + public ArchaeomancersMap copy() { + return new ArchaeomancersMap(this); + } +} + +enum ArchaeomancersMapCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + UUID playerId = (UUID) source.getEffects().get(0).getValue("permanentEnteringControllerId"); + return playerId != null && game.getBattlefield().count( + StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, + source.getSourceId(), playerId, game + ) > game.getBattlefield().count( + StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, + source.getSourceId(), source.getControllerId(), game + ); + } +} diff --git a/Mage.Sets/src/mage/cards/a/ArchangelOfTithes.java b/Mage.Sets/src/mage/cards/a/ArchangelOfTithes.java index bd98a677cd46..82f961cb8d2c 100644 --- a/Mage.Sets/src/mage/cards/a/ArchangelOfTithes.java +++ b/Mage.Sets/src/mage/cards/a/ArchangelOfTithes.java @@ -54,7 +54,7 @@ class ArchangelOfTithesPayManaToAttackAllEffect extends CantAttackYouUnlessPayMa ArchangelOfTithesPayManaToAttackAllEffect() { super(new ManaCostsImpl<>("{1}"), true); - staticText = "As long as {this} is untapped, creatures can't attack you or a planeswalker you control unless their controller pays {1} for each of those creatures."; + staticText = "As long as {this} is untapped, creatures can't attack you or planeswalkers you control unless their controller pays {1} for each of those creatures."; } ArchangelOfTithesPayManaToAttackAllEffect(ArchangelOfTithesPayManaToAttackAllEffect effect) { diff --git a/Mage.Sets/src/mage/cards/a/ArchfiendOfSpite.java b/Mage.Sets/src/mage/cards/a/ArchfiendOfSpite.java index 740aa5c673e0..a25c3ab7d03d 100644 --- a/Mage.Sets/src/mage/cards/a/ArchfiendOfSpite.java +++ b/Mage.Sets/src/mage/cards/a/ArchfiendOfSpite.java @@ -73,7 +73,7 @@ public ArchfiendOfSpiteAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/a/ArchfiendsVessel.java b/Mage.Sets/src/mage/cards/a/ArchfiendsVessel.java index e879c473ebf2..ba8e149d4ed1 100644 --- a/Mage.Sets/src/mage/cards/a/ArchfiendsVessel.java +++ b/Mage.Sets/src/mage/cards/a/ArchfiendsVessel.java @@ -113,7 +113,7 @@ public boolean apply(Game game, Ability source) { if (archfiendsVessel != null) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - boolean moved = controller.moveCards(archfiendsVessel.getMainCard(), Zone.EXILED, source, game); + boolean moved = controller.moveCards(archfiendsVessel, Zone.EXILED, source, game); if (moved) { Token token = new DemonToken(); token.putOntoBattlefield(1, game, source, controller.getId()); @@ -128,4 +128,4 @@ public boolean apply(Game game, Ability source) { public ArchfiendsVesselEffect copy() { return new ArchfiendsVesselEffect(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/a/ArchmageAscension.java b/Mage.Sets/src/mage/cards/a/ArchmageAscension.java index 2af1cf730074..ef2a99e76521 100644 --- a/Mage.Sets/src/mage/cards/a/ArchmageAscension.java +++ b/Mage.Sets/src/mage/cards/a/ArchmageAscension.java @@ -1,18 +1,16 @@ - package mage.cards.a; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.ReplacementEffectImpl; 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.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.CounterType; import mage.game.Game; import mage.game.events.GameEvent; @@ -32,12 +30,17 @@ public ArchmageAscension(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); // At the beginning of each end step, if you drew two or more cards this turn, you may put a quest counter on Archmage Ascension. - this.addAbility(new ArchmageAscensionTriggeredAbility(), new CardsAmountDrawnThisTurnWatcher()); + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new BeginningOfEndStepTriggeredAbility( + new AddCountersSourceEffect(CounterType.QUEST.createInstance(1)), + TargetController.EACH_PLAYER, true + ), ArchmageAscensionCondition.instance, "At the beginning of each end step, " + + "if you drew two or more cards this turn, you may put a quest counter on {this}." + ), new CardsAmountDrawnThisTurnWatcher()); // As long as Archmage Ascension has six or more quest counters on it, if you would draw a card, // you may instead search your library for a card, put that card into your hand, then shuffle your library. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ArchmageAscensionReplacementEffect())); - + this.addAbility(new SimpleStaticAbility(new ArchmageAscensionReplacementEffect())); } private ArchmageAscension(final ArchmageAscension card) { @@ -50,49 +53,25 @@ public ArchmageAscension copy() { } } -class ArchmageAscensionTriggeredAbility extends TriggeredAbilityImpl { - - public ArchmageAscensionTriggeredAbility() { - super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.QUEST.createInstance(1)), true); - } - - public ArchmageAscensionTriggeredAbility(final ArchmageAscensionTriggeredAbility ability) { - super(ability); - } - - @Override - public ArchmageAscensionTriggeredAbility copy() { - return new ArchmageAscensionTriggeredAbility(this); - } +enum ArchmageAscensionCondition implements Condition { + instance; @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.END_TURN_STEP_PRE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent archmage = game.getPermanent(super.getSourceId()); - CardsAmountDrawnThisTurnWatcher watcher - = game.getState().getWatcher(CardsAmountDrawnThisTurnWatcher.class); - return archmage != null && watcher != null && watcher.getAmountCardsDrawn(this.getControllerId()) >= 2; - } - - @Override - public String getRule() { - return "At the beginning of each end step, if you drew two or more cards this turn, you may put a quest counter on {this}"; + public boolean apply(Game game, Ability source) { + CardsAmountDrawnThisTurnWatcher watcher = game.getState().getWatcher(CardsAmountDrawnThisTurnWatcher.class); + return watcher != null && watcher.getAmountCardsDrawn(source.getControllerId()) >= 2; } } class ArchmageAscensionReplacementEffect extends ReplacementEffectImpl { - public ArchmageAscensionReplacementEffect() { + ArchmageAscensionReplacementEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit); staticText = "As long as {this} has six or more quest counters on it, if you would draw a card, " - + "you may instead search your library for a card, put that card into your hand, then shuffle your library"; + + "you may instead search your library for a card, put that card into your hand, then shuffle"; } - public ArchmageAscensionReplacementEffect(final ArchmageAscensionReplacementEffect effect) { + private ArchmageAscensionReplacementEffect(final ArchmageAscensionReplacementEffect effect) { super(effect); } @@ -109,16 +88,16 @@ public boolean apply(Game game, Ability source) { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player player = game.getPlayer(event.getPlayerId()); - if (player != null) { - TargetCardInLibrary target = new TargetCardInLibrary(); - if (player.searchLibrary(target, source, game)) { - Card card = game.getCard(target.getFirstTarget()); - if (card != null) { - card.moveToZone(Zone.HAND, source, game, false); - player.shuffleLibrary(source, game); - } - } + if (player == null) { + return false; + } + TargetCardInLibrary target = new TargetCardInLibrary(); + player.searchLibrary(target, source, game); + Card card = player.getLibrary().getCard(target.getFirstTarget(), game); + if (card != null) { + player.moveCards(card, Zone.HAND, source, game); } + player.shuffleLibrary(source, game); return true; } @@ -135,6 +114,6 @@ public boolean applies(GameEvent event, Ability source, Game game) { && archmage != null && archmage.getCounters(game).getCount(CounterType.QUEST) >= 6 && you != null - && you.chooseUse(Outcome.Benefit, "Would you like to search your library instead of drawing a card?", source, game); + && you.chooseUse(Outcome.Benefit, "Search your library instead of drawing a card?", source, game); } } diff --git a/Mage.Sets/src/mage/cards/a/ArchmageEmeritus.java b/Mage.Sets/src/mage/cards/a/ArchmageEmeritus.java new file mode 100644 index 000000000000..92fda504236b --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/ArchmageEmeritus.java @@ -0,0 +1,38 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +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 ArchmageEmeritus extends CardImpl { + + public ArchmageEmeritus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, draw a card. + this.addAbility(new MagecraftAbility(new DrawCardSourceControllerEffect(1))); + } + + private ArchmageEmeritus(final ArchmageEmeritus card) { + super(card); + } + + @Override + public ArchmageEmeritus copy() { + return new ArchmageEmeritus(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/ArchmagesCharm.java b/Mage.Sets/src/mage/cards/a/ArchmagesCharm.java index 20905a9a2990..d74e8e6f61a9 100644 --- a/Mage.Sets/src/mage/cards/a/ArchmagesCharm.java +++ b/Mage.Sets/src/mage/cards/a/ArchmagesCharm.java @@ -11,7 +11,7 @@ import mage.constants.Duration; import mage.filter.FilterPermanent; import mage.filter.common.FilterNonlandPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetPermanent; import mage.target.TargetPlayer; import mage.target.TargetSpell; @@ -24,10 +24,10 @@ public final class ArchmagesCharm extends CardImpl { private static final FilterPermanent filter - = new FilterNonlandPermanent("nonland permanent with converted mana cost 1 or less"); + = new FilterNonlandPermanent("nonland permanent with mana value 1 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 2)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 2)); } public ArchmagesCharm(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/a/ArchonOfAbsolution.java b/Mage.Sets/src/mage/cards/a/ArchonOfAbsolution.java index e2175f84b873..cb5d9b05040e 100644 --- a/Mage.Sets/src/mage/cards/a/ArchonOfAbsolution.java +++ b/Mage.Sets/src/mage/cards/a/ArchonOfAbsolution.java @@ -35,7 +35,7 @@ public ArchonOfAbsolution(UUID ownerId, CardSetInfo setInfo) { // Creatures can't attack you or a planeswalker you control unless their controller pays {1} for each of those creatures. this.addAbility(new SimpleStaticAbility(new CantAttackYouUnlessPayManaAllEffect( new ManaCostsImpl("{1}"), true - ).setText("creatures can't attack you or a planeswalker you control " + + ).setText("creatures can't attack you or planeswalkers you control " + "unless their controller pays {1} for each of those creatures"))); } diff --git a/Mage.Sets/src/mage/cards/a/ArchonOfFallingStars.java b/Mage.Sets/src/mage/cards/a/ArchonOfFallingStars.java index 0e32d8285018..6d21ccd3c218 100644 --- a/Mage.Sets/src/mage/cards/a/ArchonOfFallingStars.java +++ b/Mage.Sets/src/mage/cards/a/ArchonOfFallingStars.java @@ -33,7 +33,7 @@ public ArchonOfFallingStars(UUID ownerId, CardSetInfo setInfo) { this.addAbility(FlyingAbility.getInstance()); // When Archon of Falling Stars dies, you may return target enchantment card from your graveyard to the battlefield. - Ability ability = new DiesSourceTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(false, false), true); + Ability ability = new DiesSourceTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(), true); ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/ArchwayCommons.java b/Mage.Sets/src/mage/cards/a/ArchwayCommons.java new file mode 100644 index 000000000000..32c28dd39feb --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/ArchwayCommons.java @@ -0,0 +1,42 @@ +package mage.cards.a; + +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect; +import mage.abilities.mana.AnyColorManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ArchwayCommons extends CardImpl { + + public ArchwayCommons(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // Archway Commons enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // When Archway Commons enters the battlefield, sacrifice it unless you pay {1}. + this.addAbility(new EntersBattlefieldTriggeredAbility( + new SacrificeSourceUnlessPaysEffect(new GenericManaCost(1)).setText("sacrifice it unless you pay {1}") + )); + + // {T}: Add one mana of any color. + this.addAbility(new AnyColorManaAbility()); + } + + private ArchwayCommons(final ArchwayCommons card) { + super(card); + } + + @Override + public ArchwayCommons copy() { + return new ArchwayCommons(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/ArcumDagsson.java b/Mage.Sets/src/mage/cards/a/ArcumDagsson.java index 43b4063a5e2a..05850343d9f5 100644 --- a/Mage.Sets/src/mage/cards/a/ArcumDagsson.java +++ b/Mage.Sets/src/mage/cards/a/ArcumDagsson.java @@ -68,7 +68,7 @@ class ArcumDagssonEffect extends OneShotEffect { ArcumDagssonEffect() { super(Outcome.Removal); - this.staticText = "Target artifact creature's controller sacrifices it. That player may search their library for a noncreature artifact card, put it onto the battlefield, then shuffle their library"; + this.staticText = "Target artifact creature's controller sacrifices it. That player may search their library for a noncreature artifact card, put it onto the battlefield, then shuffle"; } ArcumDagssonEffect(final ArcumDagssonEffect effect) { diff --git a/Mage.Sets/src/mage/cards/a/ArdentDustspeaker.java b/Mage.Sets/src/mage/cards/a/ArdentDustspeaker.java new file mode 100644 index 000000000000..1d8f14d44ba5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/ArdentDustspeaker.java @@ -0,0 +1,99 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.CostImpl; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.ExileTopXMayPlayUntilEndOfTurnEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.common.FilterInstantOrSorceryCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author weirddan455 + */ +public final class ArdentDustspeaker extends CardImpl { + + public ArdentDustspeaker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}"); + + this.subtype.add(SubType.MINOTAUR); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Whenever Ardent Dustspeaker attacks, you may put an instant or sorcery card from your graveyard on the bottom of your library. + // If you do, exile the top two cards of your library. You may play those cards this turn. + this.addAbility(new AttacksTriggeredAbility( + new DoIfCostPaid( + new ExileTopXMayPlayUntilEndOfTurnEffect(2) + .setText("exile the top two cards of your library. You may play those cards this turn"), + new ArdentDustspeakerCost() + ), + false + )); + } + + private ArdentDustspeaker(final ArdentDustspeaker card) { + super(card); + } + + @Override + public ArdentDustspeaker copy() { + return new ArdentDustspeaker(this); + } +} + +class ArdentDustspeakerCost extends CostImpl { + + private static final FilterCard filter + = new FilterInstantOrSorceryCard("instant or sorcery card from your graveyard"); + + public ArdentDustspeakerCost() { + TargetCardInYourGraveyard target = new TargetCardInYourGraveyard(filter); + target.setNotTarget(true); + this.addTarget(target); + this.text = "put an instant or sorcery card from your graveyard on the bottom of your library"; + } + + private ArdentDustspeakerCost(final ArdentDustspeakerCost cost) { + super(cost); + } + + @Override + public ArdentDustspeakerCost copy() { + return new ArdentDustspeakerCost(this); + } + + @Override + public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { + return targets.canChoose(source.getSourceId(), controllerId, game); + } + + @Override + public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { + Player controller = game.getPlayer(controllerId); + if (controller != null) { + if (controller.chooseTarget(Outcome.Benefit, targets.get(0), source, game)) { + Card card = game.getCard(targets.get(0).getFirstTarget()); + if (card != null) { + controller.putCardsOnBottomOfLibrary(card, game, source, true); + paid = true; + } + } + } + return paid; + } +} diff --git a/Mage.Sets/src/mage/cards/a/ArenaOfTheAncients.java b/Mage.Sets/src/mage/cards/a/ArenaOfTheAncients.java index 56a7bbceeeb7..822a337b12e8 100644 --- a/Mage.Sets/src/mage/cards/a/ArenaOfTheAncients.java +++ b/Mage.Sets/src/mage/cards/a/ArenaOfTheAncients.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.cards.a; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/a/ArenaRector.java b/Mage.Sets/src/mage/cards/a/ArenaRector.java index d32f4d649191..a0ab52bbbeb0 100644 --- a/Mage.Sets/src/mage/cards/a/ArenaRector.java +++ b/Mage.Sets/src/mage/cards/a/ArenaRector.java @@ -34,7 +34,7 @@ public ArenaRector(UUID ownerId, CardSetInfo setInfo) { new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(new FilterPlaneswalkerCard())), new ExileSourceFromGraveCost(), "Exile to search for a planeswalker?" - ).setText("you may exile it. If you do, search your library for a planeswalker card, put it onto the battlefield, then shuffle your library"), false + ).setText("you may exile it. If you do, search your library for a planeswalker card, put it onto the battlefield, then shuffle"), false )); } diff --git a/Mage.Sets/src/mage/cards/a/ArgentSphinx.java b/Mage.Sets/src/mage/cards/a/ArgentSphinx.java index c99f92483d24..1386549a108a 100644 --- a/Mage.Sets/src/mage/cards/a/ArgentSphinx.java +++ b/Mage.Sets/src/mage/cards/a/ArgentSphinx.java @@ -1,22 +1,22 @@ package mage.cards.a; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.ActivateIfConditionActivatedAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnToBattlefieldUnderYourControlTargetEffect; import mage.abilities.hint.common.MetalcraftHint; import mage.abilities.keyword.FlyingAbility; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.targetpointer.FixedTarget; import java.util.UUID; @@ -37,7 +37,10 @@ public ArgentSphinx(UUID ownerId, CardSetInfo setInfo) { this.addAbility(FlyingAbility.getInstance()); // Metalcraft — {U}: Exile Argent Sphinx. Return it to the battlefield under your control at the beginning of the next end step. Activate this ability only if you control three or more artifacts. - Ability ability = new ActivateIfConditionActivatedAbility(Zone.BATTLEFIELD, new ArgentSphinxEffect(), new ManaCostsImpl("{U}"), MetalcraftCondition.instance); + Ability ability = new ActivateIfConditionActivatedAbility( + Zone.BATTLEFIELD, new ArgentSphinxEffect(), + new ManaCostsImpl("{U}"), MetalcraftCondition.instance + ); ability.setAbilityWord(AbilityWord.METALCRAFT); ability.addHint(MetalcraftHint.instance); this.addAbility(ability); @@ -56,37 +59,38 @@ public ArgentSphinx copy() { class ArgentSphinxEffect extends OneShotEffect { - private static final String effectText = "Exile {this}. Return it to the battlefield under your control at the beginning of the next end step"; + private static final String effectText = "Exile {this}. Return it to the battlefield " + + "under your control at the beginning of the next end step"; ArgentSphinxEffect() { super(Outcome.Benefit); staticText = effectText; } - ArgentSphinxEffect(ArgentSphinxEffect effect) { + private ArgentSphinxEffect(ArgentSphinxEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (permanent != null && sourceObject != null) { - if (permanent.moveToExile(source.getSourceId(), sourceObject.getIdName(), source, game)) { - //create delayed triggered ability - Effect effect = new ReturnToBattlefieldUnderYourControlTargetEffect(); - effect.setText("Return it to the battlefield under your control at the beginning of the next end step"); - effect.setTargetPointer(new FixedTarget(source.getSourceId(), game)); - game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source); - return true; - } + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (player == null || permanent == null) { + return false; } - return false; + Card card = permanent.getMainCard(); + player.moveCards(permanent, Zone.EXILED, source, game); + //create delayed triggered ability + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility( + new ReturnToBattlefieldUnderYourControlTargetEffect().setText( + "Return it to the battlefield under your control at the beginning of the next end step" + ).setTargetPointer(new FixedTarget(card, game)) + ), source); + return true; } @Override public ArgentSphinxEffect copy() { return new ArgentSphinxEffect(this); } - } diff --git a/Mage.Sets/src/mage/cards/a/ArgivianRestoration.java b/Mage.Sets/src/mage/cards/a/ArgivianRestoration.java index 3e8845c32b0f..9b555630301f 100644 --- a/Mage.Sets/src/mage/cards/a/ArgivianRestoration.java +++ b/Mage.Sets/src/mage/cards/a/ArgivianRestoration.java @@ -18,7 +18,7 @@ public ArgivianRestoration(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}{U}"); // Return target artifact card from your graveyard to the battlefield. - this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect(false, false)); + this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(new FilterArtifactCard("artifact card from your graveyard"))); } diff --git a/Mage.Sets/src/mage/cards/a/AridMesa.java b/Mage.Sets/src/mage/cards/a/AridMesa.java index 3d060b32195a..6178641b3d0a 100644 --- a/Mage.Sets/src/mage/cards/a/AridMesa.java +++ b/Mage.Sets/src/mage/cards/a/AridMesa.java @@ -20,7 +20,7 @@ public final class AridMesa extends CardImpl { public AridMesa(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.LAND},null); this.frameColor = new ObjectColor("WR"); - this.addAbility(new FetchLandActivatedAbility(EnumSet.of(SubType.MOUNTAIN, SubType.PLAINS))); + this.addAbility(new FetchLandActivatedAbility(SubType.MOUNTAIN, SubType.PLAINS)); } private AridMesa(final AridMesa card) { diff --git a/Mage.Sets/src/mage/cards/a/ArrogantPoet.java b/Mage.Sets/src/mage/cards/a/ArrogantPoet.java new file mode 100644 index 000000000000..30b6f5fbd92c --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/ArrogantPoet.java @@ -0,0 +1,49 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.FlyingAbility; +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 ArrogantPoet extends CardImpl { + + public ArrogantPoet(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Whenever Arrogant Poet attacks, you may pay 2 life. If you do, it gains flying until end of turn. + this.addAbility(new AttacksTriggeredAbility( + new DoIfCostPaid( + new GainAbilitySourceEffect( + FlyingAbility.getInstance(), Duration.EndOfTurn + ).setText("it gains flying until end of turn"), + new PayLifeCost(2) + ), false + )); + } + + private ArrogantPoet(final ArrogantPoet card) { + super(card); + } + + @Override + public ArrogantPoet copy() { + return new ArrogantPoet(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/ArtifactMutation.java b/Mage.Sets/src/mage/cards/a/ArtifactMutation.java index eb213386fea3..278d8763deea 100644 --- a/Mage.Sets/src/mage/cards/a/ArtifactMutation.java +++ b/Mage.Sets/src/mage/cards/a/ArtifactMutation.java @@ -1,6 +1,6 @@ package mage.cards.a; -import mage.abilities.dynamicvalue.common.TargetConvertedManaCost; +import mage.abilities.dynamicvalue.common.TargetManaValue; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; @@ -24,7 +24,7 @@ public ArtifactMutation(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addTarget(new TargetArtifactPermanent()); // create X 1/1 green Saproling creature tokens, where X is that artifact's converted mana cost. - this.getSpellAbility().addEffect(new CreateTokenEffect(new SaprolingToken(), TargetConvertedManaCost.instance).setText("create X 1/1 green Saproling creature tokens, where X is that artifact's converted mana cost")); + this.getSpellAbility().addEffect(new CreateTokenEffect(new SaprolingToken(), TargetManaValue.instance).setText("create X 1/1 green Saproling creature tokens, where X is that artifact's mana value")); } private ArtifactMutation(final ArtifactMutation card) { diff --git a/Mage.Sets/src/mage/cards/a/ArtificersIntuition.java b/Mage.Sets/src/mage/cards/a/ArtificersIntuition.java index a2a44968fd8f..1ff0807b76df 100644 --- a/Mage.Sets/src/mage/cards/a/ArtificersIntuition.java +++ b/Mage.Sets/src/mage/cards/a/ArtificersIntuition.java @@ -1,7 +1,5 @@ - package mage.cards.a; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.DiscardCardCost; @@ -12,27 +10,30 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.filter.common.FilterArtifactCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; +import java.util.UUID; + /** - * * @author Plopman */ public final class ArtificersIntuition extends CardImpl { - private static final FilterArtifactCard filter = new FilterArtifactCard("artifact card with converted mana cost 1 or less"); + private static final FilterArtifactCard filter = new FilterArtifactCard("artifact card with mana value 1 or less"); + static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 2)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 2)); } - public ArtificersIntuition(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{U}"); + public ArtificersIntuition(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); // {U}, Discard an artifact card: Search your library for an artifact card with converted mana cost 1 or less, reveal that card, and put it into your hand. Then shuffle your library. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true), new ManaCostsImpl("{U}")); - ability.addCost(new DiscardCardCost(new FilterArtifactCard())); + ability.addCost(new DiscardCardCost(StaticFilters.FILTER_CARD_ARTIFACT_AN)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/AsForetold.java b/Mage.Sets/src/mage/cards/a/AsForetold.java index fa5560075379..b07ad1208303 100644 --- a/Mage.Sets/src/mage/cards/a/AsForetold.java +++ b/Mage.Sets/src/mage/cards/a/AsForetold.java @@ -72,7 +72,7 @@ public boolean apply(Game game, Ability source) { MageObject object = game.getObject(source.getSourceId()); return object != null && !object.isLand() - && object.getConvertedManaCost() <= counters; + && object.getManaValue() <= counters; } } @@ -105,7 +105,7 @@ public boolean askToActivateAlternativeCosts(Ability ability, Game game) { Permanent asForetold = game.getPermanent(getSourceId()); if (controller != null && asForetold != null) { - if (controller.chooseUse(Outcome.Neutral, "Do you wish to use " + if (controller.chooseUse(Outcome.Neutral, "Use " + asForetold.getLogName() + " to pay the alternative cost ?", ability, game)) { wasActivated = super.askToActivateAlternativeCosts(ability, game); if (wasActivated) { @@ -128,7 +128,7 @@ class AsForetoldAddAltCostEffect extends ContinuousEffectImpl { public AsForetoldAddAltCostEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "Once each turn, you may pay {0} rather than pay the mana cost for a spell you cast with converted mana cost X or less, where X is the number of time counters on {this}."; + staticText = "Once each turn, you may pay {0} rather than pay the mana cost for a spell you cast with mana value X or less, where X is the number of time counters on {this}."; } public AsForetoldAddAltCostEffect(final AsForetoldAddAltCostEffect effect) { diff --git a/Mage.Sets/src/mage/cards/a/AscentOfTheWorthy.java b/Mage.Sets/src/mage/cards/a/AscentOfTheWorthy.java index fef6b428f4c3..46898f98184e 100644 --- a/Mage.Sets/src/mage/cards/a/AscentOfTheWorthy.java +++ b/Mage.Sets/src/mage/cards/a/AscentOfTheWorthy.java @@ -95,6 +95,7 @@ public boolean apply(Game game, Ability source) { if (permanent == null) { return false; } + game.informPlayers(player.getName() + " chooses to have all damage redirected to " + permanent.getIdName()); game.addEffect(new AscentOfTheWorthyRedirectEffect(new MageObjectReference(permanent, game)), source); return false; } @@ -127,13 +128,13 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT; } @Override public boolean applies(GameEvent event, Ability source, Game game) { Permanent permanent = game.getPermanent(event.getTargetId()); - return permanent != null && permanent.isControlledBy(source.getControllerId()); + return permanent != null && permanent.isCreature() && permanent.isControlledBy(source.getControllerId()); } @Override @@ -161,7 +162,7 @@ public AscentOfTheWorthyReturnEffect copy() { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getSourceId()); + Player player = game.getPlayer(source.getControllerId()); Card card = game.getCard(source.getFirstTarget()); if (player == null || card == null) { return false; diff --git a/Mage.Sets/src/mage/cards/a/AshenmoorCohort.java b/Mage.Sets/src/mage/cards/a/AshenmoorCohort.java index 9c935fd5c2fc..6a3987ad6da3 100644 --- a/Mage.Sets/src/mage/cards/a/AshenmoorCohort.java +++ b/Mage.Sets/src/mage/cards/a/AshenmoorCohort.java @@ -16,7 +16,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/a/AshesToAshes.java b/Mage.Sets/src/mage/cards/a/AshesToAshes.java index 3330c5b16d71..8687a4c8478f 100644 --- a/Mage.Sets/src/mage/cards/a/AshesToAshes.java +++ b/Mage.Sets/src/mage/cards/a/AshesToAshes.java @@ -1,38 +1,33 @@ - package mage.cards.a; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageControllerEffect; +import mage.abilities.effects.common.ExileTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; +import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class AshesToAshes extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonartifact creature"); + + private static final FilterPermanent filter = new FilterCreaturePermanent("nonartifact creatures"); static { filter.add(Predicates.not(CardType.ARTIFACT.getPredicate())); } public AshesToAshes(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{B}{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}{B}"); // Exile two target nonartifact creatures. Ashes to Ashes deals 5 damage to you. - this.getSpellAbility().addEffect(new AshesToAshesEffect()); + this.getSpellAbility().addEffect(new ExileTargetEffect()); this.getSpellAbility().addTarget(new TargetPermanent(2, filter)); this.getSpellAbility().addEffect(new DamageControllerEffect(5)); } @@ -46,32 +41,3 @@ public AshesToAshes copy() { return new AshesToAshes(this); } } - -class AshesToAshesEffect extends OneShotEffect { - - public AshesToAshesEffect() { - super(Outcome.Benefit); - staticText = "Exile two target nonartifact creatures"; - } - - public AshesToAshesEffect(final AshesToAshesEffect effect) { - super(effect); - } - - @Override - public AshesToAshesEffect copy() { - return new AshesToAshesEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - UUID exileId = source.getSourceId(); - for (UUID permanentId : targetPointer.getTargets(game, source)) { - Permanent target = game.getPermanent(permanentId); - if (target != null) { - target.moveToExile(exileId, "Ashes to Ashes", source, game); - } - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/a/AshiokNightmareWeaver.java b/Mage.Sets/src/mage/cards/a/AshiokNightmareWeaver.java index f04861595842..e52d69111306 100644 --- a/Mage.Sets/src/mage/cards/a/AshiokNightmareWeaver.java +++ b/Mage.Sets/src/mage/cards/a/AshiokNightmareWeaver.java @@ -13,7 +13,7 @@ import mage.constants.*; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -96,7 +96,7 @@ class AshiokNightmareWeaverPutIntoPlayEffect extends OneShotEffect { public AshiokNightmareWeaverPutIntoPlayEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "Put a creature card with converted mana cost X exiled with {this} onto the battlefield under your control. That creature is a Nightmare in addition to its other types"; + this.staticText = "Put a creature card with mana value X exiled with {this} onto the battlefield under your control. That creature is a Nightmare in addition to its other types"; } public AshiokNightmareWeaverPutIntoPlayEffect(final AshiokNightmareWeaverPutIntoPlayEffect effect) { @@ -123,8 +123,8 @@ public boolean apply(Game game, Ability source) { } } - FilterCard filter = new FilterCreatureCard("creature card with converted mana cost {" + cmc + "} exiled with " + sourceObject.getIdName()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, cmc)); + FilterCard filter = new FilterCreatureCard("creature card with mana value {" + cmc + "} exiled with " + sourceObject.getIdName()); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, cmc)); Target target = new TargetCardInExile(filter, CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter())); diff --git a/Mage.Sets/src/mage/cards/a/AshlingsPrerogative.java b/Mage.Sets/src/mage/cards/a/AshlingsPrerogative.java index 5c666f6640b8..12f5d0c75afd 100644 --- a/Mage.Sets/src/mage/cards/a/AshlingsPrerogative.java +++ b/Mage.Sets/src/mage/cards/a/AshlingsPrerogative.java @@ -53,12 +53,12 @@ public AshlingsPrerogative copy() { class AshlingsPrerogativeIncorrectOddityEffect extends PermanentsEnterBattlefieldTappedEffect { - private static final FilterCreaturePermanent creaturefilter = new FilterCreaturePermanent("Each creature without converted mana cost of the chosen value"); + private static final FilterCreaturePermanent creaturefilter = new FilterCreaturePermanent("Each creature without mana value of the chosen quality"); private static final ModeChoiceSourceCondition oddCondition = new ModeChoiceSourceCondition("Odd"); public AshlingsPrerogativeIncorrectOddityEffect() { super(creaturefilter); - staticText = "Each creature without converted mana cost of the chosen value enters the battlefield tapped."; + staticText = "Each creature without mana value of the chosen quality enters the battlefield tapped."; } public AshlingsPrerogativeIncorrectOddityEffect(final AshlingsPrerogativeIncorrectOddityEffect effect) { @@ -77,7 +77,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); - return permanent != null && creaturefilter.match(permanent, game) && permanent.getConvertedManaCost() % 2 == incorrectModResult; + return permanent != null && creaturefilter.match(permanent, game) && permanent.getManaValue() % 2 == incorrectModResult; } @Override @@ -88,12 +88,12 @@ public AshlingsPrerogativeIncorrectOddityEffect copy() { class AshlingsPrerogativeCorrectOddityEffect extends GainAbilityAllEffect { - private static final FilterCreaturePermanent creaturefilter = new FilterCreaturePermanent("Each creature with converted mana cost of the chosen value"); + private static final FilterCreaturePermanent creaturefilter = new FilterCreaturePermanent("Each creature with mana value of the chosen quality"); private static final ModeChoiceSourceCondition oddCondition = new ModeChoiceSourceCondition("Odd"); public AshlingsPrerogativeCorrectOddityEffect() { super(HasteAbility.getInstance(), Duration.WhileOnBattlefield, creaturefilter); - staticText = "Each creature with converted mana cost of the chosen value has haste."; + staticText = "Each creature with mana value of the chosen quality has haste."; } public AshlingsPrerogativeCorrectOddityEffect(final AshlingsPrerogativeCorrectOddityEffect effect) { super(effect); @@ -107,7 +107,7 @@ protected boolean selectedByRuntimeData(Permanent permanent, Ability source, Gam } else { correctModResult = 0; } - return permanent != null && creaturefilter.match(permanent, game) && permanent.getConvertedManaCost() % 2 == correctModResult; + return permanent != null && creaturefilter.match(permanent, game) && permanent.getManaValue() % 2 == correctModResult; } @Override diff --git a/Mage.Sets/src/mage/cards/a/AssassinsTrophy.java b/Mage.Sets/src/mage/cards/a/AssassinsTrophy.java index 00432187d641..4551a2144f12 100644 --- a/Mage.Sets/src/mage/cards/a/AssassinsTrophy.java +++ b/Mage.Sets/src/mage/cards/a/AssassinsTrophy.java @@ -56,7 +56,7 @@ public AssassinsTrophyEffect() { super(Outcome.PutLandInPlay); this.staticText = "Its controller may search their library " + "for a basic land card, put it onto the battlefield, " - + "then shuffle their library"; + + "then shuffle"; } public AssassinsTrophyEffect(final AssassinsTrophyEffect effect) { @@ -74,7 +74,7 @@ public boolean apply(Game game, Ability source) { if (permanent != null) { Player controller = game.getPlayer(permanent.getControllerId()); if (controller != null) { - if (controller.chooseUse(Outcome.PutLandInPlay, "Do you wish to search for a basic land, put it onto the battlefield and then shuffle your library?", source, game)) { + if (controller.chooseUse(Outcome.PutLandInPlay, "Search for a basic land, put it onto the battlefield and then shuffle?", source, game)) { TargetCardInLibrary target = new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND); if (controller.searchLibrary(target, source, game)) { Card card = controller.getLibrary().getCard(target.getFirstTarget(), game); diff --git a/Mage.Sets/src/mage/cards/a/AssaultSuit.java b/Mage.Sets/src/mage/cards/a/AssaultSuit.java index 07af257e2e1f..29c8e677e0e5 100644 --- a/Mage.Sets/src/mage/cards/a/AssaultSuit.java +++ b/Mage.Sets/src/mage/cards/a/AssaultSuit.java @@ -41,7 +41,7 @@ public AssaultSuit(UUID ownerId, CardSetInfo setInfo) { effect.setText(", has haste"); ability.addEffect(effect); effect = new CantAttackControllerAttachedEffect(AttachmentType.EQUIPMENT); - effect.setText(", can't attack you or a planeswalker you control"); + effect.setText(", can't attack you or planeswalkers you control"); ability.addEffect(effect); effect = new AssaultSuitCantBeSacrificed(); effect.setText(", and can't be sacrificed"); diff --git a/Mage.Sets/src/mage/cards/a/AssemblyHall.java b/Mage.Sets/src/mage/cards/a/AssemblyHall.java index d31eca61594b..83960e895e8a 100644 --- a/Mage.Sets/src/mage/cards/a/AssemblyHall.java +++ b/Mage.Sets/src/mage/cards/a/AssemblyHall.java @@ -55,7 +55,7 @@ public AssemblyHallEffect() { super(Outcome.Benefit); this.staticText = "reveal a creature card from your hand. " + "Search your library for a card with the same name as that card, " - + "reveal it, and put it into your hand. Then shuffle your library"; + + "reveal it, and put it into your hand. Then shuffle"; } public AssemblyHallEffect(final AssemblyHallEffect effect) { diff --git a/Mage.Sets/src/mage/cards/a/AtemsisAllSeeing.java b/Mage.Sets/src/mage/cards/a/AtemsisAllSeeing.java index 76f4e60caba7..7f110b7c056e 100644 --- a/Mage.Sets/src/mage/cards/a/AtemsisAllSeeing.java +++ b/Mage.Sets/src/mage/cards/a/AtemsisAllSeeing.java @@ -63,7 +63,7 @@ class AtemsisAllSeeingEffect extends OneShotEffect { AtemsisAllSeeingEffect() { super(Outcome.Benefit); - staticText = "reveal your hand. If cards with at least six different converted mana costs " + + staticText = "reveal your hand. If cards with at least six different mana values " + "are revealed this way, that player loses the game."; } @@ -88,7 +88,7 @@ public boolean apply(Game game, Ability source) { .getHand() .getCards(game) .stream() - .map(card -> card.getConvertedManaCost()) + .map(card -> card.getManaValue()) .distinct() .count() > 5) { opponent.lost(game); diff --git a/Mage.Sets/src/mage/cards/a/AthreosGodOfPassage.java b/Mage.Sets/src/mage/cards/a/AthreosGodOfPassage.java index 7a72706e8dee..1c923b398113 100644 --- a/Mage.Sets/src/mage/cards/a/AthreosGodOfPassage.java +++ b/Mage.Sets/src/mage/cards/a/AthreosGodOfPassage.java @@ -16,10 +16,9 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; import mage.players.Player; import mage.target.common.TargetOpponent; diff --git a/Mage.Sets/src/mage/cards/a/AthreosShroudVeiled.java b/Mage.Sets/src/mage/cards/a/AthreosShroudVeiled.java index 57feccd8943b..acad64c2ec94 100644 --- a/Mage.Sets/src/mage/cards/a/AthreosShroudVeiled.java +++ b/Mage.Sets/src/mage/cards/a/AthreosShroudVeiled.java @@ -18,7 +18,7 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; diff --git a/Mage.Sets/src/mage/cards/a/AttendedHealer.java b/Mage.Sets/src/mage/cards/a/AttendedHealer.java index ea804d0d36fb..a6b1a19536a8 100644 --- a/Mage.Sets/src/mage/cards/a/AttendedHealer.java +++ b/Mage.Sets/src/mage/cards/a/AttendedHealer.java @@ -14,7 +14,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.CatToken3; import mage.target.TargetPermanent; diff --git a/Mage.Sets/src/mage/cards/a/AttuneWithAether.java b/Mage.Sets/src/mage/cards/a/AttuneWithAether.java index a176f0d214ad..d87d93216c05 100644 --- a/Mage.Sets/src/mage/cards/a/AttuneWithAether.java +++ b/Mage.Sets/src/mage/cards/a/AttuneWithAether.java @@ -23,7 +23,7 @@ public AttuneWithAether(UUID ownerId, CardSetInfo setInfo) { // Search you library for a basic land card, reveal it, put it into your hand, then shuffle your library. You get {E}{E}. Effect effect = new SearchLibraryPutInHandEffect(new TargetCardInLibrary(1, 1, StaticFilters.FILTER_CARD_BASIC_LAND), true); - effect.setText("Search your library for a basic land card, reveal it, put it into your hand, then shuffle your library"); + effect.setText("Search your library for a basic land card, reveal it, put it into your hand, then shuffle"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addEffect(new GetEnergyCountersControllerEffect(2)); } diff --git a/Mage.Sets/src/mage/cards/a/AtzocanArcher.java b/Mage.Sets/src/mage/cards/a/AtzocanArcher.java index c0d82a88ff00..d9d0853a9181 100644 --- a/Mage.Sets/src/mage/cards/a/AtzocanArcher.java +++ b/Mage.Sets/src/mage/cards/a/AtzocanArcher.java @@ -13,7 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/a/AudaciousInfiltrator.java b/Mage.Sets/src/mage/cards/a/AudaciousInfiltrator.java index c41a3e39f1e7..58bab8ce0b6e 100644 --- a/Mage.Sets/src/mage/cards/a/AudaciousInfiltrator.java +++ b/Mage.Sets/src/mage/cards/a/AudaciousInfiltrator.java @@ -29,7 +29,7 @@ public AudaciousInfiltrator(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(1); // Audacious Infiltrator can't be blocked by artifact creatures. - Effect effect = new CantBeBlockedByCreaturesSourceEffect(StaticFilters.FILTER_ARTIFACT_CREATURE_PERMANENT, Duration.WhileOnBattlefield); + Effect effect = new CantBeBlockedByCreaturesSourceEffect(StaticFilters.FILTER_PERMANENT_ARTIFACT_CREATURE, Duration.WhileOnBattlefield); effect.setText("{this} can't be blocked by artifact creatures"); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); } diff --git a/Mage.Sets/src/mage/cards/a/AudaciousReshapers.java b/Mage.Sets/src/mage/cards/a/AudaciousReshapers.java new file mode 100644 index 000000000000..ec4298c9b823 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AudaciousReshapers.java @@ -0,0 +1,98 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.OneShotEffect; +import mage.cards.*; +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.players.Player; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AudaciousReshapers extends CardImpl { + + public AudaciousReshapers(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ARTIFICER); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // {T}, Sacrifice an artifact: Reveal cards from the top of your library until you reveal an artifact card. Put that card onto the battlefield and the rest on the bottom of your library in a random order. Audacious Reshapers deals damage to you equal to the number of cards revealed this way. + Ability ability = new SimpleActivatedAbility(new AudaciousReshapersEffect(), new TapSourceCost()); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent( + StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT_AN + ))); + this.addAbility(ability); + } + + private AudaciousReshapers(final AudaciousReshapers card) { + super(card); + } + + @Override + public AudaciousReshapers copy() { + return new AudaciousReshapers(this); + } +} + +class AudaciousReshapersEffect extends OneShotEffect { + + AudaciousReshapersEffect() { + super(Outcome.Benefit); + staticText = "reveal cards from the top of your library until you reveal an artifact card. " + + "Put that card onto the battlefield and the rest on the bottom of your library in a random order. " + + "{this} deals damage to you equal to the number of cards revealed this way"; + } + + private AudaciousReshapersEffect(final AudaciousReshapersEffect effect) { + super(effect); + } + + @Override + public AudaciousReshapersEffect copy() { + return new AudaciousReshapersEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(); + Card artifact = null; + for (Card card : player.getLibrary().getCards(game)) { + if (card == null) { + continue; + } + cards.add(card); + if (card.isArtifact()) { + artifact = card; + break; + } + } + int size = cards.size(); + player.revealCards(source, cards, game); + if (artifact != null) { + player.moveCards(artifact, Zone.BATTLEFIELD, source, game); + } + cards.removeIf(uuid -> game.getState().getZone(uuid) != Zone.LIBRARY); + player.putCardsOnBottomOfLibrary(cards, game, source, false); + player.damage(size, source.getSourceId(), source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/a/AugmenterPugilist.java b/Mage.Sets/src/mage/cards/a/AugmenterPugilist.java new file mode 100644 index 000000000000..802f888fb742 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AugmenterPugilist.java @@ -0,0 +1,110 @@ +package mage.cards.a; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.common.LandsYouControlHint; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.util.functions.CopyApplier; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class AugmenterPugilist extends ModalDoubleFacesCard { + + public AugmenterPugilist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.TROLL, SubType.DRUID}, "{1}{G}{G}", + "Echoing Equation", new CardType[]{CardType.SORCERY}, new SubType[]{}, "{3}{U}{U}"); + + // 1. + // Augmenter Pugilist + // Creature — Troll Druid + this.getLeftHalfCard().setPT(3, 3); + + // Trample + this.getLeftHalfCard().addAbility(TrampleAbility.getInstance()); + + // As long as you control eight or more lands, Augmenter Pugilist gets +5/+5. + this.getLeftHalfCard().addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, + new ConditionalContinuousEffect( + new BoostSourceEffect( + 5, 5, Duration.WhileOnBattlefield + ), + new PermanentsOnTheBattlefieldCondition( + StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, + ComparisonType.MORE_THAN, 7 + ), + "as long as you control eight or more lands, {this} gets +5/+5" + ) + ).addHint(LandsYouControlHint.instance)); + + // 2. + // Echoing Equation + // Sorcery + + // Choose target creature you control. Each other creature you control becomes a copy of it until end of turn, except those creatures aren’t legendary if the chosen creature is legendary. + this.getRightHalfCard().getSpellAbility().addEffect(new EchoingEquationEffect()); + this.getRightHalfCard().getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + + } + + private AugmenterPugilist(final AugmenterPugilist card) { + super(card); + } + + @Override + public AugmenterPugilist copy() { + return new AugmenterPugilist(this); + } +} + +class EchoingEquationEffect extends OneShotEffect { + + public EchoingEquationEffect() { + super(Outcome.Benefit); + staticText = "choose target creature you control. Each other creature you control becomes a copy of it until end of turn, except those creatures aren't legendary if the chosen creature is legendary"; + } + + EchoingEquationEffect(EchoingEquationEffect effect) { + super(effect); + } + + @Override + public EchoingEquationEffect copy() { + return new EchoingEquationEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent copyFrom = game.getPermanent(source.getFirstTarget()); + if (copyFrom != null) { + game.getBattlefield().getAllActivePermanents(source.getControllerId()).stream() + .filter(permanent -> permanent.isCreature() && !permanent.getId().equals(copyFrom.getId())) + .forEach(copyTo -> game.copyPermanent(Duration.EndOfTurn, copyFrom, copyTo.getId(), source, new CopyApplier() { + @Override + public boolean apply(Game game, MageObject blueprint, Ability source, UUID targetObjectId) { + blueprint.getSuperType().remove(SuperType.LEGENDARY); + return true; + } + })); + return true; + } + return false; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/a/AuguryAdept.java b/Mage.Sets/src/mage/cards/a/AuguryAdept.java index 1b3beb52eba8..503fb1f6b6bd 100644 --- a/Mage.Sets/src/mage/cards/a/AuguryAdept.java +++ b/Mage.Sets/src/mage/cards/a/AuguryAdept.java @@ -1,12 +1,13 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +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; @@ -14,8 +15,9 @@ import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class AuguryAdept extends CardImpl { @@ -44,12 +46,13 @@ public AuguryAdept copy() { class AuguryAdeptEffect extends OneShotEffect { - public AuguryAdeptEffect() { + AuguryAdeptEffect() { super(Outcome.GainLife); - this.staticText = "reveal the top card of your library and put that card into your hand. You gain life equal to its converted mana cost"; + this.staticText = "reveal the top card of your library and put that card into your hand. " + + "You gain life equal to its mana value"; } - public AuguryAdeptEffect(final AuguryAdeptEffect effect) { + private AuguryAdeptEffect(final AuguryAdeptEffect effect) { super(effect); } @@ -65,13 +68,14 @@ public boolean apply(Game game, Ability source) { return false; } Card card = controller.getLibrary().getFromTop(game); - if (card != null) { - card.moveToZone(Zone.HAND, source, game, true); - int cmc = card.getConvertedManaCost(); - if (cmc > 0) { - controller.gainLife(cmc, game, source); - } - controller.revealCards(source, new CardsImpl(card), game); + if (card == null) { + return false; + } + controller.revealCards(source, new CardsImpl(card), game); + controller.moveCards(card, Zone.HAND, source, game); + int cmc = card.getManaValue(); + if (cmc > 0) { + controller.gainLife(cmc, game, source); } return true; } diff --git a/Mage.Sets/src/mage/cards/a/AuntiesSnitch.java b/Mage.Sets/src/mage/cards/a/AuntiesSnitch.java index 38f5b6903854..7432bd05b7c7 100644 --- a/Mage.Sets/src/mage/cards/a/AuntiesSnitch.java +++ b/Mage.Sets/src/mage/cards/a/AuntiesSnitch.java @@ -83,7 +83,7 @@ public boolean checkEventType(GameEvent event, Game game) { public boolean checkTrigger(GameEvent event, Game game) { DamagedPlayerEvent damageEvent = (DamagedPlayerEvent)event; Permanent p = game.getPermanent(event.getSourceId()); - return damageEvent.isCombatDamage() && p != null && filter.match(p, getSourceId(), getControllerId(), game); + return damageEvent.isCombatDamage() && filter.match(p, getSourceId(), getControllerId(), game); } @Override diff --git a/Mage.Sets/src/mage/cards/a/AuraGraft.java b/Mage.Sets/src/mage/cards/a/AuraGraft.java index be06b6746015..26fdd13056db 100644 --- a/Mage.Sets/src/mage/cards/a/AuraGraft.java +++ b/Mage.Sets/src/mage/cards/a/AuraGraft.java @@ -16,7 +16,7 @@ import mage.filter.FilterPermanent; import mage.filter.predicate.ObjectPlayer; import mage.filter.predicate.ObjectPlayerPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/a/AuraMutation.java b/Mage.Sets/src/mage/cards/a/AuraMutation.java index fc9f2d28028a..8f24b8c34bfa 100644 --- a/Mage.Sets/src/mage/cards/a/AuraMutation.java +++ b/Mage.Sets/src/mage/cards/a/AuraMutation.java @@ -2,7 +2,7 @@ package mage.cards.a; import java.util.UUID; -import mage.abilities.dynamicvalue.common.TargetConvertedManaCost; +import mage.abilities.dynamicvalue.common.TargetManaValue; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; @@ -25,7 +25,7 @@ public AuraMutation(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addTarget(new TargetEnchantmentPermanent()); // create X 1/1 green Saproling creature tokens, where X is that enchantment's converted mana cost. - this.getSpellAbility().addEffect(new CreateTokenEffect(new SaprolingToken(), TargetConvertedManaCost.instance)); + this.getSpellAbility().addEffect(new CreateTokenEffect(new SaprolingToken(), TargetManaValue.instance)); } private AuraMutation(final AuraMutation card) { diff --git a/Mage.Sets/src/mage/cards/a/AuraThief.java b/Mage.Sets/src/mage/cards/a/AuraThief.java index 463f934acc90..0c29f34f9cf2 100644 --- a/Mage.Sets/src/mage/cards/a/AuraThief.java +++ b/Mage.Sets/src/mage/cards/a/AuraThief.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.cards.a; import mage.MageInt; diff --git a/Mage.Sets/src/mage/cards/a/AuratouchedMage.java b/Mage.Sets/src/mage/cards/a/AuratouchedMage.java index 032430a6666f..bba8246e880f 100644 --- a/Mage.Sets/src/mage/cards/a/AuratouchedMage.java +++ b/Mage.Sets/src/mage/cards/a/AuratouchedMage.java @@ -12,7 +12,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.other.AuraCardCanAttachToLKIPermanentId; +import mage.filter.predicate.card.AuraCardCanAttachToLKIPermanentId; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -51,7 +51,7 @@ class AuratouchedMageEffect extends OneShotEffect { public AuratouchedMageEffect() { super(Outcome.BoostCreature); - staticText = "search your library for an Aura card that could enchant it. If {this} is still on the battlefield, put that Aura card onto the battlefield attached to it. Otherwise, reveal the Aura card and put it into your hand. Then shuffle your library."; + staticText = "search your library for an Aura card that could enchant it. If {this} is still on the battlefield, put that Aura card onto the battlefield attached to it. Otherwise, reveal the Aura card and put it into your hand. Then shuffle"; } public AuratouchedMageEffect(final AuratouchedMageEffect effect) { diff --git a/Mage.Sets/src/mage/cards/a/AureliasFury.java b/Mage.Sets/src/mage/cards/a/AureliasFury.java index 5d54846142c9..c487c51c03bc 100644 --- a/Mage.Sets/src/mage/cards/a/AureliasFury.java +++ b/Mage.Sets/src/mage/cards/a/AureliasFury.java @@ -18,7 +18,6 @@ import mage.constants.WatcherScope; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.players.Player; @@ -177,21 +176,19 @@ class AureliasFuryDamagedByWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.DAMAGED_CREATURE) { - MageObject obj = game.getObject(event.getSourceId()); - if (obj instanceof Spell) { - if (sourceId.equals(((Spell) obj).getSourceId())) { - damagedCreatures.add(event.getTargetId()); - } - } + MageObject obj = game.getObject(event.getSourceId()); + if (!(obj instanceof Spell) || !sourceId.equals(((Spell) obj).getSourceId())) { + return; } - if (event.getType() == GameEvent.EventType.DAMAGED_PLAYER) { - MageObject obj = game.getObject(event.getSourceId()); - if (obj instanceof Spell) { - if (sourceId.equals(((Spell) obj).getSourceId())) { - damagedPlayers.add(event.getTargetId()); + switch (event.getType()) { + case DAMAGED_PERMANENT: + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null && permanent.isCreature()) { + damagedCreatures.add(event.getTargetId()); } - } + return; + case DAMAGED_PLAYER: + damagedPlayers.add(event.getTargetId()); } } diff --git a/Mage.Sets/src/mage/cards/a/AuriokChampion.java b/Mage.Sets/src/mage/cards/a/AuriokChampion.java index 9cd2337d0992..986e7e28f44c 100644 --- a/Mage.Sets/src/mage/cards/a/AuriokChampion.java +++ b/Mage.Sets/src/mage/cards/a/AuriokChampion.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/a/AuriokSalvagers.java b/Mage.Sets/src/mage/cards/a/AuriokSalvagers.java index fbfadb4b470f..396921324de9 100644 --- a/Mage.Sets/src/mage/cards/a/AuriokSalvagers.java +++ b/Mage.Sets/src/mage/cards/a/AuriokSalvagers.java @@ -6,6 +6,7 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -14,7 +15,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterArtifactCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInYourGraveyard; /** @@ -23,9 +24,9 @@ */ public final class AuriokSalvagers extends CardImpl { - private static final FilterArtifactCard filter = new FilterArtifactCard("artifact card with converted mana cost 1 or less from your graveyard"); + private static final FilterArtifactCard filter = new FilterArtifactCard("artifact card with mana value 1 or less from your graveyard"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 2)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 2)); } public AuriokSalvagers(UUID ownerId, CardSetInfo setInfo) { @@ -37,7 +38,7 @@ public AuriokSalvagers(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(4); // {1}{W}: Return target artifact card with converted mana cost 1 or less from your graveyard to your hand. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnToHandTargetEffect(),new ManaCostsImpl("{1}{W}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnFromGraveyardToHandTargetEffect(),new ManaCostsImpl("{1}{W}")); ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/a/Aurochs.java b/Mage.Sets/src/mage/cards/a/Aurochs.java index d115b40dbc31..ec3c832c9c50 100644 --- a/Mage.Sets/src/mage/cards/a/Aurochs.java +++ b/Mage.Sets/src/mage/cards/a/Aurochs.java @@ -14,7 +14,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.common.FilterAttackingCreature; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/a/AurochsHerd.java b/Mage.Sets/src/mage/cards/a/AurochsHerd.java index fe3a6234ce64..baf2a98d7df8 100644 --- a/Mage.Sets/src/mage/cards/a/AurochsHerd.java +++ b/Mage.Sets/src/mage/cards/a/AurochsHerd.java @@ -17,7 +17,7 @@ import mage.constants.SubType; import mage.filter.FilterCard; import mage.filter.common.FilterAttackingCreature; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCardInLibrary; /** diff --git a/Mage.Sets/src/mage/cards/a/AuroraChampion.java b/Mage.Sets/src/mage/cards/a/AuroraChampion.java index c21ea6ea9360..c21eefb425cd 100644 --- a/Mage.Sets/src/mage/cards/a/AuroraChampion.java +++ b/Mage.Sets/src/mage/cards/a/AuroraChampion.java @@ -13,7 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.common.FilterTeamPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/a/AustereCommand.java b/Mage.Sets/src/mage/cards/a/AustereCommand.java index c470f5eff9cf..5f0a0950bf29 100644 --- a/Mage.Sets/src/mage/cards/a/AustereCommand.java +++ b/Mage.Sets/src/mage/cards/a/AustereCommand.java @@ -11,7 +11,7 @@ import mage.filter.common.FilterArtifactPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterEnchantmentPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; /** * @@ -19,11 +19,11 @@ */ public final class AustereCommand extends CardImpl { - private static final FilterCreaturePermanent filter3orLess = new FilterCreaturePermanent("creatures with converted mana cost 3 or less"); - private static final FilterCreaturePermanent filter4orMore = new FilterCreaturePermanent("creatures with converted mana cost 4 or greater"); + private static final FilterCreaturePermanent filter3orLess = new FilterCreaturePermanent("creatures with mana value 3 or less"); + private static final FilterCreaturePermanent filter4orMore = new FilterCreaturePermanent("creatures with mana value 4 or greater"); static { - filter3orLess.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); - filter4orMore.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 3)); + filter3orLess.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); + filter4orMore.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 3)); } public AustereCommand(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/a/AuthorOfShadows.java b/Mage.Sets/src/mage/cards/a/AuthorOfShadows.java new file mode 100644 index 000000000000..6ba8b90fba2f --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AuthorOfShadows.java @@ -0,0 +1,96 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.*; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInExile; +import mage.util.CardUtil; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AuthorOfShadows extends CardImpl { + + public AuthorOfShadows(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); + + this.subtype.add(SubType.SHADE); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When Author of Shadows enters the battlefield, exile all cards from all opponents' graveyards. Choose a nonland card exiled this way. You may cast that card for as long as it remains exiled, and you may spend mana as though it were mana of any color to cast that spell. + this.addAbility(new EntersBattlefieldTriggeredAbility(new AuthorOfShadowsEffect())); + } + + private AuthorOfShadows(final AuthorOfShadows card) { + super(card); + } + + @Override + public AuthorOfShadows copy() { + return new AuthorOfShadows(this); + } +} + +class AuthorOfShadowsEffect extends OneShotEffect { + + AuthorOfShadowsEffect() { + super(Outcome.Benefit); + staticText = "exile all cards from all opponents' graveyards. Choose a nonland card exiled this way. " + + "You may cast that card for as long as it remains exiled, and you may spend mana " + + "as though it were mana of any color to cast that spell"; + } + + private AuthorOfShadowsEffect(final AuthorOfShadowsEffect effect) { + super(effect); + } + + @Override + public AuthorOfShadowsEffect copy() { + return new AuthorOfShadowsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(); + game.getOpponents(source.getControllerId()) + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .map(Player::getGraveyard) + .forEach(cards::addAll); + cards.removeIf(Objects::isNull); + if (cards.isEmpty()) { + return false; + } + player.moveCards(cards, Zone.EXILED, source, game); + cards.retainZone(Zone.EXILED, game); + if (cards.isEmpty()) { + return false; + } + TargetCard target = new TargetCardInExile(StaticFilters.FILTER_CARD_A_NON_LAND); + target.setNotTarget(true); + player.choose(outcome, cards, target, game); + Card card = game.getCard(target.getFirstTarget()); + if (card == null) { + return false; + } + CardUtil.makeCardPlayable(game, source, card, Duration.Custom, true); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/a/AvacynGuardianAngel.java b/Mage.Sets/src/mage/cards/a/AvacynGuardianAngel.java index da550aa06756..5a21352b8c3c 100644 --- a/Mage.Sets/src/mage/cards/a/AvacynGuardianAngel.java +++ b/Mage.Sets/src/mage/cards/a/AvacynGuardianAngel.java @@ -1,7 +1,5 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.ObjectColor; @@ -17,15 +15,16 @@ import mage.choices.ChoiceColor; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import mage.target.common.TargetPlayerOrPlaneswalker; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class AvacynGuardianAngel extends CardImpl { @@ -80,7 +79,7 @@ class AvacynGuardianAngelPreventToCreatureEffect extends OneShotEffect { this.staticText = "Prevent all damage that would be dealt to another target creature this turn by sources of the color of your choice"; } - AvacynGuardianAngelPreventToCreatureEffect(final AvacynGuardianAngelPreventToCreatureEffect effect) { + private AvacynGuardianAngelPreventToCreatureEffect(final AvacynGuardianAngelPreventToCreatureEffect effect) { super(effect); } @@ -112,7 +111,7 @@ class AvacynGuardianAngelPreventToCreaturePreventionEffect extends PreventionEff this.color = color; } - AvacynGuardianAngelPreventToCreaturePreventionEffect(AvacynGuardianAngelPreventToCreaturePreventionEffect effect) { + private AvacynGuardianAngelPreventToCreaturePreventionEffect(AvacynGuardianAngelPreventToCreaturePreventionEffect effect) { super(effect); this.color = effect.color; } @@ -124,16 +123,13 @@ public boolean apply(Game game, Ability source) { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (super.applies(event, source, game)) { - if (event.getType() == GameEvent.EventType.DAMAGE_CREATURE - && event.getTargetId().equals(getTargetPointer().getFirst(game, source))) { - MageObject sourceObject = game.getObject(event.getSourceId()); - if (sourceObject != null && sourceObject.getColor(game).shares(this.color)) { - return true; - } - } + if (!super.applies(event, source, game) + || event.getType() != GameEvent.EventType.DAMAGE_PERMANENT + || !event.getTargetId().equals(getTargetPointer().getFirst(game, source))) { + return false; } - return false; + MageObject sourceObject = game.getObject(event.getSourceId()); + return sourceObject != null && sourceObject.getColor(game).shares(this.color); } @Override @@ -149,7 +145,7 @@ class AvacynGuardianAngelPreventToPlayerEffect extends OneShotEffect { this.staticText = "Prevent all damage that would be dealt to target player or planeswalker this turn by sources of the color of your choice"; } - AvacynGuardianAngelPreventToPlayerEffect(final AvacynGuardianAngelPreventToPlayerEffect effect) { + private AvacynGuardianAngelPreventToPlayerEffect(final AvacynGuardianAngelPreventToPlayerEffect effect) { super(effect); } @@ -181,7 +177,7 @@ class AvacynGuardianAngelPreventToPlayerPreventionEffect extends PreventionEffec this.color = color; } - AvacynGuardianAngelPreventToPlayerPreventionEffect(AvacynGuardianAngelPreventToPlayerPreventionEffect effect) { + private AvacynGuardianAngelPreventToPlayerPreventionEffect(AvacynGuardianAngelPreventToPlayerPreventionEffect effect) { super(effect); this.color = effect.color; } @@ -193,17 +189,11 @@ public boolean apply(Game game, Ability source) { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (super.applies(event, source, game)) { - if ((event.getType() == GameEvent.EventType.DAMAGE_PLAYER - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER) - && event.getTargetId().equals(getTargetPointer().getFirst(game, source))) { - MageObject sourceObject = game.getObject(event.getSourceId()); - if (sourceObject != null && sourceObject.getColor(game).shares(this.color)) { - return true; - } - } + if (!super.applies(event, source, game) || !event.getTargetId().equals(getTargetPointer().getFirst(game, source))) { + return false; } - return false; + MageObject sourceObject = game.getObject(event.getSourceId()); + return sourceObject != null && sourceObject.getColor(game).shares(this.color); } @Override diff --git a/Mage.Sets/src/mage/cards/a/AvacynThePurifier.java b/Mage.Sets/src/mage/cards/a/AvacynThePurifier.java index 6f9c4286b601..b9b085e33893 100644 --- a/Mage.Sets/src/mage/cards/a/AvacynThePurifier.java +++ b/Mage.Sets/src/mage/cards/a/AvacynThePurifier.java @@ -9,7 +9,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/a/AvatarOfGrowth.java b/Mage.Sets/src/mage/cards/a/AvatarOfGrowth.java index e65b1cf4eb64..0470a84d7e6e 100644 --- a/Mage.Sets/src/mage/cards/a/AvatarOfGrowth.java +++ b/Mage.Sets/src/mage/cards/a/AvatarOfGrowth.java @@ -63,7 +63,7 @@ class AvatarOfGrowthSearchEffect extends OneShotEffect { public AvatarOfGrowthSearchEffect() { super(Outcome.Detriment); - this.staticText = "each player searches their library for up to two basic land cards, puts them onto the battlefield, then shuffles their libarary"; + this.staticText = "each player searches their library for up to two basic land cards, puts them onto the battlefield, then shuffles"; } public AvatarOfGrowthSearchEffect(final AvatarOfGrowthSearchEffect effect) { diff --git a/Mage.Sets/src/mage/cards/a/AvatarOfMight.java b/Mage.Sets/src/mage/cards/a/AvatarOfMight.java index b74597a7a8bc..efe76affc176 100644 --- a/Mage.Sets/src/mage/cards/a/AvatarOfMight.java +++ b/Mage.Sets/src/mage/cards/a/AvatarOfMight.java @@ -33,7 +33,7 @@ public AvatarOfMight(UUID ownerId, CardSetInfo setInfo) { // If an opponent controls at least four more creatures than you, Avatar of Might costs {6} less to cast. this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(6, condition) - .setText("If an opponent controls at least four more creatures than you, {this} costs {6} less to cast")) + .setText("If an opponent controls at least four more creatures than you, this spell costs {6} less to cast")) .addHint(new ConditionHint(condition, "Opponent controls at least four more creatures than you")) ); diff --git a/Mage.Sets/src/mage/cards/a/AvatarOfTheResolute.java b/Mage.Sets/src/mage/cards/a/AvatarOfTheResolute.java index fe1c0cb6f544..d3d44beea71d 100644 --- a/Mage.Sets/src/mage/cards/a/AvatarOfTheResolute.java +++ b/Mage.Sets/src/mage/cards/a/AvatarOfTheResolute.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/a/AvatarOfWoe.java b/Mage.Sets/src/mage/cards/a/AvatarOfWoe.java index ec9159d3dab9..47e4137ed232 100644 --- a/Mage.Sets/src/mage/cards/a/AvatarOfWoe.java +++ b/Mage.Sets/src/mage/cards/a/AvatarOfWoe.java @@ -39,7 +39,7 @@ public AvatarOfWoe(UUID ownerId, CardSetInfo setInfo) { // If there are ten or more creature cards total in all graveyards, Avatar of Woe costs {6} less to cast. this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(6, condition) - .setText("If there are ten or more creature cards total in all graveyards, {this} costs {6} less to cast")) + .setText("If there are ten or more creature cards total in all graveyards, this spell costs {6} less to cast")) .addHint(new ValueHint("Creature cards in all graveyards", graveyardCount)) ); diff --git a/Mage.Sets/src/mage/cards/a/AvenSoulgazer.java b/Mage.Sets/src/mage/cards/a/AvenSoulgazer.java index 0e5bd76ccfd0..1fae3a76be9b 100644 --- a/Mage.Sets/src/mage/cards/a/AvenSoulgazer.java +++ b/Mage.Sets/src/mage/cards/a/AvenSoulgazer.java @@ -18,7 +18,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.other.FaceDownPredicate; +import mage.filter.predicate.card.FaceDownPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/a/AvenWindreader.java b/Mage.Sets/src/mage/cards/a/AvenWindreader.java index 495e0e2eddb5..31418007ea5a 100644 --- a/Mage.Sets/src/mage/cards/a/AvenWindreader.java +++ b/Mage.Sets/src/mage/cards/a/AvenWindreader.java @@ -1,28 +1,30 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.common.RevealTargetPlayerLibraryEffect; +import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; 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.game.Game; +import mage.players.Player; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * - * @author cbt33 + * @author TheElk801 */ public final class AvenWindreader extends CardImpl { public AvenWindreader(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); this.subtype.add(SubType.BIRD); this.subtype.add(SubType.SOLDIER); this.subtype.add(SubType.WIZARD); @@ -34,7 +36,7 @@ public AvenWindreader(UUID ownerId, CardSetInfo setInfo) { this.addAbility(FlyingAbility.getInstance()); // {1}{U}: Target player reveals the top card of their library. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new RevealTargetPlayerLibraryEffect(1), new ManaCostsImpl("{1}{U}")); + Ability ability = new SimpleActivatedAbility(new AvenWindreaderEffect(), new ManaCostsImpl("{1}{U}")); ability.addTarget(new TargetPlayer()); this.addAbility(ability); } @@ -48,3 +50,30 @@ public AvenWindreader copy() { return new AvenWindreader(this); } } + +class AvenWindreaderEffect extends OneShotEffect { + + AvenWindreaderEffect() { + super(Outcome.Benefit); + staticText = "target player reveals the top card of their library"; + } + + private AvenWindreaderEffect(final AvenWindreaderEffect effect) { + super(effect); + } + + @Override + public AvenWindreaderEffect copy() { + return new AvenWindreaderEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); + if (player == null) { + return false; + } + player.revealCards(source, new CardsImpl(player.getLibrary().getFromTop(game)), game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/a/AvengingArrow.java b/Mage.Sets/src/mage/cards/a/AvengingArrow.java index a3a667d2ff54..236599d79ab6 100644 --- a/Mage.Sets/src/mage/cards/a/AvengingArrow.java +++ b/Mage.Sets/src/mage/cards/a/AvengingArrow.java @@ -1,32 +1,33 @@ - package mage.cards.a; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.StaticFilters; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.watchers.common.SourceDidDamageWatcher; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class AvengingArrow extends CardImpl { + private static final FilterPermanent filter + = new FilterCreaturePermanent("creature that dealt damage this turn"); + public AvengingArrow(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}"); // Destroy target creature that dealt damage this turn. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new AvengingArrowTarget()); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); this.getSpellAbility().addWatcher(new SourceDidDamageWatcher()); } @@ -40,46 +41,12 @@ public AvengingArrow copy() { } } -class AvengingArrowTarget extends TargetPermanent { - - public AvengingArrowTarget() { - super(1, 1, StaticFilters.FILTER_PERMANENT_CREATURE, false); - targetName = "creature that dealt damage this turn"; - } - - public AvengingArrowTarget(final AvengingArrowTarget target) { - super(target); - } - - @Override - public boolean canTarget(UUID id, Ability source, Game game) { - SourceDidDamageWatcher watcher = game.getState().getWatcher(SourceDidDamageWatcher.class); - if (watcher != null) { - if (watcher.damageSources.contains(id)) { - return super.canTarget(id, source, game); - } - } - return false; - } +enum AvengingArrowPredicate implements Predicate { + instance; @Override - public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { - Set availablePossibleTargets = super.possibleTargets(sourceId, sourceControllerId, game); - Set possibleTargets = new HashSet<>(); + public boolean apply(Permanent input, Game game) { SourceDidDamageWatcher watcher = game.getState().getWatcher(SourceDidDamageWatcher.class); - if (watcher != null) { - for (UUID targetId : availablePossibleTargets) { - Permanent permanent = game.getPermanent(targetId); - if (permanent != null && watcher.damageSources.contains(targetId)) { - possibleTargets.add(targetId); - } - } - } - return possibleTargets; - } - - @Override - public AvengingArrowTarget copy() { - return new AvengingArrowTarget(this); + return watcher != null && watcher.checkSource(input, game); } } diff --git a/Mage.Sets/src/mage/cards/a/AvengingHuntbonder.java b/Mage.Sets/src/mage/cards/a/AvengingHuntbonder.java index 0867621cbf06..4df34e8f44a1 100644 --- a/Mage.Sets/src/mage/cards/a/AvengingHuntbonder.java +++ b/Mage.Sets/src/mage/cards/a/AvengingHuntbonder.java @@ -12,7 +12,7 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterAttackingCreature; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/a/AviaryMechanic.java b/Mage.Sets/src/mage/cards/a/AviaryMechanic.java index 060ce4995108..757a1604ba4e 100644 --- a/Mage.Sets/src/mage/cards/a/AviaryMechanic.java +++ b/Mage.Sets/src/mage/cards/a/AviaryMechanic.java @@ -10,7 +10,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/a/AvoidFate.java b/Mage.Sets/src/mage/cards/a/AvoidFate.java index 74f133c57db8..e2eca8f029df 100644 --- a/Mage.Sets/src/mage/cards/a/AvoidFate.java +++ b/Mage.Sets/src/mage/cards/a/AvoidFate.java @@ -9,7 +9,7 @@ import mage.filter.FilterSpell; import mage.filter.StaticFilters; import mage.filter.predicate.Predicates; -import mage.filter.predicate.other.TargetsPermanentPredicate; +import mage.filter.predicate.mageobject.TargetsPermanentPredicate; import mage.target.TargetSpell; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/a/AxgardArmory.java b/Mage.Sets/src/mage/cards/a/AxgardArmory.java index aedbb8822138..a275274dc549 100644 --- a/Mage.Sets/src/mage/cards/a/AxgardArmory.java +++ b/Mage.Sets/src/mage/cards/a/AxgardArmory.java @@ -37,7 +37,7 @@ public AxgardArmory(UUID ownerId, CardSetInfo setInfo) { Ability ability = new SimpleActivatedAbility( new SearchLibraryPutInHandEffect(new AxgardArmoryTarget(), true) .setText("search your library for an Aura card and/or an Equipment card, reveal them, " + - "put them into your hand, then shuffle your library"), + "put them into your hand, then shuffle"), new ManaCostsImpl("{1}{R}{R}{W}") ); ability.addCost(new TapSourceCost()); diff --git a/Mage.Sets/src/mage/cards/a/AxisOfMortality.java b/Mage.Sets/src/mage/cards/a/AxisOfMortality.java index 79df1aedac30..874f47c66f1d 100644 --- a/Mage.Sets/src/mage/cards/a/AxisOfMortality.java +++ b/Mage.Sets/src/mage/cards/a/AxisOfMortality.java @@ -1,21 +1,17 @@ - package mage.cards.a; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExchangeLifeTwoTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.TargetController; -import mage.game.Game; -import mage.players.Player; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class AxisOfMortality extends CardImpl { @@ -24,9 +20,10 @@ public AxisOfMortality(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{W}{W}"); // At the beginning of your upkeep, you may have two target players exchange life totals. - Ability ability = new BeginningOfUpkeepTriggeredAbility(new AxisOfMortalityEffect(), TargetController.YOU, true); - ability.addTarget(new TargetPlayer()); - ability.addTarget(new TargetPlayer()); + Ability ability = new BeginningOfUpkeepTriggeredAbility( + new ExchangeLifeTwoTargetEffect(), TargetController.YOU, true + ); + ability.addTarget(new TargetPlayer(2)); this.addAbility(ability); } @@ -39,52 +36,3 @@ public AxisOfMortality copy() { return new AxisOfMortality(this); } } - -class AxisOfMortalityEffect extends OneShotEffect { - - public AxisOfMortalityEffect() { - super(Outcome.Neutral); - this.staticText = "two target players exchange life totals"; - } - - public AxisOfMortalityEffect(final AxisOfMortalityEffect effect) { - super(effect); - } - - @Override - public AxisOfMortalityEffect copy() { - return new AxisOfMortalityEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player1 = game.getPlayer(source.getFirstTarget()); - Player player2 = game.getPlayer(source.getTargets().get(1).getFirstTarget()); - if (player1 != null && player2 != null) { - int lifePlayer1 = player1.getLife(); - int lifePlayer2 = player2.getLife(); - - if (lifePlayer1 == lifePlayer2) { - return false; - } - - if (!player1.isLifeTotalCanChange() || !player2.isLifeTotalCanChange()) { - return false; - } - - // 20110930 - 118.7, 118.8 - if (lifePlayer1 < lifePlayer2 && (!player1.isCanGainLife() || !player2.isCanLoseLife())) { - return false; - } - - if (lifePlayer1 > lifePlayer2 && (!player1.isCanLoseLife() || !player2.isCanGainLife())) { - return false; - } - - player1.setLife(lifePlayer2, game, source); - player2.setLife(lifePlayer1, game, source); - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/a/AyaraFirstOfLocthwain.java b/Mage.Sets/src/mage/cards/a/AyaraFirstOfLocthwain.java index 0e45252d447c..73f22405a662 100644 --- a/Mage.Sets/src/mage/cards/a/AyaraFirstOfLocthwain.java +++ b/Mage.Sets/src/mage/cards/a/AyaraFirstOfLocthwain.java @@ -20,7 +20,7 @@ import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/a/AyeshaTanaka.java b/Mage.Sets/src/mage/cards/a/AyeshaTanaka.java index c034861bab02..02ab49e6904b 100644 --- a/Mage.Sets/src/mage/cards/a/AyeshaTanaka.java +++ b/Mage.Sets/src/mage/cards/a/AyeshaTanaka.java @@ -16,7 +16,7 @@ import mage.constants.SuperType; import mage.constants.Zone; import mage.filter.FilterStackObject; -import mage.filter.predicate.ability.ArtifactSourcePredicate; +import mage.filter.predicate.other.ArtifactSourcePredicate; import mage.target.common.TargetActivatedAbility; /** diff --git a/Mage.Sets/src/mage/cards/a/AyliEternalPilgrim.java b/Mage.Sets/src/mage/cards/a/AyliEternalPilgrim.java index 3a67ffe57797..1caf9edea151 100644 --- a/Mage.Sets/src/mage/cards/a/AyliEternalPilgrim.java +++ b/Mage.Sets/src/mage/cards/a/AyliEternalPilgrim.java @@ -57,7 +57,7 @@ public AyliEternalPilgrim(UUID ownerId, CardSetInfo setInfo) { new ExileTargetEffect(), new ManaCostsImpl("{1}{W}{B}"), new AyliEternalPilgrimCondition(), - "{1}{W}{B}, Sacrifice another creature: Exile target nonland permanent. Activate this ability only if you have at least 10 life more than your starting life total"); + "{1}{W}{B}, Sacrifice another creature: Exile target nonland permanent. Activate only if you have at least 10 life more than your starting life total"); ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE))); ability.addTarget(new TargetNonlandPermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/a/AyulaQueenAmongBears.java b/Mage.Sets/src/mage/cards/a/AyulaQueenAmongBears.java index 1b08f1aa6e86..cbbd241f965f 100644 --- a/Mage.Sets/src/mage/cards/a/AyulaQueenAmongBears.java +++ b/Mage.Sets/src/mage/cards/a/AyulaQueenAmongBears.java @@ -15,7 +15,7 @@ import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import mage.target.common.TargetControlledPermanent; diff --git a/Mage.Sets/src/mage/cards/a/AzorTheLawbringer.java b/Mage.Sets/src/mage/cards/a/AzorTheLawbringer.java index 507efd9d484b..8326a5c688c6 100644 --- a/Mage.Sets/src/mage/cards/a/AzorTheLawbringer.java +++ b/Mage.Sets/src/mage/cards/a/AzorTheLawbringer.java @@ -131,7 +131,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { if (opponentId.equals(event.getPlayerId())) { MageObject object = game.getObject(event.getSourceId()); if (event.getType() == GameEvent.EventType.CAST_SPELL) { - if (object.isInstant() || object.isSorcery()) { + if (object.isInstantOrSorcery()) { return true; } } diff --git a/Mage.Sets/src/mage/cards/a/AzorsGateway.java b/Mage.Sets/src/mage/cards/a/AzorsGateway.java index 43f91bd90080..a8d4649e8b28 100644 --- a/Mage.Sets/src/mage/cards/a/AzorsGateway.java +++ b/Mage.Sets/src/mage/cards/a/AzorsGateway.java @@ -60,7 +60,7 @@ class AzorsGatewayEffect extends OneShotEffect { public AzorsGatewayEffect() { super(Outcome.Benefit); - this.staticText = "Draw a card, then exile a card from your hand. If cards with five or more different converted mana costs are exiled with {this}, you gain 5 life, untap Azor's Gateway, and transform it"; + this.staticText = "Draw a card, then exile a card from your hand. If cards with five or more different mana values are exiled with {this}, you gain 5 life, untap Azor's Gateway, and transform it"; } public AzorsGatewayEffect(final AzorsGatewayEffect effect) { @@ -89,7 +89,7 @@ public boolean apply(Game game, Ability source) { ExileZone exileZone = game.getExile().getExileZone(exileId); if (exileZone != null) { for (Card card : exileZone.getCards(game)) { - usedCMC.add(card.getConvertedManaCost()); + usedCMC.add(card.getManaValue()); } if (usedCMC.size() > 4) { controller.gainLife(4, game, source); diff --git a/Mage.Sets/src/mage/cards/b/BINGO.java b/Mage.Sets/src/mage/cards/b/BINGO.java index c25b26db8f4e..c02556c19038 100644 --- a/Mage.Sets/src/mage/cards/b/BINGO.java +++ b/Mage.Sets/src/mage/cards/b/BINGO.java @@ -26,6 +26,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.Spell; @@ -47,7 +48,7 @@ public BINGO(UUID ownerId, CardSetInfo setInfo) { this.addAbility(TrampleAbility.getInstance()); // Whenever a player casts a spell, put a chip counter on its converted mana cost. - this.addAbility(new SpellCastAllTriggeredAbility(new BingoEffect(), new FilterSpell("a spell"), false, SetTargetPointer.SPELL)); + this.addAbility(new SpellCastAllTriggeredAbility(new BingoEffect(), StaticFilters.FILTER_SPELL_A, false, SetTargetPointer.SPELL)); // B-I-N-G-O gets +9/+9 for each set of three numbers in a row with chip counters on them. BingoCount count = new BingoCount(); @@ -68,7 +69,7 @@ class BingoEffect extends OneShotEffect { public BingoEffect() { super(Outcome.Neutral); - staticText = "put a chip counter on its converted mana cost"; + staticText = "put a chip counter on its mana value"; } public BingoEffect(final BingoEffect effect) { @@ -79,7 +80,7 @@ public BingoEffect(final BingoEffect effect) { public boolean apply(Game game, Ability source) { Spell spell = game.getStack().getSpell(this.getTargetPointer().getFirst(game, source)); if (spell != null) { - if (spell.getConvertedManaCost() > 9) { + if (spell.getManaValue() > 9) { return true; } MageObject mageObject = game.getObject(source.getSourceId()); @@ -88,8 +89,8 @@ public boolean apply(Game game, Ability source) { if (game.getState().getValue(mageObject.getId() + "_chip") != null) { chipCounters.putAll((Map) game.getState().getValue(mageObject.getId() + "_chip")); } - chipCounters.putIfAbsent(spell.getConvertedManaCost(), 0); - chipCounters.put(spell.getConvertedManaCost(), chipCounters.get(spell.getConvertedManaCost()) + 1); + chipCounters.putIfAbsent(spell.getManaValue(), 0); + chipCounters.put(spell.getManaValue(), chipCounters.get(spell.getManaValue()) + 1); game.getState().setValue(mageObject.getId() + "_chip", chipCounters); if (mageObject instanceof Permanent) { StringBuilder sb = new StringBuilder(); diff --git a/Mage.Sets/src/mage/cards/b/BackdraftHellkite.java b/Mage.Sets/src/mage/cards/b/BackdraftHellkite.java index 80475b21aaac..b07ad1658610 100644 --- a/Mage.Sets/src/mage/cards/b/BackdraftHellkite.java +++ b/Mage.Sets/src/mage/cards/b/BackdraftHellkite.java @@ -1,6 +1,7 @@ package mage.cards.b; import mage.MageInt; +import mage.MageObject; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; @@ -75,7 +76,7 @@ public void init(Ability source, Game game) { player.getGraveyard() .stream() .map((cardId) -> game.getCard(cardId)) - .filter(card -> card.isInstant() || card.isSorcery()) + .filter(MageObject::isInstantOrSorcery) .forEachOrdered(card -> affectedObjectList.add(new MageObjectReference(card, game))); } diff --git a/Mage.Sets/src/mage/cards/b/BadRiver.java b/Mage.Sets/src/mage/cards/b/BadRiver.java index 12cfa364aa66..0c2b71beefe2 100644 --- a/Mage.Sets/src/mage/cards/b/BadRiver.java +++ b/Mage.Sets/src/mage/cards/b/BadRiver.java @@ -22,7 +22,7 @@ public BadRiver(UUID ownerId, CardSetInfo setInfo) { // Bad River enters the battlefield tapped. this.addAbility(new EntersBattlefieldTappedAbility()); // {tap}, Sacrifice Bad River: Search your library for an Island or Swamp card and put it onto the battlefield. Then shuffle your library. - this.addAbility(new FetchLandActivatedAbility(false, EnumSet.of(SubType.ISLAND, SubType.SWAMP))); + this.addAbility(new FetchLandActivatedAbility(false, SubType.ISLAND, SubType.SWAMP)); } diff --git a/Mage.Sets/src/mage/cards/b/BafflingEnd.java b/Mage.Sets/src/mage/cards/b/BafflingEnd.java index 7011b3943705..057d7b54d520 100644 --- a/Mage.Sets/src/mage/cards/b/BafflingEnd.java +++ b/Mage.Sets/src/mage/cards/b/BafflingEnd.java @@ -13,7 +13,7 @@ import mage.constants.ComparisonType; import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.permanent.token.DinosaurToken; import mage.target.Target; import mage.target.TargetPermanent; @@ -25,10 +25,10 @@ */ public final class BafflingEnd extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with converted mana cost 3 or less an opponent controls"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with mana value 3 or less an opponent controls"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); filter.add(TargetController.OPPONENT.getControllerPredicate()); } diff --git a/Mage.Sets/src/mage/cards/b/BairdStewardOfArgive.java b/Mage.Sets/src/mage/cards/b/BairdStewardOfArgive.java index 6abfde6e10fc..c1ac8ccec20b 100644 --- a/Mage.Sets/src/mage/cards/b/BairdStewardOfArgive.java +++ b/Mage.Sets/src/mage/cards/b/BairdStewardOfArgive.java @@ -33,7 +33,7 @@ public BairdStewardOfArgive(UUID ownerId, CardSetInfo setInfo) { // Creatures can't attack you or a planeswalker you control unless their controller pays {1} for each of those creatures. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackYouUnlessPayManaAllEffect(new ManaCostsImpl("{1}"), true) - .setText("Creatures can't attack you or a planeswalker you control unless their controller pays {1} for each of those creatures"))); + .setText("Creatures can't attack you or planeswalkers you control unless their controller pays {1} for each of those creatures"))); } private BairdStewardOfArgive(final BairdStewardOfArgive card) { diff --git a/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java b/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java index a13d998e5cac..741bac471cf7 100644 --- a/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java +++ b/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java @@ -60,7 +60,7 @@ class BalduvianWarlordUnblockEffect extends OneShotEffect { BalduvianWarlordUnblockEffect() { super(Outcome.Benefit); - this.staticText = " Remove target blocking creature from combat. Creatures it was blocking that hadn't become blocked by another creature this combat become unblocked, then it blocks an attacking creature of your choice"; + this.staticText = "Remove target blocking creature from combat. Creatures it was blocking that hadn't become blocked by another creature this combat become unblocked, then it blocks an attacking creature of your choice"; } private BalduvianWarlordUnblockEffect(final BalduvianWarlordUnblockEffect effect) { diff --git a/Mage.Sets/src/mage/cards/b/BalefulMastery.java b/Mage.Sets/src/mage/cards/b/BalefulMastery.java new file mode 100644 index 000000000000..5c92ae781df1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BalefulMastery.java @@ -0,0 +1,88 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.costs.AlternativeCostSourceAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCreatureOrPlaneswalker; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author htrajan + */ +public final class BalefulMastery extends CardImpl { + + public BalefulMastery(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{B}"); + + // You may pay {1}{B} rather than pay this spell's mana cost. + Ability costAbility = new AlternativeCostSourceAbility(new ManaCostsImpl<>("{1}{B}")); + this.addAbility(costAbility); + + // If the {1}{B} cost was paid, an opponent draws a card. + this.getSpellAbility().addEffect(new BalefulMasteryAlternativeCostEffect(costAbility.getOriginalId())); + + // Exile target creature or planeswalker. + this.getSpellAbility().addEffect(new ExileTargetEffect()); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); + } + + private BalefulMastery(final BalefulMastery card) { + super(card); + } + + @Override + public BalefulMastery copy() { + return new BalefulMastery(this); + } +} + +class BalefulMasteryAlternativeCostEffect extends OneShotEffect { + + private final UUID alternativeCostOriginalID; + + BalefulMasteryAlternativeCostEffect(UUID alternativeCostOriginalID) { + super(Outcome.Detriment); + staticText = "if the {1}{B} cost was paid, an opponent draws a card.
"; + this.alternativeCostOriginalID = alternativeCostOriginalID; + } + + private BalefulMasteryAlternativeCostEffect(BalefulMasteryAlternativeCostEffect effect) { + super(effect); + this.alternativeCostOriginalID = effect.alternativeCostOriginalID; + } + + @Override + public BalefulMasteryAlternativeCostEffect copy() { + return new BalefulMasteryAlternativeCostEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (!AlternativeCostSourceAbility.getActivatedStatus( + game, source, this.alternativeCostOriginalID, false + )) { + return false; + } + + Player player = game.getPlayer(source.getControllerId()); + TargetOpponent targetOpponent = new TargetOpponent(true); + if (player.chooseTarget(Outcome.DrawCard, targetOpponent, source, game)) { + Player opponent = game.getPlayer(targetOpponent.getFirstTarget()); + if (opponent != null) { + opponent.drawCards(1, source, game); + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/b/BallotBroker.java b/Mage.Sets/src/mage/cards/b/BallotBroker.java new file mode 100644 index 000000000000..928edf1a1cac --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BallotBroker.java @@ -0,0 +1,77 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.VoteEvent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BallotBroker extends CardImpl { + + public BallotBroker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ADVISOR); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // While voting, you may vote an additional time. + this.addAbility(new SimpleStaticAbility(new BallotBrokerReplacementEffect())); + } + + private BallotBroker(final BallotBroker card) { + super(card); + } + + @Override + public BallotBroker copy() { + return new BallotBroker(this); + } +} + +class BallotBrokerReplacementEffect extends ReplacementEffectImpl { + + BallotBrokerReplacementEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "while voting, you may vote an additional time"; + } + + private BallotBrokerReplacementEffect(final BallotBrokerReplacementEffect effect) { + super(effect); + } + + @Override + public BallotBrokerReplacementEffect copy() { + return new BallotBrokerReplacementEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.VOTE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.isControlledBy(event.getTargetId()); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + ((VoteEvent) event).incrementOptionalExtraVotes(); + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BallynockCohort.java b/Mage.Sets/src/mage/cards/b/BallynockCohort.java index 2b4f28122f31..a7afdb28be32 100644 --- a/Mage.Sets/src/mage/cards/b/BallynockCohort.java +++ b/Mage.Sets/src/mage/cards/b/BallynockCohort.java @@ -18,7 +18,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/b/BalshanBeguiler.java b/Mage.Sets/src/mage/cards/b/BalshanBeguiler.java index c1cff65d59cb..1b26b36a4570 100644 --- a/Mage.Sets/src/mage/cards/b/BalshanBeguiler.java +++ b/Mage.Sets/src/mage/cards/b/BalshanBeguiler.java @@ -1,7 +1,5 @@ - package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; @@ -14,26 +12,29 @@ import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.FilterCard; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; /** - * * @author cbt33, noxx (DiscardCardYouChooseTargetOpponentEffect) */ public final class BalshanBeguiler extends CardImpl { public BalshanBeguiler(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); this.subtype.add(SubType.HUMAN, SubType.WIZARD); this.power = new MageInt(1); this.toughness = new MageInt(1); // Whenever Balshan Beguiler deals combat damage to a player, that player reveals the top two cards of their library. You choose one of those cards and put it into their graveyard. - this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new BalshanBeguilerEffect(), false, true)); + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( + new BalshanBeguilerEffect(), false, true + )); } private BalshanBeguiler(final BalshanBeguiler card) { @@ -47,36 +48,40 @@ public BalshanBeguiler copy() { } class BalshanBeguilerEffect extends OneShotEffect { - - public BalshanBeguilerEffect() { + + BalshanBeguilerEffect() { super(Outcome.Benefit); - this.staticText = " that player reveals the top two cards of their library. You choose one of those cards and put it into their graveyard."; + this.staticText = "that player reveals the top two cards of their library. " + + "You choose one of those cards and put it into their graveyard."; } - - public BalshanBeguilerEffect(final BalshanBeguilerEffect effect) { + + private BalshanBeguilerEffect(final BalshanBeguilerEffect effect) { super(effect); } - + @Override public BalshanBeguilerEffect copy() { return new BalshanBeguilerEffect(this); } - + @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getFirstTarget()); - if (player != null) { - CardsImpl cards = new CardsImpl(); - cards.addAll(player.getLibrary().getTopCards(game, 2)); - Player you = game.getPlayer(source.getControllerId()); - if (you != null) { - TargetCard target = new TargetCard(Zone.LIBRARY, new FilterCard()); - if (you.choose(Outcome.Benefit, cards, target, game)) { - Card card = player.getLibrary().getCard(target.getFirstTarget(), game); - card.moveToZone(Zone.BATTLEFIELD, source, game, true); - } - } + Player player = game.getPlayer(targetPointer.getFirst(game, source)); + Player you = game.getPlayer(source.getControllerId()); + if (player == null || you == null) { + return false; + } + CardsImpl cards = new CardsImpl(); + cards.addAll(player.getLibrary().getTopCards(game, 2)); + if (cards.isEmpty()) { + return false; + } + player.revealCards(source, cards, game); + TargetCard target = new TargetCardInLibrary(); + if (you.choose(Outcome.Benefit, cards, target, game)) { + Card card = player.getLibrary().getCard(target.getFirstTarget(), game); + you.moveCards(card, Zone.GRAVEYARD, source, game); } - return false; + return true; } } diff --git a/Mage.Sets/src/mage/cards/b/BalthorTheStout.java b/Mage.Sets/src/mage/cards/b/BalthorTheStout.java index 0ed999b14a3b..f92050b1e38c 100644 --- a/Mage.Sets/src/mage/cards/b/BalthorTheStout.java +++ b/Mage.Sets/src/mage/cards/b/BalthorTheStout.java @@ -13,7 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/b/BandTogether.java b/Mage.Sets/src/mage/cards/b/BandTogether.java index 1c61c35ee32a..cab380356ca9 100644 --- a/Mage.Sets/src/mage/cards/b/BandTogether.java +++ b/Mage.Sets/src/mage/cards/b/BandTogether.java @@ -9,7 +9,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.Target; diff --git a/Mage.Sets/src/mage/cards/b/BaneAlleyBroker.java b/Mage.Sets/src/mage/cards/b/BaneAlleyBroker.java index 43bafd5abc15..32ee4804fbee 100644 --- a/Mage.Sets/src/mage/cards/b/BaneAlleyBroker.java +++ b/Mage.Sets/src/mage/cards/b/BaneAlleyBroker.java @@ -1,9 +1,5 @@ - package mage.cards.b; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -13,44 +9,46 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.game.ExileZone; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.Target; +import mage.target.TargetCard; import mage.target.common.TargetCardInExile; import mage.target.common.TargetCardInHand; import mage.util.CardUtil; +import java.util.UUID; + /** * Gatecrash FAQ (01.2013) - * + *

* If Bane Alley Broker's first ability resolves when you have no cards in your * hand, you'll draw a card and then exile it. You won't have the opportunity to * cast that card (or do anything else with it) before exiling it. - * + *

* Due to a recent rules change, once you are allowed to look at a face-down * card in exile, you are allowed to look at that card as long as it's exiled. * If you no longer control Bane Alley Broker when its last ability resolves, * you can continue to look at the relevant cards in exile to choose one to * return. - * + *

* Bane Alley Broker's second and third abilities apply to cards exiled with * that specific Bane Alley Broker, not any other creature named Bane Alley * Broker. You should keep cards exiled by different Bane Alley Brokers * separate. - * + *

* If Bane Alley Broker leaves the battlefield, the cards exiled with it will be * exiled indefinitely. If it later returns to the battlefield, it will be a new * object with no connection to the cards exiled with it in its previous * existence. You won't be able to use the "new" Bane Alley Broker to return * cards exiled with the "old" one. - * + *

* Even if not all players can look at the exiled cards, each card's owner is * still known. It is advisable to keep cards owned by different players in * distinct piles in case another player gains control of Bane Alley Broker and @@ -61,24 +59,22 @@ public final class BaneAlleyBroker extends CardImpl { public BaneAlleyBroker(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{B}"); this.subtype.add(SubType.HUMAN, SubType.ROGUE); this.power = new MageInt(0); this.toughness = new MageInt(3); // {tap}: Draw a card, then exile a card from your hand face down. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BaneAlleyBrokerDrawExileEffect(), new TapSourceCost())); + this.addAbility(new SimpleActivatedAbility(new BaneAlleyBrokerDrawExileEffect(), new TapSourceCost())); // You may look at cards exiled with Bane Alley Broker. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new BaneAlleyBrokerLookAtCardEffect())); + this.addAbility(new SimpleStaticAbility(new BaneAlleyBrokerLookAtCardEffect())); // {U}{B}, {tap}: Return a card exiled with Bane Alley Broker to its owner's hand. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnToHandTargetEffect(), new ManaCostsImpl("{U}{B}")); + Ability ability = new SimpleActivatedAbility(new BaneAlleyBrokerReturnToHandEffect(), new ManaCostsImpl("{U}{B}")); ability.addCost(new TapSourceCost()); - ability.addTarget(new TargetCardInBaneAlleyBrokerExile(this.getId())); this.addAbility(ability); - } private BaneAlleyBroker(final BaneAlleyBroker card) { @@ -93,33 +89,39 @@ public BaneAlleyBroker copy() { class BaneAlleyBrokerDrawExileEffect extends OneShotEffect { - public BaneAlleyBrokerDrawExileEffect() { + BaneAlleyBrokerDrawExileEffect() { super(Outcome.DrawCard); staticText = "Draw a card, then exile a card from your hand face down"; } - public BaneAlleyBrokerDrawExileEffect(final BaneAlleyBrokerDrawExileEffect effect) { + private BaneAlleyBrokerDrawExileEffect(final BaneAlleyBrokerDrawExileEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - controller.drawCards(1, source, game); - Target target = new TargetCardInHand(new FilterCard("card to exile")); - if (controller.chooseTarget(outcome, target, source, game)) { - Card card = game.getCard(target.getFirstTarget()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (card != null && sourceObject != null) { - if (card.moveToExile(CardUtil.getCardExileZoneId(game, source), sourceObject.getName(), source, game)) { - card.setFaceDown(true, game); - return true; - } - } - } + if (controller == null) { + return false; + } + controller.drawCards(1, source, game); + if (controller.getHand().isEmpty()) { + return false; + } + TargetCard target = new TargetCardInHand().withChooseHint("to exile"); + controller.chooseTarget(outcome, controller.getHand(), target, source, game); + Card card = game.getCard(target.getFirstTarget()); + MageObject sourceObject = source.getSourcePermanentOrLKI(game); + if (card == null || sourceObject == null) { + return false; } - return false; + if (!controller.moveCardsToExile( + card, source, game, false, CardUtil.getExileZoneId(game, source), sourceObject.getIdName() + )) { + return false; + } + card.setFaceDown(true, game); + return true; } @Override @@ -128,74 +130,52 @@ public BaneAlleyBrokerDrawExileEffect copy() { } } -class TargetCardInBaneAlleyBrokerExile extends TargetCardInExile { +class BaneAlleyBrokerReturnToHandEffect extends OneShotEffect { - public TargetCardInBaneAlleyBrokerExile(UUID cardId) { - super(1, 1, new FilterCard("card exiled with Bane Alley Broker"), null); + BaneAlleyBrokerReturnToHandEffect() { + super(Outcome.Benefit); + staticText = "return a card exiled with {this} to its owner's hand"; } - public TargetCardInBaneAlleyBrokerExile(final TargetCardInBaneAlleyBrokerExile target) { - super(target); + private BaneAlleyBrokerReturnToHandEffect(final BaneAlleyBrokerReturnToHandEffect effect) { + super(effect); } @Override - public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { - Set possibleTargets = new HashSet<>(); - Card sourceCard = game.getCard(sourceId); - if (sourceCard != null) { - UUID exileId = CardUtil.getCardExileZoneId(game, sourceId); - ExileZone exile = game.getExile().getExileZone(exileId); - if (exile != null && !exile.isEmpty()) { - possibleTargets.addAll(exile); - } - } - return possibleTargets; + public BaneAlleyBrokerReturnToHandEffect copy() { + return new BaneAlleyBrokerReturnToHandEffect(this); } @Override - public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { - Card sourceCard = game.getCard(sourceId); - if (sourceCard != null) { - UUID exileId = CardUtil.getCardExileZoneId(game, sourceId); - ExileZone exile = game.getExile().getExileZone(exileId); - if (exile != null && !exile.isEmpty()) { - return true; - } + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = source.getSourcePermanentOrLKI(game); + if (player == null || permanent == null) { + return false; } - return false; - } - - @Override - public boolean canTarget(UUID id, Ability source, Game game) { - Card card = game.getCard(id); - if (card != null && game.getState().getZone(card.getId()) == Zone.EXILED) { - ExileZone exile = null; - Card sourceCard = game.getCard(source.getSourceId()); - if (sourceCard != null) { - UUID exileId = CardUtil.getCardExileZoneId(game, source); - exile = game.getExile().getExileZone(exileId); - } - if (exile != null && exile.contains(id)) { - return filter.match(card, source.getControllerId(), game); - } + ExileZone exile = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + if (exile == null || exile.isEmpty()) { + return false; } - return false; - } - - @Override - public TargetCardInBaneAlleyBrokerExile copy() { - return new TargetCardInBaneAlleyBrokerExile(this); + TargetCardInExile target = new TargetCardInExile(StaticFilters.FILTER_CARD, exile.getId()); + target.setNotTarget(true); + player.chooseTarget(outcome, exile, target, source, game); + Card card = game.getCard(target.getFirstTarget()); + if (card == null) { + return false; + } + return card != null && player.moveCards(card, Zone.HAND, source, game); } } class BaneAlleyBrokerLookAtCardEffect extends AsThoughEffectImpl { - public BaneAlleyBrokerLookAtCardEffect() { + BaneAlleyBrokerLookAtCardEffect() { super(AsThoughEffectType.LOOK_AT_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit); staticText = "You may look at cards exiled with {this}"; } - public BaneAlleyBrokerLookAtCardEffect(final BaneAlleyBrokerLookAtCardEffect effect) { + private BaneAlleyBrokerLookAtCardEffect(final BaneAlleyBrokerLookAtCardEffect effect) { super(effect); } @@ -211,19 +191,12 @@ public BaneAlleyBrokerLookAtCardEffect copy() { @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (affectedControllerId.equals(source.getControllerId())) { - Card card = game.getCard(objectId); - if (card != null) { - MageObject sourceObject = game.getObject(source.getSourceId()); - if (sourceObject == null) { - return false; - } - UUID exileId = CardUtil.getCardExileZoneId(game, source); - ExileZone exile = game.getExile().getExileZone(exileId); - return exile != null && exile.contains(objectId); - } + if (!source.isControlledBy(affectedControllerId)) { + return false; } - return false; + Card card = game.getCard(objectId); + ExileZone exile = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + return card != null && exile != null && exile.getCards(game).contains(card); } } diff --git a/Mage.Sets/src/mage/cards/b/BaneOfHanweir.java b/Mage.Sets/src/mage/cards/b/BaneOfHanweir.java index 889be3bc5327..a787fbe86ecb 100644 --- a/Mage.Sets/src/mage/cards/b/BaneOfHanweir.java +++ b/Mage.Sets/src/mage/cards/b/BaneOfHanweir.java @@ -1,20 +1,14 @@ - package mage.cards.b; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; import mage.abilities.common.AttacksEachCombatStaticAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; -import mage.abilities.keyword.TransformAbility; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** * @author nantuko @@ -22,7 +16,7 @@ public final class BaneOfHanweir extends CardImpl { public BaneOfHanweir(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.WEREWOLF); this.color.setRed(true); @@ -37,8 +31,7 @@ public BaneOfHanweir(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new AttacksEachCombatStaticAbility()); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Bane of Hanweir. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private BaneOfHanweir(final BaneOfHanweir card) { diff --git a/Mage.Sets/src/mage/cards/b/BanefulOmen.java b/Mage.Sets/src/mage/cards/b/BanefulOmen.java index a362632d87df..2c01f4bed15b 100644 --- a/Mage.Sets/src/mage/cards/b/BanefulOmen.java +++ b/Mage.Sets/src/mage/cards/b/BanefulOmen.java @@ -10,7 +10,6 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.players.Player; import java.util.Set; @@ -64,7 +63,7 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { - return "At the beginning of your end step, you may reveal the top card of your library. If you do, each opponent loses life equal to that card's converted mana cost."; + return "At the beginning of your end step, you may reveal the top card of your library. If you do, each opponent loses life equal to that card's mana value."; } } @@ -95,7 +94,7 @@ public boolean apply(Game game, Ability source) { player.revealCards("Baneful Omen", cards, game); - int loseLife = card.getConvertedManaCost(); + int loseLife = card.getManaValue(); Set opponents = game.getOpponents(source.getControllerId()); for (UUID opponentUuid : opponents) { Player opponent = game.getPlayer(opponentUuid); diff --git a/Mage.Sets/src/mage/cards/b/BansheesBlade.java b/Mage.Sets/src/mage/cards/b/BansheesBlade.java index d79d33870c68..0e6a9715acd2 100644 --- a/Mage.Sets/src/mage/cards/b/BansheesBlade.java +++ b/Mage.Sets/src/mage/cards/b/BansheesBlade.java @@ -73,8 +73,7 @@ public BansheesBladeAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || event.getType() == GameEvent.EventType.DAMAGED_CREATURE - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER + || event.getType() == GameEvent.EventType.DAMAGED_PERMANENT || event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_PRE; } diff --git a/Mage.Sets/src/mage/cards/b/BantSureblade.java b/Mage.Sets/src/mage/cards/b/BantSureblade.java index ccc073ef3022..fe889bf15dc4 100644 --- a/Mage.Sets/src/mage/cards/b/BantSureblade.java +++ b/Mage.Sets/src/mage/cards/b/BantSureblade.java @@ -18,7 +18,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.MulticoloredPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/b/BaronSengir.java b/Mage.Sets/src/mage/cards/b/BaronSengir.java index f4163c022964..4c3e87dfadcb 100644 --- a/Mage.Sets/src/mage/cards/b/BaronSengir.java +++ b/Mage.Sets/src/mage/cards/b/BaronSengir.java @@ -16,7 +16,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/b/BarrageTyrant.java b/Mage.Sets/src/mage/cards/b/BarrageTyrant.java index e62323f5ef60..cb4658785ee5 100644 --- a/Mage.Sets/src/mage/cards/b/BarrageTyrant.java +++ b/Mage.Sets/src/mage/cards/b/BarrageTyrant.java @@ -18,7 +18,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.ColorlessPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetAnyTarget; diff --git a/Mage.Sets/src/mage/cards/b/BarrenGlory.java b/Mage.Sets/src/mage/cards/b/BarrenGlory.java index c28d9c26c2d9..e7c448b67d93 100644 --- a/Mage.Sets/src/mage/cards/b/BarrenGlory.java +++ b/Mage.Sets/src/mage/cards/b/BarrenGlory.java @@ -14,7 +14,7 @@ import mage.constants.ComparisonType; import mage.constants.TargetController; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * @author fireshoes diff --git a/Mage.Sets/src/mage/cards/b/BarrinTolarianArchmage.java b/Mage.Sets/src/mage/cards/b/BarrinTolarianArchmage.java index 7bdf6f46fdae..74df305d38b1 100644 --- a/Mage.Sets/src/mage/cards/b/BarrinTolarianArchmage.java +++ b/Mage.Sets/src/mage/cards/b/BarrinTolarianArchmage.java @@ -12,7 +12,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; diff --git a/Mage.Sets/src/mage/cards/b/BarrinsSpite.java b/Mage.Sets/src/mage/cards/b/BarrinsSpite.java index 265380278dcc..a9be4bde62e7 100644 --- a/Mage.Sets/src/mage/cards/b/BarrinsSpite.java +++ b/Mage.Sets/src/mage/cards/b/BarrinsSpite.java @@ -1,8 +1,5 @@ - package mage.cards.b; -import java.util.UUID; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; @@ -14,10 +11,16 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import mage.target.Target; import mage.target.common.TargetCreaturePermanentSameController; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + /** - * * @author jeffwadsworth */ public final class BarrinsSpite extends CardImpl { @@ -27,8 +30,9 @@ public BarrinsSpite(UUID ownerId, CardSetInfo setInfo) { // Choose two target creatures controlled by the same player. Their controller chooses and sacrifices one of them. Return the other to its owner's hand. this.getSpellAbility().addEffect(new BarrinsSpiteEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanentSameController(2, 2, StaticFilters.FILTER_PERMANENT_CREATURE, false)); - + this.getSpellAbility().addTarget(new TargetCreaturePermanentSameController( + 2, 2, StaticFilters.FILTER_PERMANENT_CREATURE, false + )); } private BarrinsSpite(final BarrinsSpite card) { @@ -43,12 +47,13 @@ public BarrinsSpite copy() { class BarrinsSpiteEffect extends OneShotEffect { - public BarrinsSpiteEffect() { + BarrinsSpiteEffect() { super(Outcome.Detriment); - this.staticText = "Choose two target creatures controlled by the same player. Their controller chooses and sacrifices one of them. Return the other to its owner's hand"; + this.staticText = "Choose two target creatures controlled by the same player. " + + "Their controller chooses and sacrifices one of them. Return the other to its owner's hand"; } - public BarrinsSpiteEffect(final BarrinsSpiteEffect effect) { + private BarrinsSpiteEffect(final BarrinsSpiteEffect effect) { super(effect); } @@ -59,30 +64,42 @@ public BarrinsSpiteEffect copy() { @Override public boolean apply(Game game, Ability source) { - MageObject sourceObject = source.getSourceObject(game); - if (sourceObject != null) { - boolean sacrificeDone = false; - int count = 0; - for (UUID targetId : getTargetPointer().getTargets(game, source)) { - Permanent creature = game.getPermanent(targetId); - if (creature != null) { - Player controllerOfCreature = game.getPlayer(creature.getControllerId()); - if(controllerOfCreature != null) { - if ((count == 0 - && controllerOfCreature.chooseUse(Outcome.Sacrifice, "Sacrifice " + creature.getLogName() + '?', source, game)) - || (count == 1 - && !sacrificeDone)) { - creature.sacrifice(source, game); - sacrificeDone = true; - } else { - creature.moveToZone(Zone.HAND, source, game, false); - } - count++; - } - } - } + List permanents = source + .getTargets() + .stream() + .map(Target::getTargets) + .flatMap(Collection::stream) + .map(game::getPermanent) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + if (permanents.isEmpty()) { + return false; + } + if (permanents.size() == 1) { + permanents.get(0).sacrifice(source, game); + return true; + } + if (permanents.size() > 2) { + throw new IllegalStateException("Too many permanents in list, shouldn't be possible"); + } + Player player = game.getPlayer(permanents.get(0).getControllerId()); + Player controller = game.getPlayer(source.getControllerId()); + if (player == null || controller == null) { + return false; + } + Permanent perm1 = permanents.get(0); + Permanent perm2 = permanents.get(1); + if (player.chooseUse( + outcome, "Choose which permanent to sacrifice", + "The other will be returned to your hand", + perm1.getIdName(), perm2.getIdName(), source, game + )) { + perm1.sacrifice(source, game); + controller.moveCards(perm2, Zone.HAND, source, game); return true; } - return false; + perm2.sacrifice(source, game); + controller.moveCards(perm1, Zone.HAND, source, game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/b/BasicConjuration.java b/Mage.Sets/src/mage/cards/b/BasicConjuration.java new file mode 100644 index 000000000000..b4ad758ab453 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BasicConjuration.java @@ -0,0 +1,41 @@ +package mage.cards.b; + +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +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 java.util.UUID; + +/** + * + * @author htrajan + */ +public final class BasicConjuration extends CardImpl { + + public BasicConjuration(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}{G}"); + + this.subtype.add(SubType.LESSON); + + // Look at the top six cards of your library. You may reveal a creature card from among them and put it into your hand. Put the rest on the bottom of your library in a random order. You gain 3 life. + this.getSpellAbility().addEffect(new LookLibraryAndPickControllerEffect( + 6, 1, StaticFilters.FILTER_CARD_CREATURE_A, + true, false, Zone.HAND, true + ).setBackInRandomOrder(true)); + this.getSpellAbility().addEffect(new GainLifeEffect(3)); + } + + private BasicConjuration(final BasicConjuration card) { + super(card); + } + + @Override + public BasicConjuration copy() { + return new BasicConjuration(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BasrisAcolyte.java b/Mage.Sets/src/mage/cards/b/BasrisAcolyte.java index a1e1a2e52030..eeb0dd12f105 100644 --- a/Mage.Sets/src/mage/cards/b/BasrisAcolyte.java +++ b/Mage.Sets/src/mage/cards/b/BasrisAcolyte.java @@ -12,7 +12,7 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/b/BastionProtector.java b/Mage.Sets/src/mage/cards/b/BastionProtector.java index 09735f6c8838..ababacd673bb 100644 --- a/Mage.Sets/src/mage/cards/b/BastionProtector.java +++ b/Mage.Sets/src/mage/cards/b/BastionProtector.java @@ -16,7 +16,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.CommanderPredicate; +import mage.filter.predicate.mageobject.CommanderPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/b/BattleForBretagard.java b/Mage.Sets/src/mage/cards/b/BattleForBretagard.java index 5daffe0912c0..555ba8e38b5e 100644 --- a/Mage.Sets/src/mage/cards/b/BattleForBretagard.java +++ b/Mage.Sets/src/mage/cards/b/BattleForBretagard.java @@ -13,6 +13,7 @@ import mage.constants.SagaChapter; import mage.constants.SubType; import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; @@ -100,7 +101,7 @@ public boolean apply(Game game, Ability source) { class BattleForBretagardTarget extends TargetPermanent { - private static final FilterPermanent filter = new FilterPermanent( + private static final FilterPermanent filter = new FilterControlledPermanent( "artifact tokens and/or creature tokens you control with different names" ); @@ -133,9 +134,11 @@ public boolean canTarget(UUID playerId, UUID id, Ability ability, Game game) { Set names = this.getTargets() .stream() .map(game::getPermanent) - .map(MageObject::getName) .filter(Objects::nonNull) + .map(MageObject::getName) .collect(Collectors.toSet()); + names.removeIf(Objects::isNull); + names.removeIf(String::isEmpty); Permanent permanent = game.getPermanent(id); return permanent != null && !names.contains(permanent.getName()); } @@ -147,12 +150,14 @@ public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game ga Set names = this.getTargets() .stream() .map(game::getPermanent) - .map(MageObject::getName) .filter(Objects::nonNull) + .map(MageObject::getName) .collect(Collectors.toSet()); + names.removeIf(Objects::isNull); + names.removeIf(String::isEmpty); possibleTargets.removeIf(uuid -> { Permanent permanent = game.getPermanent(uuid); - return permanent != null && !names.contains(permanent.getName()); + return permanent == null || names.contains(permanent.getName()); }); return possibleTargets; } diff --git a/Mage.Sets/src/mage/cards/b/BattleMammoth.java b/Mage.Sets/src/mage/cards/b/BattleMammoth.java index e2e1297b70b1..3255852857af 100644 --- a/Mage.Sets/src/mage/cards/b/BattleMammoth.java +++ b/Mage.Sets/src/mage/cards/b/BattleMammoth.java @@ -1,7 +1,7 @@ package mage.cards.b; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetControlledPermanentTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.ForetellAbility; import mage.abilities.keyword.TrampleAbility; @@ -9,11 +9,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; import java.util.UUID; @@ -33,7 +28,9 @@ public BattleMammoth(UUID ownerId, CardSetInfo setInfo) { this.addAbility(TrampleAbility.getInstance()); // Whenever a permanent you control becomes the target of a spell or ability an opponent controls, you may draw a card. - this.addAbility(new BattleMammothTriggeredAbility()); + this.addAbility(new BecomesTargetControlledPermanentTriggeredAbility( + new DrawCardSourceControllerEffect(1), true + )); // Foretell {2}{G}{G} this.addAbility(new ForetellAbility(this, "{2}{G}{G}")); @@ -48,40 +45,3 @@ public BattleMammoth copy() { return new BattleMammoth(this); } } - -class BattleMammothTriggeredAbility extends TriggeredAbilityImpl { - - BattleMammothTriggeredAbility() { - super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), true); - } - - private BattleMammothTriggeredAbility(final BattleMammothTriggeredAbility ability) { - super(ability); - } - - @Override - public BattleMammothTriggeredAbility copy() { - return new BattleMammothTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); - if (permanent == null || !permanent.isControlledBy(this.getControllerId())) { - return false; - } - Player targetter = game.getPlayer(event.getPlayerId()); - Object object = game.getObject(event.getSourceId()); - return object != null && targetter != null && targetter.hasOpponent(this.getControllerId(), game); - } - - @Override - public String getRule() { - return "Whenever a permanent you control becomes the target of a spell or ability an opponent controls, you may draw a card."; - } -} diff --git a/Mage.Sets/src/mage/cards/b/BattleOfFrostAndFire.java b/Mage.Sets/src/mage/cards/b/BattleOfFrostAndFire.java index f561c96dcd40..cfae8be3beea 100644 --- a/Mage.Sets/src/mage/cards/b/BattleOfFrostAndFire.java +++ b/Mage.Sets/src/mage/cards/b/BattleOfFrostAndFire.java @@ -91,13 +91,13 @@ public boolean checkEventType(GameEvent event, Game game) { public boolean checkTrigger(GameEvent event, Game game) { if (event.getPlayerId().equals(this.getControllerId())) { Spell spell = game.getStack().getSpell(event.getTargetId()); - return spell != null && spell.getConvertedManaCost() >= 5; + return spell != null && spell.getManaValue() >= 5; } return false; } @Override public String getRule() { - return "Whenever you cast a spell with converted mana cost 5 or greater this turn, " + super.getRule(); + return "Whenever you cast a spell with mana value 5 or greater this turn, " + super.getRule(); } } diff --git a/Mage.Sets/src/mage/cards/b/BattlemagesBracers.java b/Mage.Sets/src/mage/cards/b/BattlemagesBracers.java new file mode 100644 index 000000000000..d37578da8a44 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BattlemagesBracers.java @@ -0,0 +1,93 @@ +package mage.cards.b; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CopyStackAbilityEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.mana.ActivatedManaAbilityImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.StackAbility; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BattlemagesBracers extends CardImpl { + + public BattlemagesBracers(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{R}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Equipped creature has haste. + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( + HasteAbility.getInstance(), AttachmentType.EQUIPMENT, Duration.WhileOnBattlefield + ))); + + // Whenever an ability of equipped creature is activated, if it isn't a mana ability, you may pay {1}. If you do, copy that ability. You may choose new targets for the copy. + this.addAbility(new BattlemagesBracersTriggeredAbility()); + + // Equip {2} + this.addAbility(new EquipAbility(2)); + } + + private BattlemagesBracers(final BattlemagesBracers card) { + super(card); + } + + @Override + public BattlemagesBracers copy() { + return new BattlemagesBracers(this); + } +} + +class BattlemagesBracersTriggeredAbility extends TriggeredAbilityImpl { + + BattlemagesBracersTriggeredAbility() { + super(Zone.BATTLEFIELD, new DoIfCostPaid(new CopyStackAbilityEffect(), new GenericManaCost(1))); + } + + private BattlemagesBracersTriggeredAbility(final BattlemagesBracersTriggeredAbility ability) { + super(ability); + } + + @Override + public BattlemagesBracersTriggeredAbility copy() { + return new BattlemagesBracersTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ACTIVATED_ABILITY; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent equipment = game.getPermanent(this.getSourceId()); + if (equipment == null || !equipment.isAttachedTo(event.getSourceId())) { + return false; + } + StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); + if (stackAbility == null || stackAbility.getStackAbility() instanceof ActivatedManaAbilityImpl) { + return false; + } + getEffects().setValue("stackAbility", stackAbility); + return true; + } + + @Override + public String getRule() { + return "Whenever an ability of equipped creature is activated, if it isn't a mana ability, you may pay {1}. " + + "If you do, copy that ability. You may choose new targets for the copy."; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BayouGroff.java b/Mage.Sets/src/mage/cards/b/BayouGroff.java new file mode 100644 index 000000000000..b45b35fe2bd5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BayouGroff.java @@ -0,0 +1,45 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.costs.OrCost; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BayouGroff extends CardImpl { + + public BayouGroff(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.PLANT); + this.subtype.add(SubType.DOG); + this.power = new MageInt(5); + this.toughness = new MageInt(4); + + // As an additional cost to cast this spell, sacrifice a creature or pay {3}. + this.getSpellAbility().addCost(new OrCost( + new SacrificeTargetCost(new TargetControlledPermanent( + StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT + )), new GenericManaCost(3), "sacrifice a creature or pay {3}" + )); + } + + private BayouGroff(final BayouGroff card) { + super(card); + } + + @Override + public BayouGroff copy() { + return new BayouGroff(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BazaarKrovod.java b/Mage.Sets/src/mage/cards/b/BazaarKrovod.java index 881c255b768c..baf732610c0f 100644 --- a/Mage.Sets/src/mage/cards/b/BazaarKrovod.java +++ b/Mage.Sets/src/mage/cards/b/BazaarKrovod.java @@ -14,7 +14,7 @@ import mage.constants.Outcome; import mage.constants.SubType; import mage.filter.common.FilterAttackingCreature; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetAttackingCreature; diff --git a/Mage.Sets/src/mage/cards/b/BeaconBolt.java b/Mage.Sets/src/mage/cards/b/BeaconBolt.java index e1276e0f7c86..c75e36fbbd29 100644 --- a/Mage.Sets/src/mage/cards/b/BeaconBolt.java +++ b/Mage.Sets/src/mage/cards/b/BeaconBolt.java @@ -1,20 +1,26 @@ package mage.cards.b; -import java.util.UUID; import mage.abilities.dynamicvalue.common.InstantSorceryExileGraveyardCount; import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; import mage.abilities.keyword.JumpStartAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class BeaconBolt extends CardImpl { + private static final Hint hint = new ValueHint( + "Instant and sorcery cards in your exile and graveyard", InstantSorceryExileGraveyardCount.instance + ); + public BeaconBolt(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{U}{R}"); @@ -25,6 +31,7 @@ public BeaconBolt(UUID ownerId, CardSetInfo setInfo) { + "the total number of instant and sorcery cards " + "you own in exile and in your graveyard")); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addHint(hint); // Jump-start this.addAbility(new JumpStartAbility(this)); diff --git a/Mage.Sets/src/mage/cards/b/BeamingDefiance.java b/Mage.Sets/src/mage/cards/b/BeamingDefiance.java new file mode 100644 index 000000000000..499b2284fc40 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BeamingDefiance.java @@ -0,0 +1,40 @@ +package mage.cards.b; + +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.HexproofAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BeamingDefiance extends CardImpl { + + public BeamingDefiance(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); + + // Target creature you control gets +2/+2 and gains hexproof until end of turn. + this.getSpellAbility().addEffect(new BoostTargetEffect( + 2, 2, Duration.EndOfTurn + ).setText("target creature you control gets +2/+2")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + HexproofAbility.getInstance(), Duration.EndOfTurn + ).setText("and gains hexproof until end of turn")); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + } + + private BeamingDefiance(final BeamingDefiance card) { + super(card); + } + + @Override + public BeamingDefiance copy() { + return new BeamingDefiance(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BeamsplitterMage.java b/Mage.Sets/src/mage/cards/b/BeamsplitterMage.java index c4bcdc69a709..c7ce30bbfce7 100644 --- a/Mage.Sets/src/mage/cards/b/BeamsplitterMage.java +++ b/Mage.Sets/src/mage/cards/b/BeamsplitterMage.java @@ -1,7 +1,10 @@ package mage.cards.b; import mage.MageInt; +import mage.MageObject; +import mage.MageObjectReference; import mage.abilities.Ability; +import mage.abilities.AbilityImpl; import mage.abilities.Mode; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.OneShotEffect; @@ -12,19 +15,22 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicate; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.predicate.mageobject.MageObjectReferencePredicate; import mage.game.Game; -import mage.game.events.CopiedStackObjectEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.stack.Spell; +import mage.game.stack.StackObject; import mage.players.Player; import mage.target.Target; import mage.target.TargetPermanent; -import mage.target.targetpointer.FixedTarget; +import mage.util.functions.StackObjectCopyApplier; -import java.util.UUID; +import java.util.*; /** * @author TheElk801 @@ -55,11 +61,11 @@ public BeamsplitterMage copy() { class BeamsplitterMageTriggeredAbility extends TriggeredAbilityImpl { - public BeamsplitterMageTriggeredAbility() { + BeamsplitterMageTriggeredAbility() { super(Zone.BATTLEFIELD, new BeamsplitterMageEffect(), false); } - public BeamsplitterMageTriggeredAbility(final BeamsplitterMageTriggeredAbility ability) { + private BeamsplitterMageTriggeredAbility(final BeamsplitterMageTriggeredAbility ability) { super(ability); } @@ -75,46 +81,59 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getPlayerId().equals(this.getControllerId())) { - Spell spell = game.getStack().getSpell(event.getTargetId()); - if (!isControlledInstantOrSorcery(spell)) { - return false; - } - boolean targetsSource = false; - for (Ability ability : spell.getSpellAbilities()) { - for (UUID modeId : ability.getModes().getSelectedModes()) { - Mode mode = ability.getModes().get(modeId); - for (Target target : mode.getTargets()) { - if (!target.isNotTarget()) { - for (UUID targetId : target.getTargets()) { - if (targetId.equals(getSourceId())) { - targetsSource = true; - } else { - return false; - } - } - } - } - } - } - if (targetsSource) { - this.getEffects().get(0).setTargetPointer(new FixedTarget(spell.getId())); - return true; - } + if (!isControlledBy(event.getPlayerId())) { + return false; } - return false; + Spell spell = game.getSpellOrLKIStack(event.getTargetId()); + if (spell == null || !spell.isInstantOrSorcery()) { + return false; + } + if (spell.getSpellAbilities() + .stream() + .map(AbilityImpl::getModes) + .flatMap(m -> m.getSelectedModes().stream().map(m::get)) + .filter(Objects::nonNull) + .map(Mode::getTargets) + .flatMap(Collection::stream) + .filter(t -> !t.isNotTarget()) + .map(Target::getTargets) + .flatMap(Collection::stream) + .anyMatch(uuid -> !getSourceId().equals(uuid) && uuid != null)) { + return false; + } + this.getEffects().setValue("spellCast", spell); + return true; } - private boolean isControlledInstantOrSorcery(Spell spell) { - return spell != null - && (spell.isControlledBy(this.getControllerId())) - && (spell.isInstant() || spell.isSorcery()); + @Override + public boolean checkInterveningIfClause(Game game) { + Spell spell = (Spell) getEffects().get(0).getValue("spellCast"); + if (spell == null) { + return false; + } + return game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_CONTROLLED_CREATURE, + getControllerId(), getSourceId(), game + ).stream() + .filter(Objects::nonNull) + .filter(MageObject::isCreature) + .filter(p -> checkNotSource(p, game)) + .anyMatch(p -> spell.canTarget(game, p.getId())); + } + + private boolean checkNotSource(Permanent permanent, Game game) { + // workaround for zcc not being set before first intervening if check + if (this.getSourceObjectZoneChangeCounter() == 0) { + return !permanent.getId().equals(this.getSourceId()); + } + return !permanent.getId().equals(this.getSourceId()) + || permanent.getZoneChangeCounter(game) != this.getSourceObjectZoneChangeCounter(); } @Override public String getRule() { return "Whenever you cast an instant or sorcery spell that targets " - + "only {this}, if you control one or more creatures " + + "only {this}, if you control one or more other creatures " + "that spell could target, choose one of those creatures. " + "Copy that spell. The copy targets the chosen creature."; } @@ -122,11 +141,11 @@ public String getRule() { class BeamsplitterMageEffect extends OneShotEffect { - public BeamsplitterMageEffect() { + BeamsplitterMageEffect() { super(Outcome.Detriment); } - public BeamsplitterMageEffect(final BeamsplitterMageEffect effect) { + private BeamsplitterMageEffect(final BeamsplitterMageEffect effect) { super(effect); } @@ -137,93 +156,62 @@ public BeamsplitterMageEffect copy() { @Override public boolean apply(Game game, Ability source) { - Spell spell = game.getSpellOrLKIStack(this.getTargetPointer().getFirst(game, source)); - Player controller = game.getPlayer(source.getControllerId()); - if (spell != null && controller != null) { - // search the target that targets source - Target usedTarget = null; - setUsedTarget: - for (Ability ability : spell.getSpellAbilities()) { - for (UUID modeId : ability.getModes().getSelectedModes()) { - Mode mode = ability.getModes().get(modeId); - for (Target target : mode.getTargets()) { - if (!target.isNotTarget() && target.getFirstTarget().equals(source.getSourceId())) { - usedTarget = target.copy(); - usedTarget.clearChosen(); - break setUsedTarget; - } - } - } - } - if (usedTarget == null) { - return false; - } - FilterPermanent filter = new BeamsplitterMageFilter(usedTarget, source.getSourceId(), source.getControllerId()); - Target target1 = new TargetPermanent(filter); - target1.setNotTarget(true); - if (controller.choose(outcome, target1, source.getSourceId(), game)) { - Permanent creature = game.getPermanent(target1.getFirstTarget()); - if (creature == null) { - return false; - } - Spell copy = spell.copySpell(source.getControllerId(), game); - game.getStack().push(copy); - setTarget: - for (UUID modeId : copy.getSpellAbility().getModes().getSelectedModes()) { - Mode mode = copy.getSpellAbility().getModes().get(modeId); - for (Target target : mode.getTargets()) { - if (target.getClass().equals(usedTarget.getClass())) { - target.clearChosen(); // For targets with Max > 1 we need to clear before the text is comapred - if (target.getMessage().equals(usedTarget.getMessage())) { - target.addTarget(creature.getId(), copy.getSpellAbility(), game, false); - break setTarget; - } - } - } - } - game.fireEvent(new CopiedStackObjectEvent(spell, copy, source.getControllerId())); - String activateMessage = copy.getActivatedMessage(game); - if (activateMessage.startsWith(" casts ")) { - activateMessage = activateMessage.substring(6); - } - if (!game.isSimulation()) { - game.informPlayers(controller.getLogName() + activateMessage); - } - } - return true; + Player player = game.getPlayer(source.getControllerId()); + Spell spell = (Spell) getValue("spellCast"); + if (player == null || spell == null) { + return false; + } + FilterPermanent filter = new FilterControlledCreaturePermanent("a creature you control that can be targeted by " + spell.getName()); + filter.add(AnotherPredicate.instance); + filter.add(new BeamsplitterMagePredicate(spell)); + TargetPermanent target = new TargetPermanent(filter); + target.setNotTarget(true); + player.choose(outcome, target, source.getSourceId(), game); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent == null) { + return false; } - return false; + spell.createCopyOnStack( + game, source, player.getId(), false, + 1, new BeamsplitterMageApplier(permanent, game) + ); + return true; } } -class BeamsplitterMageFilter extends FilterControlledPermanent { +class BeamsplitterMagePredicate implements Predicate { - private final Target target; - private final UUID notId; + private final Spell spell; - public BeamsplitterMageFilter(Target target, UUID notId, UUID controllerId) { - super("creature this spell could target"); - this.target = target; - this.notId = notId; - this.add(new ControllerIdPredicate(controllerId)); + BeamsplitterMagePredicate(Spell spell) { + this.spell = spell; } - public BeamsplitterMageFilter(final BeamsplitterMageFilter filter) { - super(filter); - this.target = filter.target; - this.notId = filter.notId; + @Override + public boolean apply(Permanent input, Game game) { + return spell != null && spell.canTarget(game, input.getId()); + } +} + +class BeamsplitterMageApplier implements StackObjectCopyApplier { + + private final Iterator predicate; + + BeamsplitterMageApplier(Permanent permanent, Game game) { + this.predicate = Arrays.asList(new MageObjectReferencePredicate( + new MageObjectReference(permanent, game) + )).iterator(); } @Override - public BeamsplitterMageFilter copy() { - return new BeamsplitterMageFilter(this); + public void modifySpell(StackObject stackObject, Game game) { } @Override - public boolean match(Permanent permanent, UUID sourceId, UUID playerId, Game game) { - return super.match(permanent, game) - && permanent.isCreature() - && !permanent.getId().equals(notId) - && target.canTarget(permanent.getId(), game); + public MageObjectReferencePredicate getNextPredicate() { + if (predicate.hasNext()) { + return predicate.next(); + } + return null; } } diff --git a/Mage.Sets/src/mage/cards/b/BedlamReveler.java b/Mage.Sets/src/mage/cards/b/BedlamReveler.java index 5e4a2b29cc65..3ced1749e1a8 100644 --- a/Mage.Sets/src/mage/cards/b/BedlamReveler.java +++ b/Mage.Sets/src/mage/cards/b/BedlamReveler.java @@ -32,10 +32,10 @@ public BedlamReveler(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(4); // This spell costs {1} less to cast for each instant and sorcery card in your graveyard. - DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY); + DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_INSTANT_AND_SORCERY); Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue)); ability.setRuleAtTheTop(true); - ability.addHint(new ValueHint("Instant or sourcery card in your graveyard", xValue)); + ability.addHint(new ValueHint("Instant and sorcery card in your graveyard", xValue)); this.addAbility(ability); // Prowess diff --git a/Mage.Sets/src/mage/cards/b/BeetleformMage.java b/Mage.Sets/src/mage/cards/b/BeetleformMage.java index 3615ac7a803b..bc530655194c 100644 --- a/Mage.Sets/src/mage/cards/b/BeetleformMage.java +++ b/Mage.Sets/src/mage/cards/b/BeetleformMage.java @@ -33,8 +33,8 @@ public BeetleformMage (UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(2); // {G}{U}: Beetleform Mage gets +2/+2 and gains flying until end of turn. Activate this ability only once each turn. - Ability ability = new LimitedTimesPerTurnActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(2,2, Duration.EndOfTurn), new ManaCostsImpl("{G}{U}")); - ability.addEffect(new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.EndOfTurn)); + Ability ability = new LimitedTimesPerTurnActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(2,2, Duration.EndOfTurn).setText("{this} gets +2/+2"), new ManaCostsImpl("{G}{U}")); + ability.addEffect(new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.EndOfTurn).setText("and gains flying until end of turn")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/BehemothsHerald.java b/Mage.Sets/src/mage/cards/b/BehemothsHerald.java index f29f57711dc7..bfcae12781f6 100644 --- a/Mage.Sets/src/mage/cards/b/BehemothsHerald.java +++ b/Mage.Sets/src/mage/cards/b/BehemothsHerald.java @@ -1,9 +1,6 @@ - package mage.cards.b; -import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeTargetCost; @@ -14,34 +11,26 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.mageobject.ColorPredicate; import mage.filter.predicate.mageobject.NamePredicate; import mage.target.common.TargetCardInLibrary; -import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetControlledCreatureEachColor; + +import java.util.UUID; /** - * * @author North */ public final class BehemothsHerald extends CardImpl { private static final FilterCard filter = new FilterCard("card named Godsire"); - private static final FilterControlledCreaturePermanent filterRed = new FilterControlledCreaturePermanent("a red creature"); - private static final FilterControlledCreaturePermanent filterGreen = new FilterControlledCreaturePermanent("a green creature"); - private static final FilterControlledCreaturePermanent filterWhite = new FilterControlledCreaturePermanent("a white creature"); static { filter.add(new NamePredicate("Godsire")); - filterRed.add(new ColorPredicate(ObjectColor.RED)); - filterGreen.add(new ColorPredicate(ObjectColor.GREEN)); - filterWhite.add(new ColorPredicate(ObjectColor.WHITE)); } public BehemothsHerald(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}"); this.subtype.add(SubType.ELF, SubType.SHAMAN); this.power = new MageInt(1); @@ -49,14 +38,11 @@ public BehemothsHerald(UUID ownerId, CardSetInfo setInfo) { // {2}{G}, {tap}, Sacrifice a red creature, a green creature, and a white creature: // Search your library for a card named Godsire and put it onto the battlefield. Then shuffle your library. - TargetCardInLibrary target = new TargetCardInLibrary(1, 1, new FilterCard(filter)); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new SearchLibraryPutInPlayEffect(target), - new ManaCostsImpl("{2}{G}")); + Ability ability = new SimpleActivatedAbility(new SearchLibraryPutInPlayEffect( + new TargetCardInLibrary(filter)), new ManaCostsImpl<>("{2}{G}") + ); ability.addCost(new TapSourceCost()); - ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filterRed, false))); - ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filterGreen, false))); - ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filterWhite, false))); + ability.addCost(new SacrificeTargetCost(new TargetControlledCreatureEachColor("RGW"))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/BeholdTheBeyond.java b/Mage.Sets/src/mage/cards/b/BeholdTheBeyond.java index 73d713d3da33..a0d235b69208 100644 --- a/Mage.Sets/src/mage/cards/b/BeholdTheBeyond.java +++ b/Mage.Sets/src/mage/cards/b/BeholdTheBeyond.java @@ -24,7 +24,7 @@ public BeholdTheBeyond(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new DiscardHandControllerEffect()); TargetCardInLibrary target = new TargetCardInLibrary(0, 3, new FilterCard("cards")); Effect effect = new SearchLibraryPutInHandEffect(target); - effect.setText("Search your library for three cards and put those cards into your hand. Then shuffle your library"); + effect.setText("Search your library for three cards, put those cards into your hand, then shuffle"); this.getSpellAbility().addEffect(effect); } diff --git a/Mage.Sets/src/mage/cards/b/BeledrosWitherbloom.java b/Mage.Sets/src/mage/cards/b/BeledrosWitherbloom.java new file mode 100644 index 000000000000..1b64d7023689 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BeledrosWitherbloom.java @@ -0,0 +1,55 @@ +package mage.cards.b; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.UntapAllLandsControllerEffect; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.game.permanent.token.WitherbloomToken; + +/** + * + * @author weirddan455 + */ +public final class BeledrosWitherbloom extends CardImpl { + + public BeledrosWitherbloom(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{B}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ELDER); + this.subtype.add(SubType.DRAGON); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // At the beginning of each upkeep, create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life." + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new CreateTokenEffect(new WitherbloomToken()), TargetController.EACH_PLAYER, false)); + + // Pay 10 life: Untap all lands you control. Activate only once each turn. + SimpleActivatedAbility ability = new SimpleActivatedAbility(new UntapAllLandsControllerEffect() + .setText("Untap all lands you control. Activate only once each turn"), new PayLifeCost(10)); + ability.setMaxActivationsPerTurn(1); + this.addAbility(ability); + } + + private BeledrosWitherbloom(final BeledrosWitherbloom card) { + super(card); + } + + @Override + public BeledrosWitherbloom copy() { + return new BeledrosWitherbloom(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BellBorcaSpectralSergeant.java b/Mage.Sets/src/mage/cards/b/BellBorcaSpectralSergeant.java index ce92f6649ce1..982180387424 100644 --- a/Mage.Sets/src/mage/cards/b/BellBorcaSpectralSergeant.java +++ b/Mage.Sets/src/mage/cards/b/BellBorcaSpectralSergeant.java @@ -75,7 +75,7 @@ private Object readResolve() throws ObjectStreamException { } private BellBorcaSpectralSergeantAbility() { - super(Zone.BATTLEFIELD, new InfoEffect("note the converted mana cost of each card as it's put into exile")); + super(Zone.BATTLEFIELD, new InfoEffect("note the mana value of each card as it's put into exile")); } @Override @@ -132,7 +132,7 @@ public void watch(GameEvent event, Game game) { if (card == null || card.isFaceDown(game)) { return; } - int cmc = card.getConvertedManaCost(); + int cmc = card.getManaValue(); for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, game.getActivePlayerId(), game)) { if (permanent == null) { continue; diff --git a/Mage.Sets/src/mage/cards/b/BellowingAegisaur.java b/Mage.Sets/src/mage/cards/b/BellowingAegisaur.java index bcd083f52eec..efb46f11c5c3 100644 --- a/Mage.Sets/src/mage/cards/b/BellowingAegisaur.java +++ b/Mage.Sets/src/mage/cards/b/BellowingAegisaur.java @@ -13,7 +13,7 @@ import mage.constants.TargetController; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/b/BelltollDragon.java b/Mage.Sets/src/mage/cards/b/BelltollDragon.java index 2e10ffaa00cb..d8385290b1cc 100644 --- a/Mage.Sets/src/mage/cards/b/BelltollDragon.java +++ b/Mage.Sets/src/mage/cards/b/BelltollDragon.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/b/BelltowerSphinx.java b/Mage.Sets/src/mage/cards/b/BelltowerSphinx.java index 5bb674324e33..9ddda1a02a0f 100644 --- a/Mage.Sets/src/mage/cards/b/BelltowerSphinx.java +++ b/Mage.Sets/src/mage/cards/b/BelltowerSphinx.java @@ -64,7 +64,7 @@ public BelltowerSphinxEffect copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/b/BenevolentUnicorn.java b/Mage.Sets/src/mage/cards/b/BenevolentUnicorn.java index 3c733774da81..1798996ccb05 100644 --- a/Mage.Sets/src/mage/cards/b/BenevolentUnicorn.java +++ b/Mage.Sets/src/mage/cards/b/BenevolentUnicorn.java @@ -76,7 +76,8 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE || event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER || event.getType() == GameEvent.EventType.DAMAGE_PLAYER; + return event.getType() == EventType.DAMAGE_PERMANENT + || event.getType() == GameEvent.EventType.DAMAGE_PLAYER; } @Override diff --git a/Mage.Sets/src/mage/cards/b/BeseechTheQueen.java b/Mage.Sets/src/mage/cards/b/BeseechTheQueen.java index e4f4393d6415..608fa790dd84 100644 --- a/Mage.Sets/src/mage/cards/b/BeseechTheQueen.java +++ b/Mage.Sets/src/mage/cards/b/BeseechTheQueen.java @@ -19,7 +19,7 @@ */ public final class BeseechTheQueen extends CardImpl { - private static final FilterCard filter = new FilterCard("card with converted mana cost less than or equal to the number of lands you control"); + private static final FilterCard filter = new FilterCard("card with mana value less than or equal to the number of lands you control"); static{ filter.add(new BeseechTheQueenPredicate()); } @@ -48,7 +48,7 @@ class BeseechTheQueenPredicate implements Predicate { @Override public final boolean apply(Card input, Game game) { - if(input.getConvertedManaCost() <= game.getBattlefield().getAllActivePermanents(new FilterControlledLandPermanent(), input.getOwnerId(), game).size()){ + if(input.getManaValue() <= game.getBattlefield().getAllActivePermanents(new FilterControlledLandPermanent(), input.getOwnerId(), game).size()){ return true; } return false; @@ -56,6 +56,6 @@ public final boolean apply(Card input, Game game) { @Override public String toString() { - return "card with converted mana cost less than or equal to the number of lands you control"; + return "card with mana value less than or equal to the number of lands you control"; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/b/BiblioplexAssistant.java b/Mage.Sets/src/mage/cards/b/BiblioplexAssistant.java new file mode 100644 index 000000000000..5bcb3438a868 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BiblioplexAssistant.java @@ -0,0 +1,50 @@ +package mage.cards.b; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.PutOnLibraryTargetEffect; +import mage.constants.SubType; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterCard; +import mage.filter.common.FilterInstantOrSorceryCard; +import mage.target.common.TargetCardInYourGraveyard; + +/** + * + * @author weirddan455 + */ +public final class BiblioplexAssistant extends CardImpl { + + private static final FilterCard filter + = new FilterInstantOrSorceryCard("instant or sorcery card from your graveyard"); + + public BiblioplexAssistant(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}"); + + this.subtype.add(SubType.GARGOYLE); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Biblioplex Assistant enters the battlefield, put up to one target instant or sorcery card from your graveyard on top of your library. + Ability ability = new EntersBattlefieldTriggeredAbility(new PutOnLibraryTargetEffect(true)); + ability.addTarget(new TargetCardInYourGraveyard(0, 1, filter)); + this.addAbility(ability); + } + + private BiblioplexAssistant(final BiblioplexAssistant card) { + super(card); + } + + @Override + public BiblioplexAssistant copy() { + return new BiblioplexAssistant(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/Bifurcate.java b/Mage.Sets/src/mage/cards/b/Bifurcate.java index 598300100e7c..23ecae9a4372 100644 --- a/Mage.Sets/src/mage/cards/b/Bifurcate.java +++ b/Mage.Sets/src/mage/cards/b/Bifurcate.java @@ -54,7 +54,7 @@ class BifurcateEffect extends OneShotEffect { public BifurcateEffect() { super(Outcome.Benefit); - this.staticText = "Search your library for a permanent card with the same name as target nontoken creature and put that card onto the battlefield. Then shuffle your library"; + this.staticText = "search your library for a permanent card with the same name as target nontoken creature, put that card onto the battlefield, then shuffle"; } public BifurcateEffect(final BifurcateEffect effect) { diff --git a/Mage.Sets/src/mage/cards/b/BigPlay.java b/Mage.Sets/src/mage/cards/b/BigPlay.java new file mode 100644 index 000000000000..eb1ba98f2292 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BigPlay.java @@ -0,0 +1,43 @@ +package mage.cards.b; + +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BigPlay extends CardImpl { + + public BigPlay(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); + + // Target creature gets +2/+2 and gains reach until end of turn. Put a +1/+1 counter on it. + this.getSpellAbility().addEffect(new BoostTargetEffect( + 2, 2 + ).setText("target creature gets +2/+2")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + ReachAbility.getInstance(), Duration.EndOfTurn + ).setText("and gains reach until end of turn")); + this.getSpellAbility().addEffect(new AddCountersTargetEffect( + CounterType.P1P1.createInstance() + ).setText("Put a +1/+1 counter on it")); + } + + private BigPlay(final BigPlay card) { + super(card); + } + + @Override + public BigPlay copy() { + return new BigPlay(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BindingMummy.java b/Mage.Sets/src/mage/cards/b/BindingMummy.java index d03972fde74c..8e6c2e0622ee 100644 --- a/Mage.Sets/src/mage/cards/b/BindingMummy.java +++ b/Mage.Sets/src/mage/cards/b/BindingMummy.java @@ -13,7 +13,7 @@ import mage.constants.Zone; import static mage.filter.StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_CREATURE; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; /** diff --git a/Mage.Sets/src/mage/cards/b/Biomathematician.java b/Mage.Sets/src/mage/cards/b/Biomathematician.java new file mode 100644 index 000000000000..09aefa8c1a98 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/Biomathematician.java @@ -0,0 +1,48 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +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.FilterControlledPermanent; +import mage.game.permanent.token.QuandrixToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Biomathematician extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.FRACTAL); + + public Biomathematician(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // When Biomathematician enters the battlefield, create a 0/0 green and blue Fractal creature token. Put a +1/+1 counter on each Fractal you control. + Ability ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new QuandrixToken())); + ability.addEffect(new AddCountersAllEffect(CounterType.P1P1.createInstance(), filter)); + this.addAbility(ability); + } + + private Biomathematician(final Biomathematician card) { + super(card); + } + + @Override + public Biomathematician copy() { + return new Biomathematician(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/Bioshift.java b/Mage.Sets/src/mage/cards/b/Bioshift.java index 2ce6ecb571ed..edbd85651ed2 100644 --- a/Mage.Sets/src/mage/cards/b/Bioshift.java +++ b/Mage.Sets/src/mage/cards/b/Bioshift.java @@ -13,7 +13,7 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.ObjectSourcePlayer; import mage.filter.predicate.ObjectSourcePlayerPredicate; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.StackObject; diff --git a/Mage.Sets/src/mage/cards/b/BirthingPod.java b/Mage.Sets/src/mage/cards/b/BirthingPod.java index b88101d0cffc..b03e2b1603d5 100644 --- a/Mage.Sets/src/mage/cards/b/BirthingPod.java +++ b/Mage.Sets/src/mage/cards/b/BirthingPod.java @@ -17,7 +17,7 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.StaticFilters; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -60,9 +60,9 @@ class BirthingPodEffect extends OneShotEffect { BirthingPodEffect() { super(Outcome.Benefit); - staticText = "Search your library for a creature card with converted mana cost equal to 1 " + - "plus the sacrificed creature's converted mana cost, put that card " + - "onto the battlefield, then shuffle your library"; + staticText = "Search your library for a creature card with mana value equal to 1 " + + "plus the sacrificed creature's mana value, put that card " + + "onto the battlefield, then shuffle"; } private BirthingPodEffect(final BirthingPodEffect effect) { @@ -85,9 +85,9 @@ public boolean apply(Game game, Ability source) { if (sacrificedPermanent == null || controller == null) { return false; } - int newConvertedCost = sacrificedPermanent.getConvertedManaCost() + 1; - FilterCard filter = new FilterCard("creature card with converted mana cost " + newConvertedCost); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, newConvertedCost)); + int newConvertedCost = sacrificedPermanent.getManaValue() + 1; + FilterCard filter = new FilterCard("creature card with mana value " + newConvertedCost); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, newConvertedCost)); filter.add(CardType.CREATURE.getPredicate()); TargetCardInLibrary target = new TargetCardInLibrary(filter); if (controller.searchLibrary(target, source, game)) { diff --git a/Mage.Sets/src/mage/cards/b/BishopOfRebirth.java b/Mage.Sets/src/mage/cards/b/BishopOfRebirth.java index 37400fbdd09f..741193165fe1 100644 --- a/Mage.Sets/src/mage/cards/b/BishopOfRebirth.java +++ b/Mage.Sets/src/mage/cards/b/BishopOfRebirth.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInYourGraveyard; /** @@ -23,10 +23,10 @@ */ public final class BishopOfRebirth extends CardImpl { - private static final FilterCard filter = new FilterCreatureCard("creature card with converted mana cost 3 or less from your graveyard"); + private static final FilterCard filter = new FilterCreatureCard("creature card with mana value 3 or less from your graveyard"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); filter.add(CardType.CREATURE.getPredicate()); } @@ -43,7 +43,7 @@ public BishopOfRebirth(UUID ownerId, CardSetInfo setInfo) { // Whenever Bishop of Rebirth attacks, you may return target creature card with converted mana cost 3 or less from your graveyard to the battlefield. Ability ability = new AttacksTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect() - .setText("you may return target creature card with converted mana cost 3 or less from your graveyard to the battlefield"), true); + .setText("you may return target creature card with mana value 3 or less from your graveyard to the battlefield"), true); ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/BiteOfTheBlackRose.java b/Mage.Sets/src/mage/cards/b/BiteOfTheBlackRose.java index 256cd90607c0..008e02dcb88a 100644 --- a/Mage.Sets/src/mage/cards/b/BiteOfTheBlackRose.java +++ b/Mage.Sets/src/mage/cards/b/BiteOfTheBlackRose.java @@ -1,15 +1,13 @@ - package mage.cards.b; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.BoostOpponentsEffect; import mage.abilities.effects.common.discard.DiscardEachPlayerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.choices.TwoChoiceVote; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; @@ -17,14 +15,15 @@ import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author fireshoes */ public final class BiteOfTheBlackRose extends CardImpl { public BiteOfTheBlackRose(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}"); // Will of the council - Starting with you, each player votes for sickness or psychosis. If sickness gets more votes, creatures your opponents control get -2/-2 until end of turn. If psychosis gets more votes or the vote is tied, each opponent discards two cards. this.getSpellAbility().addEffect(new BiteOfTheBlackRoseEffect()); @@ -44,10 +43,13 @@ class BiteOfTheBlackRoseEffect extends OneShotEffect { BiteOfTheBlackRoseEffect() { super(Outcome.Benefit); - this.staticText = "Will of the council — Starting with you, each player votes for sickness or psychosis. If sickness gets more votes, creatures your opponents control get -2/-2 until end of turn. If psychosis gets more votes or the vote is tied, each opponent discards two cards"; + this.staticText = "Will of the council — Starting with you, " + + "each player votes for sickness or psychosis. If sickness gets more votes, " + + "creatures your opponents control get -2/-2 until end of turn. " + + "If psychosis gets more votes or the vote is tied, each opponent discards two cards"; } - BiteOfTheBlackRoseEffect(final BiteOfTheBlackRoseEffect effect) { + private BiteOfTheBlackRoseEffect(final BiteOfTheBlackRoseEffect effect) { super(effect); } @@ -59,29 +61,24 @@ public BiteOfTheBlackRoseEffect copy() { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - int sicknessCount = 0; - int psychosisCount = 0; - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - if (player.chooseUse(Outcome.ExtraTurn, "Choose sickness?", source, game)) { - sicknessCount++; - game.informPlayers(player.getLogName() + " has voted for sickness"); - } else { - psychosisCount++; - game.informPlayers(player.getLogName() + " has voted for psychosis"); - } - } - } - if (sicknessCount > psychosisCount) { - ContinuousEffect effect = new BoostOpponentsEffect(-2, -2, Duration.EndOfTurn); - game.addEffect(effect, source); - } else { - new DiscardEachPlayerEffect(StaticValue.get(2), false, TargetController.OPPONENT).apply(game, source); - } - return true; + if (controller == null) { + return false; + } + + // Outcome.Detriment - AI will discard a card all the time (Psychosis choice) + // TODO: add AI hint logic in the choice method, see Tyrant's Choice as example + TwoChoiceVote vote = new TwoChoiceVote("Sickness (-2/-2)", "Psychosis (discard cards)", Outcome.Detriment); + vote.doVotes(source, game); + + int sicknessCount = vote.getVoteCount(true); + int psychosisCount = vote.getVoteCount(false); + if (sicknessCount > psychosisCount) { + // sickness + game.addEffect(new BoostOpponentsEffect(-2, -2, Duration.EndOfTurn), source); + } else { + // psychosis or tied + new DiscardEachPlayerEffect(StaticValue.get(2), false, TargetController.OPPONENT).apply(game, source); } - return false; + return true; } } diff --git a/Mage.Sets/src/mage/cards/b/BitterFeud.java b/Mage.Sets/src/mage/cards/b/BitterFeud.java index b8d0d2be72d4..8ad9ab2694b6 100644 --- a/Mage.Sets/src/mage/cards/b/BitterFeud.java +++ b/Mage.Sets/src/mage/cards/b/BitterFeud.java @@ -111,9 +111,8 @@ public BitterFeudEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { switch (event.getType()) { - case DAMAGE_CREATURE: + case DAMAGE_PERMANENT: case DAMAGE_PLAYER: - case DAMAGE_PLANESWALKER: return true; default: return false; @@ -130,8 +129,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { case DAMAGE_PLAYER: targetPlayerId = event.getTargetId(); break; - case DAMAGE_CREATURE: - case DAMAGE_PLANESWALKER: + case DAMAGE_PERMANENT: Permanent permanent = game.getPermanent(event.getTargetId()); if (permanent != null) { targetPlayerId = permanent.getControllerId(); diff --git a/Mage.Sets/src/mage/cards/b/BitterOrdeal.java b/Mage.Sets/src/mage/cards/b/BitterOrdeal.java index 5148e61ea7eb..2e445d8d26d9 100644 --- a/Mage.Sets/src/mage/cards/b/BitterOrdeal.java +++ b/Mage.Sets/src/mage/cards/b/BitterOrdeal.java @@ -48,7 +48,7 @@ class BitterOrdealEffect extends OneShotEffect { BitterOrdealEffect() { super(Outcome.Exile); - staticText = "Search target player's library for a card and exile it. Then that player shuffles their library."; + staticText = "Search target player's library for a card and exile it. Then that player shuffles."; } BitterOrdealEffect(final BitterOrdealEffect effect) { diff --git a/Mage.Sets/src/mage/cards/b/BitterheartWitch.java b/Mage.Sets/src/mage/cards/b/BitterheartWitch.java index f2e69996de1a..18514a76b42e 100644 --- a/Mage.Sets/src/mage/cards/b/BitterheartWitch.java +++ b/Mage.Sets/src/mage/cards/b/BitterheartWitch.java @@ -62,7 +62,7 @@ class BitterheartWitchEffect extends OneShotEffect { public BitterheartWitchEffect() { super(Outcome.Detriment); - staticText = "you may search your library for a Curse card, put it onto the battlefield attached to target player, then shuffle your library"; + staticText = "you may search your library for a Curse card, put it onto the battlefield attached to target player, then shuffle"; } public BitterheartWitchEffect(final BitterheartWitchEffect effect) { diff --git a/Mage.Sets/src/mage/cards/b/BlackCarriage.java b/Mage.Sets/src/mage/cards/b/BlackCarriage.java index 91670c7f1ad2..fee00bb8ab0e 100644 --- a/Mage.Sets/src/mage/cards/b/BlackCarriage.java +++ b/Mage.Sets/src/mage/cards/b/BlackCarriage.java @@ -40,7 +40,7 @@ public BlackCarriage(UUID ownerId, CardSetInfo setInfo) { // Sacrifice a creature: Untap Black Carriage. Activate this ability only during your upkeep. this.addAbility(new ConditionalActivatedAbility(Zone.BATTLEFIELD, new UntapSourceEffect(), new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT)), - new IsStepCondition(PhaseStep.UPKEEP), "Sacrifice a creature: Untap {this}. Activate this ability only during your upkeep.")); + new IsStepCondition(PhaseStep.UPKEEP), "Sacrifice a creature: Untap {this}. Activate only during your upkeep.")); } private BlackCarriage(final BlackCarriage card) { diff --git a/Mage.Sets/src/mage/cards/b/BlackOakOfOdunos.java b/Mage.Sets/src/mage/cards/b/BlackOakOfOdunos.java index a78b00541d00..4c2ed32b4a22 100644 --- a/Mage.Sets/src/mage/cards/b/BlackOakOfOdunos.java +++ b/Mage.Sets/src/mage/cards/b/BlackOakOfOdunos.java @@ -17,7 +17,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TappedPredicate; import mage.target.common.TargetControlledCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/b/BlackbladeReforged.java b/Mage.Sets/src/mage/cards/b/BlackbladeReforged.java index 884afe27116a..87425e23a0e4 100644 --- a/Mage.Sets/src/mage/cards/b/BlackbladeReforged.java +++ b/Mage.Sets/src/mage/cards/b/BlackbladeReforged.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.cards.b; import mage.abilities.common.SimpleStaticAbility; diff --git a/Mage.Sets/src/mage/cards/b/BladeHistorian.java b/Mage.Sets/src/mage/cards/b/BladeHistorian.java new file mode 100644 index 000000000000..39a733075779 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BladeHistorian.java @@ -0,0 +1,45 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BladeHistorian extends CardImpl { + + public BladeHistorian(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R/W}{R/W}{R/W}{R/W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Attacking creatures you control have double strike. + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + DoubleStrikeAbility.getInstance(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_ATTACKING_CREATURES + ))); + } + + private BladeHistorian(final BladeHistorian card) { + super(card); + } + + @Override + public BladeHistorian copy() { + return new BladeHistorian(this); + } +} +// I studied the blade. diff --git a/Mage.Sets/src/mage/cards/b/BlastOfGenius.java b/Mage.Sets/src/mage/cards/b/BlastOfGenius.java index 781117bec265..3bdc32a06419 100644 --- a/Mage.Sets/src/mage/cards/b/BlastOfGenius.java +++ b/Mage.Sets/src/mage/cards/b/BlastOfGenius.java @@ -42,7 +42,7 @@ class BlastOfGeniusEffect extends OneShotEffect { public BlastOfGeniusEffect() { super(Outcome.Benefit); - this.staticText = "Choose any target. Draw three cards and discard a card. {this} deals damage equal to the converted mana cost of the discard card to that permanent or player"; + this.staticText = "Choose any target. Draw three cards, then discard a card. {this} deals damage equal to the discard card's mana value to that permanent or player"; } public BlastOfGeniusEffect(final BlastOfGeniusEffect effect) { @@ -65,7 +65,7 @@ public boolean apply(Game game, Ability source) { Card card = player.getHand().get(target.getFirstTarget(), game); if (card != null) { player.discard(card, false, source, game); - int damage = card.getConvertedManaCost(); + int damage = card.getManaValue(); Permanent creature = game.getPermanent(this.getTargetPointer().getFirst(game, source)); if (creature != null) { creature.damage(damage, source.getSourceId(), source, game, false, true); diff --git a/Mage.Sets/src/mage/cards/b/BlastZone.java b/Mage.Sets/src/mage/cards/b/BlastZone.java index 5f33f3a11d8e..94a9728508cb 100644 --- a/Mage.Sets/src/mage/cards/b/BlastZone.java +++ b/Mage.Sets/src/mage/cards/b/BlastZone.java @@ -20,7 +20,7 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterNonlandPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; @@ -37,9 +37,9 @@ public BlastZone(UUID ownerId, CardSetInfo setInfo) { // Blast Zone enters the battlefield with a charge counter on it. this.addAbility(new EntersBattlefieldAbility( - new AddCountersSourceEffect(CounterType.CHARGE.createInstance(1)) - ,"with a charge counter") - ); + new AddCountersSourceEffect(CounterType.CHARGE.createInstance(1)), + "with a charge counter on it" + )); // {T}: Add {C}. this.addAbility(new ColorlessManaAbility()); @@ -72,7 +72,7 @@ class BlastZoneEffect extends OneShotEffect { BlastZoneEffect() { super(Outcome.Benefit); - staticText = "Destroy each nonland permanent with converted mana cost " + + staticText = "Destroy each nonland permanent with mana value " + "equal to the number of charge counters on {this}"; } @@ -90,7 +90,7 @@ public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); int xValue = permanent.getCounters(game).getCount(CounterType.CHARGE); FilterPermanent filter = new FilterNonlandPermanent(); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, xValue)); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, xValue)); return new DestroyAllEffect(filter).apply(game, source); } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/b/BlatantThievery.java b/Mage.Sets/src/mage/cards/b/BlatantThievery.java index 3420f9c825ae..9a754b680680 100644 --- a/Mage.Sets/src/mage/cards/b/BlatantThievery.java +++ b/Mage.Sets/src/mage/cards/b/BlatantThievery.java @@ -1,26 +1,23 @@ package mage.cards.b; -import java.util.*; import mage.abilities.Ability; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.OneShotEffect; 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.constants.Outcome; 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.Target; import mage.target.TargetPermanent; import mage.target.targetadjustment.TargetAdjuster; -import mage.target.targetpointer.FixedTarget; +import mage.target.targetpointer.EachTargetPointer; + +import java.util.UUID; /** - * * @author emerald000 */ public final class BlatantThievery extends CardImpl { @@ -29,7 +26,9 @@ public BlatantThievery(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{U}{U}{U}"); // For each opponent, gain control of target permanent that player controls. - this.getSpellAbility().addEffect(new BlatantThieveryEffect()); + 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); } @@ -51,41 +50,16 @@ 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) { - FilterPermanent filter = new FilterPermanent("Permanent of player " + opponent.getName()); - filter.add(new ControllerIdPredicate(opponentId)); - TargetPermanent targetPermanent = new TargetPermanent(filter); - ability.addTarget(targetPermanent); - } - } - } -} - -class BlatantThieveryEffect extends OneShotEffect { - - BlatantThieveryEffect() { - super(Outcome.GainControl); - this.staticText = "For each opponent, gain control of target permanent that player controls"; - } - - BlatantThieveryEffect(final BlatantThieveryEffect effect) { - super(effect); - } - - @Override - public BlatantThieveryEffect copy() { - return new BlatantThieveryEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - for (Target target : source.getTargets()) { - if (target.getFirstTarget() != null) { - ContinuousEffect effect = new GainControlTargetEffect(Duration.EndOfGame); - effect.setTargetPointer(new FixedTarget(target.getFirstTarget())); - game.addEffect(effect, source); + if (opponent == null || game.getBattlefield().count( + StaticFilters.FILTER_CONTROLLED_PERMANENT, + ability.getSourceId(), opponentId, 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); } - return true; } } diff --git a/Mage.Sets/src/mage/cards/b/BlazeCommando.java b/Mage.Sets/src/mage/cards/b/BlazeCommando.java index c3ba82d68fec..ceb15534f659 100644 --- a/Mage.Sets/src/mage/cards/b/BlazeCommando.java +++ b/Mage.Sets/src/mage/cards/b/BlazeCommando.java @@ -84,7 +84,8 @@ public void reset(Game game) { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER || event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT + || event.getType() == GameEvent.EventType.DAMAGED_PLAYER; } @Override @@ -92,7 +93,7 @@ public boolean checkTrigger(GameEvent event, Game game) { if (isControlledBy(game.getControllerId(event.getSourceId()))) { MageObject damageSource = game.getObject(event.getSourceId()); if (damageSource != null) { - if (damageSource.isInstant()|| damageSource.isSorcery()) { + if (damageSource.isInstantOrSorcery()) { if (!handledStackObjects.contains(damageSource.getId())) { handledStackObjects.add(damageSource.getId()); return true; diff --git a/Mage.Sets/src/mage/cards/b/BlazingEffigy.java b/Mage.Sets/src/mage/cards/b/BlazingEffigy.java index a25fd23543d4..68c7af949662 100644 --- a/Mage.Sets/src/mage/cards/b/BlazingEffigy.java +++ b/Mage.Sets/src/mage/cards/b/BlazingEffigy.java @@ -89,7 +89,7 @@ public BlazingEffigyWatcher() { @Override public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.DAMAGED_CREATURE) { + if (event.getType() == GameEvent.EventType.DAMAGED_PERMANENT) { if (!event.getSourceId().equals(event.getTargetId())) { MageObjectReference damageSourceRef = new MageObjectReference(event.getSourceId(), game); MageObjectReference damageTargetRef = new MageObjectReference(event.getTargetId(), game); diff --git a/Mage.Sets/src/mage/cards/b/BlazingShoal.java b/Mage.Sets/src/mage/cards/b/BlazingShoal.java index 51892093df1e..38cacbfced3e 100644 --- a/Mage.Sets/src/mage/cards/b/BlazingShoal.java +++ b/Mage.Sets/src/mage/cards/b/BlazingShoal.java @@ -32,7 +32,7 @@ public BlazingShoal(UUID ownerId, CardSetInfo setInfo) { // You may exile a red card with converted mana cost X from your hand rather than pay Blazing Shoal's mana cost. - FilterOwnedCard filter = new FilterOwnedCard("a red card with converted mana cost X from your hand"); + FilterOwnedCard filter = new FilterOwnedCard("a red card with mana value X from your hand"); filter.add(new ColorPredicate(ObjectColor.RED)); filter.add(Predicates.not(new CardIdPredicate(this.getId()))); // the exile cost can never be paid with the card itself this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(filter),true))); diff --git a/Mage.Sets/src/mage/cards/b/BlazingSunsteel.java b/Mage.Sets/src/mage/cards/b/BlazingSunsteel.java index 59e13f062810..eb9218ad7f02 100644 --- a/Mage.Sets/src/mage/cards/b/BlazingSunsteel.java +++ b/Mage.Sets/src/mage/cards/b/BlazingSunsteel.java @@ -72,7 +72,7 @@ public BlazingSunsteelTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/b/BlexVexingPest.java b/Mage.Sets/src/mage/cards/b/BlexVexingPest.java new file mode 100644 index 000000000000..0c0a87da95c0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BlexVexingPest.java @@ -0,0 +1,122 @@ +package mage.cards.b; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LookLibraryControllerEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.cards.ModalDoubleFacesCard; +import mage.constants.*; +import mage.cards.CardSetInfo; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; + +/** + * + * @author weirddan455 + */ +public final class BlexVexingPest extends ModalDoubleFacesCard { + + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("Pests, Bats, Insects, Snakes, and Spiders"); + + static { + filter.add(Predicates.or( + SubType.PEST.getPredicate(), + SubType.BAT.getPredicate(), + SubType.INSECT.getPredicate(), + SubType.SNAKE.getPredicate(), + SubType.SPIDER.getPredicate() + )); + } + + public BlexVexingPest(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.PEST}, "{2}{G}", + "Search for Blex", new CardType[]{CardType.SORCERY}, new SubType[]{}, "{2}{B}{B}" + ); + + // 1. + // Blex, Vexing Pest + // Legendary Creature - Pest + this.getLeftHalfCard().addSuperType(SuperType.LEGENDARY); + this.getLeftHalfCard().setPT(new MageInt(3), new MageInt(2)); + + // Other Pests, Bats, Insects, Snakes, and Spiders you control get +1/+1. + this.getLeftHalfCard().addAbility(new SimpleStaticAbility( + new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filter, true) + )); + + // When Blex, Vexing Pest dies, you gain 4 life. + this.getLeftHalfCard().addAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(4))); + + // 2. + // Search for Blex + // Sorcery + + // Look at the top five cards of your library. + // You may put any number of them into your hand and the rest into your graveyard. + // You lose 3 life for each card put into your hand this way. + this.getRightHalfCard().getSpellAbility().addEffect(new SearchForBlexEffect()); + } + + private BlexVexingPest(final BlexVexingPest card) { + super(card); + } + + @Override + public BlexVexingPest copy() { + return new BlexVexingPest(this); + } +} + +class SearchForBlexEffect extends LookLibraryControllerEffect { + + private static final FilterCard filter = new FilterCard("cards to put into your hand"); + + public SearchForBlexEffect() { + super(Outcome.DrawCard, StaticValue.get(5), false, Zone.GRAVEYARD, true); + this.staticText = "Look at the top five cards of your library. " + + "You may put any number of them into your hand and the rest into your graveyard. " + + "You lose 3 life for each card you put into your hand this way"; + } + + private SearchForBlexEffect(final SearchForBlexEffect effect) { + super(effect); + } + + @Override + public SearchForBlexEffect copy() { + return new SearchForBlexEffect(this); + } + + @Override + protected void actionWithSelectedCards(Cards cards, Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player != null) { + TargetCard target = new TargetCard(0, 5, Zone.LIBRARY, filter); + if (player.choose(outcome, cards, target, game)) { + Cards pickedCards = new CardsImpl(target.getTargets()); + cards.removeAll(pickedCards); + player.moveCards(pickedCards, Zone.HAND, source, game); + player.loseLife(pickedCards.size() * 3, game, source, false); + } + } + } + + @Override + public String getText(Mode mode) { + return staticText; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BlightMound.java b/Mage.Sets/src/mage/cards/b/BlightMound.java new file mode 100644 index 000000000000..9628bc7a9b70 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BlightMound.java @@ -0,0 +1,66 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.AttackingPredicate; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.permanent.token.WitherbloomToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BlightMound extends CardImpl { + + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent(SubType.PEST, "attacking Pests"); + private static final FilterPermanent filter2 + = new FilterControlledCreaturePermanent("a nontoken creature you control"); + + static { + filter.add(AttackingPredicate.instance); + filter2.add(Predicates.not(TokenPredicate.instance)); + } + + public BlightMound(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); + + // Attacking Pests you control get +1/+0 and have menace. + Ability ability = new SimpleStaticAbility(new BoostControlledEffect( + 1, 0, Duration.WhileOnBattlefield, filter + )); + ability.addEffect(new GainAbilityControlledEffect( + new MenaceAbility(), Duration.WhileOnBattlefield, filter + ).setText("and have menace")); + this.addAbility(ability); + + // Whenever a nontoken creature you control dies, create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life." + this.addAbility(new DiesCreatureTriggeredAbility( + new CreateTokenEffect(new WitherbloomToken()), false, filter2 + )); + } + + private BlightMound(final BlightMound card) { + super(card); + } + + @Override + public BlightMound copy() { + return new BlightMound(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/Blightspeaker.java b/Mage.Sets/src/mage/cards/b/Blightspeaker.java index e23e6262eaaf..2df75214dab1 100644 --- a/Mage.Sets/src/mage/cards/b/Blightspeaker.java +++ b/Mage.Sets/src/mage/cards/b/Blightspeaker.java @@ -16,7 +16,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetPlayer; import mage.target.common.TargetCardInLibrary; @@ -26,11 +26,11 @@ */ public final class Blightspeaker extends CardImpl { - private static final FilterPermanentCard filter = new FilterPermanentCard("Rebel permanent card with converted mana cost 3 or less"); + private static final FilterPermanentCard filter = new FilterPermanentCard("Rebel permanent card with mana value 3 or less"); static { filter.add(SubType.REBEL.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public Blightspeaker(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/b/BlindFury.java b/Mage.Sets/src/mage/cards/b/BlindFury.java index d97aae48c13d..27b859747461 100644 --- a/Mage.Sets/src/mage/cards/b/BlindFury.java +++ b/Mage.Sets/src/mage/cards/b/BlindFury.java @@ -11,7 +11,7 @@ import mage.constants.Outcome; import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.events.DamageCreatureEvent; +import mage.game.events.DamageEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.util.CardUtil; @@ -63,7 +63,7 @@ public FurnaceOfRathEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT; } @Override @@ -71,7 +71,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { Permanent permanent = game.getPermanent(event.getSourceId()); return permanent != null && permanent.isCreature() - && ((DamageCreatureEvent) event).isCombatDamage(); + && ((DamageEvent) event).isCombatDamage(); } diff --git a/Mage.Sets/src/mage/cards/b/BlindSpotGiant.java b/Mage.Sets/src/mage/cards/b/BlindSpotGiant.java index e0a21ff64616..e040ce790691 100644 --- a/Mage.Sets/src/mage/cards/b/BlindSpotGiant.java +++ b/Mage.Sets/src/mage/cards/b/BlindSpotGiant.java @@ -12,7 +12,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/b/BlinkmothUrn.java b/Mage.Sets/src/mage/cards/b/BlinkmothUrn.java index 27caf50a9474..23a6f1b71577 100644 --- a/Mage.Sets/src/mage/cards/b/BlinkmothUrn.java +++ b/Mage.Sets/src/mage/cards/b/BlinkmothUrn.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.cards.b; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/b/BlizzardStrix.java b/Mage.Sets/src/mage/cards/b/BlizzardStrix.java index 58f7b35b9c07..b45eaca2a0c8 100644 --- a/Mage.Sets/src/mage/cards/b/BlizzardStrix.java +++ b/Mage.Sets/src/mage/cards/b/BlizzardStrix.java @@ -16,7 +16,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/b/BloodAgeGeneral.java b/Mage.Sets/src/mage/cards/b/BloodAgeGeneral.java new file mode 100644 index 000000000000..456ee92aa637 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BloodAgeGeneral.java @@ -0,0 +1,51 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.common.FilterAttackingCreature; +import mage.filter.common.FilterCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BloodAgeGeneral extends CardImpl { + + private static final FilterCreaturePermanent filter + = new FilterAttackingCreature("attacking Spirits"); + + static { + filter.add(SubType.SPIRIT.getPredicate()); + } + + public BloodAgeGeneral(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // {T}: Attacking Spirits get +1/+0 until end of turn. + this.addAbility(new SimpleActivatedAbility(new BoostAllEffect( + 1, 0, Duration.EndOfTurn, filter, false + ), new TapSourceCost())); + } + + private BloodAgeGeneral(final BloodAgeGeneral card) { + super(card); + } + + @Override + public BloodAgeGeneral copy() { + return new BloodAgeGeneral(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BloodChinFanatic.java b/Mage.Sets/src/mage/cards/b/BloodChinFanatic.java index 03df4345dac1..89951ed98990 100644 --- a/Mage.Sets/src/mage/cards/b/BloodChinFanatic.java +++ b/Mage.Sets/src/mage/cards/b/BloodChinFanatic.java @@ -17,7 +17,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPlayer; import mage.target.common.TargetControlledPermanent; diff --git a/Mage.Sets/src/mage/cards/b/BloodClock.java b/Mage.Sets/src/mage/cards/b/BloodClock.java index dbbc9cb072d3..90739872eff4 100644 --- a/Mage.Sets/src/mage/cards/b/BloodClock.java +++ b/Mage.Sets/src/mage/cards/b/BloodClock.java @@ -1,9 +1,8 @@ - package mage.cards.b; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.costs.common.PayLifeCost; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -17,18 +16,20 @@ import mage.target.Target; import mage.target.common.TargetControlledPermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class BloodClock extends CardImpl { public BloodClock(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); // At the beginning of each player's upkeep, that player returns a permanent they control to its owner's hand unless they pay 2 life. - Ability ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new BloodClockEffect(), TargetController.ANY, false, true); - this.addAbility(ability); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + Zone.BATTLEFIELD, new BloodClockEffect(), TargetController.ANY, false, true + )); } private BloodClock(final BloodClock card) { @@ -43,12 +44,12 @@ public BloodClock copy() { class BloodClockEffect extends OneShotEffect { - public BloodClockEffect() { + BloodClockEffect() { super(Outcome.ReturnToHand); this.staticText = "that player returns a permanent they control to its owner's hand unless they pay 2 life"; } - public BloodClockEffect(final BloodClockEffect effect) { + private BloodClockEffect(final BloodClockEffect effect) { super(effect); } @@ -63,20 +64,23 @@ public boolean apply(Game game, Ability source) { if (player == null) { return false; } - if (player.getLife() > 2 && player.chooseUse(Outcome.Neutral, "Pay 2 life? If you don't, return a permanent you control to its owner's hand.", source, game)) { - player.loseLife(2, game, source, false); - game.informPlayers(player.getLogName() + " pays 2 life. They will not return a permanent they control."); + PayLifeCost cost = new PayLifeCost(2); + if (cost.canPay(source, source, player.getId(), game) && player.chooseUse( + Outcome.Neutral, "Pay 2 life? If you don't, " + + "return a permanent you control to its owner's hand.", source, game + ) && cost.pay(source, game, source, player.getId(), true)) { return true; - } else { - Target target = new TargetControlledPermanent(); - if (target.canChoose(source.getSourceId(), player.getId(), game) && player.chooseTarget(outcome, target, source, game)) { - Permanent permanent = game.getPermanent(target.getFirstTarget()); - if (permanent != null) { - game.informPlayers(player.getLogName() + " returns " + permanent.getName() + " to hand."); - return permanent.moveToZone(Zone.HAND, source, game, false); - } - } } - return false; + Target target = new TargetControlledPermanent(); + target.setNotTarget(true); + if (!target.canChoose(source.getSourceId(), player.getId(), game) + || !player.chooseTarget(outcome, target, source, game)) { + return false; + } + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent == null) { + return false; + } + return player.moveCards(permanent, Zone.HAND, source, game); } } diff --git a/Mage.Sets/src/mage/cards/b/BloodFeud.java b/Mage.Sets/src/mage/cards/b/BloodFeud.java index bfd93fcffbd0..c2a9319a4af9 100644 --- a/Mage.Sets/src/mage/cards/b/BloodFeud.java +++ b/Mage.Sets/src/mage/cards/b/BloodFeud.java @@ -7,7 +7,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/b/BloodOfTheMartyr.java b/Mage.Sets/src/mage/cards/b/BloodOfTheMartyr.java index 11ee3bafa662..4c4c25ebd2dc 100644 --- a/Mage.Sets/src/mage/cards/b/BloodOfTheMartyr.java +++ b/Mage.Sets/src/mage/cards/b/BloodOfTheMartyr.java @@ -12,6 +12,7 @@ import mage.game.Game; import mage.game.events.DamageEvent; import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; import mage.players.Player; /** @@ -55,7 +56,7 @@ public BloodOfTheMartyrEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT; } @Override @@ -77,8 +78,11 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { @Override public boolean applies(GameEvent event, Ability source, Game game) { Player controller = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(event.getTargetId()); DamageEvent damageEvent = (DamageEvent) event; return controller != null - && controller.chooseUse(outcome, "Would you like to have " + damageEvent.getAmount() + " damage dealt to you instead of " + game.getPermanentOrLKIBattlefield(damageEvent.getTargetId()).getLogName() + "?", source, game); + && permanent != null + && permanent.isCreature() + && controller.chooseUse(outcome, "Have " + damageEvent.getAmount() + " damage dealt to you instead of " + permanent.getLogName() + "?", source, game); } } diff --git a/Mage.Sets/src/mage/cards/b/BloodOnTheSnow.java b/Mage.Sets/src/mage/cards/b/BloodOnTheSnow.java index 64f36ed344f9..fc935680f52d 100644 --- a/Mage.Sets/src/mage/cards/b/BloodOnTheSnow.java +++ b/Mage.Sets/src/mage/cards/b/BloodOnTheSnow.java @@ -13,7 +13,7 @@ import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; @@ -42,7 +42,7 @@ public BloodOnTheSnow(UUID ownerId, CardSetInfo setInfo) { mode.addEffect(new BloodOnTheSnowEffect()); this.getSpellAbility().addMode(mode); this.getSpellAbility().appendToRule( - "Then return a creature or planeswalker card with converted mana cost X or less" + "Then return a creature or planeswalker card with mana value X or less" + " from your graveyard to the battlefield, where X is the amount of {S} spent to cast this spell." ); } @@ -77,9 +77,9 @@ public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { int snow = source.getManaCostsToPay().getUsedManaToPay().getSnow(); - FilterCard filter = new FilterCard("a creature or planeswalker card with CMC " + snow + " or less from your graveyard"); + FilterCard filter = new FilterCard("a creature or planeswalker card with mana value " + snow + " or less from your graveyard"); filter.add(Predicates.or(CardType.CREATURE.getPredicate(), CardType.PLANESWALKER.getPredicate())); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, snow + 1)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, snow + 1)); TargetCardInYourGraveyard target = new TargetCardInYourGraveyard(1, 1, filter, true); controller.chooseTarget(outcome, target, source, game); Card card = game.getCard(target.getFirstTarget()); diff --git a/Mage.Sets/src/mage/cards/b/BloodResearcher.java b/Mage.Sets/src/mage/cards/b/BloodResearcher.java new file mode 100644 index 000000000000..03708ec147da --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BloodResearcher.java @@ -0,0 +1,45 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.common.GainLifeControllerTriggeredAbility; +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.SubType; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BloodResearcher extends CardImpl { + + public BloodResearcher(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{G}"); + + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Menace + this.addAbility(new MenaceAbility()); + + // Whenever you gain life, put a +1/+1 counter on Blood Researcher. + this.addAbility(new GainLifeControllerTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false + )); + } + + private BloodResearcher(final BloodResearcher card) { + super(card); + } + + @Override + public BloodResearcher copy() { + return new BloodResearcher(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BloodScrivener.java b/Mage.Sets/src/mage/cards/b/BloodScrivener.java index a19d7fa7181c..39d3f785cbec 100644 --- a/Mage.Sets/src/mage/cards/b/BloodScrivener.java +++ b/Mage.Sets/src/mage/cards/b/BloodScrivener.java @@ -49,7 +49,7 @@ class BloodScrivenerReplacementEffect extends ReplacementEffectImpl { public BloodScrivenerReplacementEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "If you would draw a card while you have no cards in hand, instead draw two cards and lose 1 life"; + staticText = "If you would draw a card while you have no cards in hand, instead you draw two cards and you lose 1 life"; } public BloodScrivenerReplacementEffect(final BloodScrivenerReplacementEffect effect) { diff --git a/Mage.Sets/src/mage/cards/b/Bloodbriar.java b/Mage.Sets/src/mage/cards/b/Bloodbriar.java index 4a3866a61dd4..ed3c14c8197a 100644 --- a/Mage.Sets/src/mage/cards/b/Bloodbriar.java +++ b/Mage.Sets/src/mage/cards/b/Bloodbriar.java @@ -12,7 +12,7 @@ import mage.constants.TargetController; import mage.counters.CounterType; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/b/BloodchiefsThirst.java b/Mage.Sets/src/mage/cards/b/BloodchiefsThirst.java index a432b935ab81..fdbbe32ce9b5 100644 --- a/Mage.Sets/src/mage/cards/b/BloodchiefsThirst.java +++ b/Mage.Sets/src/mage/cards/b/BloodchiefsThirst.java @@ -10,7 +10,7 @@ import mage.constants.ComparisonType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.target.TargetPermanent; import mage.target.common.TargetCreatureOrPlaneswalker; @@ -31,7 +31,7 @@ public BloodchiefsThirst(UUID ownerId, CardSetInfo setInfo) { // Destroy target creature or planeswalker with converted mana cost 2 or less. If this spell was kicked, instead destroy target creature or planeswalker. this.getSpellAbility().addEffect(new DestroyTargetEffect( - "Destroy target creature or planeswalker with converted mana cost 2 or less. " + + "Destroy target creature or planeswalker with mana value 2 or less. " + "If this spell was kicked, instead destroy target creature or planeswalker." )); this.getSpellAbility().setTargetAdjuster(BloodchiefsThirstAdjuster.instance); @@ -51,11 +51,11 @@ enum BloodchiefsThirstAdjuster implements TargetAdjuster { instance; private static final FilterPermanent filter = new FilterCreatureOrPlaneswalkerPermanent( - "creature or planeswalker with converted mana cost 2 or less" + "creature or planeswalker with mana value 2 or less" ); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); } @Override diff --git a/Mage.Sets/src/mage/cards/b/BloodlinePretender.java b/Mage.Sets/src/mage/cards/b/BloodlinePretender.java index 1da57f4a724a..9cc4b8efb103 100644 --- a/Mage.Sets/src/mage/cards/b/BloodlinePretender.java +++ b/Mage.Sets/src/mage/cards/b/BloodlinePretender.java @@ -15,7 +15,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ChosenSubtypePredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/b/BloodmistInfiltrator.java b/Mage.Sets/src/mage/cards/b/BloodmistInfiltrator.java index 1a523bb73956..ddf7cf914727 100644 --- a/Mage.Sets/src/mage/cards/b/BloodmistInfiltrator.java +++ b/Mage.Sets/src/mage/cards/b/BloodmistInfiltrator.java @@ -12,7 +12,7 @@ import mage.constants.SubType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/b/BloodpyreElemental.java b/Mage.Sets/src/mage/cards/b/BloodpyreElemental.java index b1eaad371e65..bc61c9a52a12 100644 --- a/Mage.Sets/src/mage/cards/b/BloodpyreElemental.java +++ b/Mage.Sets/src/mage/cards/b/BloodpyreElemental.java @@ -28,7 +28,7 @@ public BloodpyreElemental(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(1); // Sacrifice Bloodpyre Elemental: Bloodpyre Elemental deals 4 damage to target creature. Activate this ability only any time you could cast a sorcery. - Ability ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(4), new SacrificeSourceCost()); + Ability ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(4, "it"), new SacrificeSourceCost()); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/BloodshotTrainee.java b/Mage.Sets/src/mage/cards/b/BloodshotTrainee.java index 18b6712b38f6..a46fbb0e55ef 100644 --- a/Mage.Sets/src/mage/cards/b/BloodshotTrainee.java +++ b/Mage.Sets/src/mage/cards/b/BloodshotTrainee.java @@ -1,12 +1,9 @@ - package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.condition.Condition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; @@ -18,23 +15,26 @@ import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author North */ public final class BloodshotTrainee extends CardImpl { public BloodshotTrainee(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); this.subtype.add(SubType.GOBLIN); this.subtype.add(SubType.WARRIOR); this.power = new MageInt(2); this.toughness = new MageInt(3); - SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(4), new TapSourceCost()); + Ability ability = new ActivateIfConditionActivatedAbility( + Zone.BATTLEFIELD, new DamageTargetEffect(4), + new TapSourceCost(), BloodshotTraineeCondition.instance + ); ability.addTarget(new TargetCreaturePermanent()); - ability.addCost(new BloodshotTraineeCost()); this.addAbility(ability); } @@ -48,35 +48,17 @@ public BloodshotTrainee copy() { } } -class BloodshotTraineeCost extends CostImpl { - - public BloodshotTraineeCost() { - this.text = "Activate this ability only if Bloodshot Trainee's power is 4 or greater"; - } - - public BloodshotTraineeCost(final BloodshotTraineeCost cost) { - super(cost); - } - - @Override - public BloodshotTraineeCost copy() { - return new BloodshotTraineeCost(this); - } +enum BloodshotTraineeCondition implements Condition { + instance; @Override - public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - if (permanent.getPower().getValue() >= 4) { - return true; - } - } - return false; + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + return permanent != null && permanent.getPower().getValue() >= 4; } @Override - public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - this.paid = true; - return paid; + public String toString() { + return "{this}'s power is 4 or greater"; } } diff --git a/Mage.Sets/src/mage/cards/b/BloodsoakedChampion.java b/Mage.Sets/src/mage/cards/b/BloodsoakedChampion.java index 2e7a093a3c67..fc6dd14922eb 100644 --- a/Mage.Sets/src/mage/cards/b/BloodsoakedChampion.java +++ b/Mage.Sets/src/mage/cards/b/BloodsoakedChampion.java @@ -39,7 +39,7 @@ public BloodsoakedChampion(UUID ownerId, CardSetInfo setInfo) { new ReturnSourceFromGraveyardToBattlefieldEffect(), new ManaCostsImpl<>("{1}{B}"), RaidCondition.instance, - "Raid — {1}{B}: Return {this} from your graveyard to the battlefield. Activate this ability only if you attacked this turn"); + "Raid — {1}{B}: Return {this} from your graveyard to the battlefield. Activate only if you attacked this turn."); ability.setAbilityWord(AbilityWord.RAID); ability.addHint(RaidHint.instance); this.addAbility(ability, new PlayerAttackedWatcher()); diff --git a/Mage.Sets/src/mage/cards/b/BloodstainedMire.java b/Mage.Sets/src/mage/cards/b/BloodstainedMire.java index 67dfdeabd714..d57f4219a17c 100644 --- a/Mage.Sets/src/mage/cards/b/BloodstainedMire.java +++ b/Mage.Sets/src/mage/cards/b/BloodstainedMire.java @@ -21,7 +21,7 @@ public BloodstainedMire(UUID ownerId, CardSetInfo setInfo) { this.frameColor = new ObjectColor("RB"); // {tap}, Pay 1 life, Sacrifice Bloodstained Mire: Search your library for a Swamp or Mountain card and put it onto the battlefield. Then shuffle your library. - this.addAbility(new FetchLandActivatedAbility(EnumSet.of(SubType.SWAMP,SubType.MOUNTAIN))); + this.addAbility(new FetchLandActivatedAbility(SubType.SWAMP,SubType.MOUNTAIN)); } private BloodstainedMire(final BloodstainedMire card) { diff --git a/Mage.Sets/src/mage/cards/b/BloodswornSteward.java b/Mage.Sets/src/mage/cards/b/BloodswornSteward.java index 726c39641a98..9801b696564c 100644 --- a/Mage.Sets/src/mage/cards/b/BloodswornSteward.java +++ b/Mage.Sets/src/mage/cards/b/BloodswornSteward.java @@ -17,7 +17,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.CommanderPredicate; +import mage.filter.predicate.mageobject.CommanderPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/b/BlossomingBogbeast.java b/Mage.Sets/src/mage/cards/b/BlossomingBogbeast.java new file mode 100644 index 000000000000..3c00ed0013d6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BlossomingBogbeast.java @@ -0,0 +1,53 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.dynamicvalue.common.ControllerGotLifeCount; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +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.filter.StaticFilters; +import mage.watchers.common.PlayerGainedLifeWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BlossomingBogbeast extends CardImpl { + + public BlossomingBogbeast(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}"); + + this.subtype.add(SubType.BEAST); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Whenever Blossoming Bogbeast attacks, you gain 2 life. Then creatures you control gain trample and get +X/+X until end of turn, where X is the amount of life you gained this turn. + Ability ability = new AttacksTriggeredAbility(new GainLifeEffect(2), false); + ability.addEffect(new GainAbilityControlledEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_CONTROLLED_CREATURE + ).setText("Then creatures you control gain trample")); + ability.addEffect(new BoostControlledEffect( + ControllerGotLifeCount.instance, ControllerGotLifeCount.instance, Duration.EndOfTurn + ).setText("and get +X/+X until end of turn, where X is the amount of life you gained this turn")); + this.addAbility(ability.addHint(ControllerGotLifeCount.getHint()), new PlayerGainedLifeWatcher()); + } + + private BlossomingBogbeast(final BlossomingBogbeast card) { + super(card); + } + + @Override + public BlossomingBogbeast copy() { + return new BlossomingBogbeast(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BlotOutTheSky.java b/Mage.Sets/src/mage/cards/b/BlotOutTheSky.java new file mode 100644 index 000000000000..c303992fdc94 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BlotOutTheSky.java @@ -0,0 +1,61 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DestroyAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterNonlandPermanent; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.permanent.token.SilverquillToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BlotOutTheSky extends CardImpl { + + private static final FilterPermanent filter = new FilterNonlandPermanent(); + + static { + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); + } + + public BlotOutTheSky(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{W}{B}"); + + // Create X tapped 2/1 white and black Inkling creature tokens with flying. If X is 6 or more, destroy all noncreature, nonland permanents. + this.getSpellAbility().addEffect(new CreateTokenEffect( + new SilverquillToken(), ManacostVariableValue.instance, true, false + )); + this.getSpellAbility().addEffect(new ConditionalOneShotEffect( + new DestroyAllEffect(filter), BlotOutTheSkyCondition.instance, + "If X is 6 or more, destroy all noncreature, nonland permanents" + )); + } + + private BlotOutTheSky(final BlotOutTheSky card) { + super(card); + } + + @Override + public BlotOutTheSky copy() { + return new BlotOutTheSky(this); + } +} + +enum BlotOutTheSkyCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + return source.getManaCostsToPay().getX() >= 6; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BludgeonBrawl.java b/Mage.Sets/src/mage/cards/b/BludgeonBrawl.java index db9daac15f7b..972eb3d2736e 100644 --- a/Mage.Sets/src/mage/cards/b/BludgeonBrawl.java +++ b/Mage.Sets/src/mage/cards/b/BludgeonBrawl.java @@ -61,7 +61,7 @@ public BludgeonBrawlAbility copy() { @Override public String getRule() { - return "Each noncreature, non-Equipment artifact is an Equipment with equip {X} and \"Equipped creature gets +X/+0,\" where X is that artifact's converted mana cost."; + return "Each noncreature, non-Equipment artifact is an Equipment with equip {X} and \"Equipped creature gets +X/+0,\" where X is that artifact's mana value."; } } @@ -121,9 +121,9 @@ public boolean apply(Game game, Ability source) { for (UUID permanentId : permanents) { Permanent permanent = game.getPermanent(permanentId); if (permanent != null) { - int convertedManaCost = permanent.getConvertedManaCost(); - permanent.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(convertedManaCost)), source.getSourceId(), game); - permanent.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(convertedManaCost, 0)), source.getSourceId(), game); + int manaValue = permanent.getManaValue(); + permanent.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(manaValue)), source.getSourceId(), game); + permanent.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(manaValue, 0)), source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/b/BodyOfResearch.java b/Mage.Sets/src/mage/cards/b/BodyOfResearch.java new file mode 100644 index 000000000000..ff02fc8406a5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BodyOfResearch.java @@ -0,0 +1,58 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.Game; +import mage.game.permanent.token.QuandrixToken; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BodyOfResearch extends CardImpl { + + public BodyOfResearch(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{G}{G}{G}{U}{U}{U}"); + + // Create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it, where X is the number of cards in your library. + this.getSpellAbility().addEffect(QuandrixToken.getEffect( + BodyOfResearchValue.instance, "Put X +1/+1 counters on it, " + + "where X is the number of cards in your library" + )); + } + + private BodyOfResearch(final BodyOfResearch card) { + super(card); + } + + @Override + public BodyOfResearch copy() { + return new BodyOfResearch(this); + } +} + +enum BodyOfResearchValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + Player player = game.getPlayer(sourceAbility.getControllerId()); + return player != null ? player.getLibrary().size() : 0; + } + + @Override + public BodyOfResearchValue copy() { + return instance; + } + + @Override + public String getMessage() { + return ""; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BogGlider.java b/Mage.Sets/src/mage/cards/b/BogGlider.java index b5891baca82d..9331d4a6203e 100644 --- a/Mage.Sets/src/mage/cards/b/BogGlider.java +++ b/Mage.Sets/src/mage/cards/b/BogGlider.java @@ -18,7 +18,7 @@ import mage.filter.common.FilterControlledLandPermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetControlledPermanent; @@ -29,11 +29,11 @@ public final class BogGlider extends CardImpl { static final FilterControlledPermanent landFilter = new FilterControlledLandPermanent("a land"); - private static final FilterPermanentCard filter = new FilterPermanentCard("Mercenary permanent card with converted mana cost 2 or less"); + private static final FilterPermanentCard filter = new FilterPermanentCard("Mercenary permanent card with mana value 2 or less"); static { filter.add(SubType.MERCENARY.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); } public BogGlider(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/b/BogardanDragonheart.java b/Mage.Sets/src/mage/cards/b/BogardanDragonheart.java index 23d4cdbd24bf..c788d89a1c38 100644 --- a/Mage.Sets/src/mage/cards/b/BogardanDragonheart.java +++ b/Mage.Sets/src/mage/cards/b/BogardanDragonheart.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.TokenImpl; import mage.target.common.TargetControlledPermanent; diff --git a/Mage.Sets/src/mage/cards/b/BoggartHarbinger.java b/Mage.Sets/src/mage/cards/b/BoggartHarbinger.java index 21193efdeeeb..2e86ea73f3b3 100644 --- a/Mage.Sets/src/mage/cards/b/BoggartHarbinger.java +++ b/Mage.Sets/src/mage/cards/b/BoggartHarbinger.java @@ -34,7 +34,7 @@ public BoggartHarbinger(UUID ownerId, CardSetInfo setInfo) { // When Boggart Harbinger enters the battlefield, you may search your library for a Goblin card, reveal it, // then shuffle your library and put that card on top of it. TargetCardInLibrary target = new TargetCardInLibrary(filter); - this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutOnLibraryEffect(target, true, true))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutOnLibraryEffect(target, true, true),true)); } private BoggartHarbinger(final BoggartHarbinger card) { diff --git a/Mage.Sets/src/mage/cards/b/BolassCitadel.java b/Mage.Sets/src/mage/cards/b/BolassCitadel.java index a3f56f3094fc..cae98223fd2e 100644 --- a/Mage.Sets/src/mage/cards/b/BolassCitadel.java +++ b/Mage.Sets/src/mage/cards/b/BolassCitadel.java @@ -11,16 +11,16 @@ import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.common.LoseLifeOpponentsEffect; import mage.abilities.effects.common.continuous.LookAtTopCardOfLibraryAnyTimeEffect; -import mage.cards.*; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetControlledPermanent; -import mage.util.CardUtil; -import java.util.Objects; import java.util.UUID; @@ -70,7 +70,7 @@ class BolassCitadelPlayTheTopCardEffect extends AsThoughEffectImpl { super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.AIDontUseIt); // AI will need help with this staticText = "You may play lands and cast spells from the top of your library. If you cast a spell this way, " - + "pay life equal to its converted mana cost rather than pay its mana cost."; + + "pay life equal to its mana value rather than pay its mana cost."; } private BolassCitadelPlayTheTopCardEffect(final BolassCitadelPlayTheTopCardEffect effect) { @@ -89,69 +89,37 @@ public BolassCitadelPlayTheTopCardEffect copy() { @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - return applies(objectId, null, source, game, affectedControllerId); - } + // current card's part + Card cardToCheck = game.getCard(objectId); + if (cardToCheck == null) { + return false; + } - @Override - public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) { - if (!Objects.equals(source.getControllerId(), playerId)) { + // must be you + if (!affectedControllerId.equals(source.getControllerId())) { return false; } - Player player = game.getPlayer(playerId); - if (player != null) { - Card topCard = player.getLibrary().getFromTop(game); - UUID objectIdToCast = CardUtil.getMainCardId(game, objectId); // for adventure cards - if (topCard == null || !topCard.getId().equals(objectIdToCast)) { - return false; - } - - if (topCard instanceof SplitCard || topCard instanceof ModalDoubleFacesCard) { - // double faces cards - Card card1; - Card card2; - if (topCard instanceof SplitCard) { - card1 = ((SplitCard) topCard).getLeftHalfCard(); - card2 = ((SplitCard) topCard).getRightHalfCard(); - } else { - card1 = ((ModalDoubleFacesCard) topCard).getLeftHalfCard(); - card2 = ((ModalDoubleFacesCard) topCard).getRightHalfCard(); - } - - // left - if (!card1.isLand()) { - PayLifeCost lifeCost = new PayLifeCost(card1.getSpellAbility().getManaCosts().convertedManaCost()); - Costs newCosts = new CostsImpl(); - newCosts.add(lifeCost); - newCosts.addAll(card1.getSpellAbility().getCosts()); - player.setCastSourceIdWithAlternateMana(card1.getId(), null, newCosts); - } - - // right - if (!card2.isLand()) { - PayLifeCost lifeCost = new PayLifeCost(card2.getSpellAbility().getManaCosts().convertedManaCost()); - Costs newCosts = new CostsImpl(); - newCosts.add(lifeCost); - newCosts.addAll(card2.getSpellAbility().getCosts()); - player.setCastSourceIdWithAlternateMana(card2.getId(), null, newCosts); - } - } else { - // other single face cards - if (!topCard.isLand()) { - if (affectedAbility == null) { - affectedAbility = topCard.getSpellAbility(); - } else { - objectIdToCast = affectedAbility.getSourceId(); - } - PayLifeCost cost = new PayLifeCost(affectedAbility.getManaCosts().convertedManaCost()); - Costs costs = new CostsImpl(); - costs.add(cost); - costs.addAll(affectedAbility.getCosts()); - player.setCastSourceIdWithAlternateMana(objectIdToCast, null, costs); - } - } - return true; + // must be your card + Player player = game.getPlayer(cardToCheck.getOwnerId()); + if (player == null || !player.getId().equals(affectedControllerId)) { + return false; } - return false; + + // must be from your library + Card topCard = player.getLibrary().getFromTop(game); + if (topCard == null || !topCard.getId().equals(cardToCheck.getMainCard().getId())) { + return false; + } + + // allows to play/cast with alternative life cost + if (!cardToCheck.isLand()) { + PayLifeCost lifeCost = new PayLifeCost(cardToCheck.getSpellAbility().getManaCosts().manaValue()); + Costs newCosts = new CostsImpl(); + newCosts.add(lifeCost); + newCosts.addAll(cardToCheck.getSpellAbility().getCosts()); + player.setCastSourceIdWithAlternateMana(cardToCheck.getId(), null, newCosts); + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/b/BoldDefense.java b/Mage.Sets/src/mage/cards/b/BoldDefense.java index 6803c907310f..182b91a3c25c 100644 --- a/Mage.Sets/src/mage/cards/b/BoldDefense.java +++ b/Mage.Sets/src/mage/cards/b/BoldDefense.java @@ -30,7 +30,7 @@ public BoldDefense(UUID ownerId, CardSetInfo setInfo) { // Creatures you control get +1/+1 until end of turn. If Bold Defense was kicked, instead creatures you control get +2/+2 and gain first strike until end of turn. this.getSpellAbility().addEffect(new ConditionalContinuousEffect(new BoostControlledEffect(2, 2, Duration.EndOfTurn), new BoostTargetEffect(1, 1, Duration.EndOfTurn), new LockedInCondition(KickedCondition.instance), - "Creatures you control get +1/+1 until end of turn. if this spell was kicked, instead creatures you control get +2/+2")); + "Creatures you control get +1/+1 until end of turn. If this spell was kicked, instead creatures you control get +2/+2")); this.getSpellAbility().addEffect(new ConditionalContinuousEffect(new GainAbilityControlledEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURE, false), null, new LockedInCondition(KickedCondition.instance), "and gain first strike until end of turn")); diff --git a/Mage.Sets/src/mage/cards/b/BoldPlagiarist.java b/Mage.Sets/src/mage/cards/b/BoldPlagiarist.java new file mode 100644 index 000000000000..d22749c8cec0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BoldPlagiarist.java @@ -0,0 +1,122 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.FlashAbility; +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.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * @author Jason Felix + */ +public final class BoldPlagiarist extends CardImpl { + + public BoldPlagiarist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Whenever an opponent puts one or more counters on a creature they control, they put the same number and kind of counters on Bold Plagiarist. + this.addAbility(new BoldPlagiaristTriggeredAbility()); + } + + private BoldPlagiarist(final BoldPlagiarist card) { + super(card); + } + + @Override + public BoldPlagiarist copy() { + return new BoldPlagiarist(this); + } +} + +class BoldPlagiaristTriggeredAbility extends TriggeredAbilityImpl { + + BoldPlagiaristTriggeredAbility() { + super(Zone.BATTLEFIELD, new BoldPlagiaristEffect()); + } + + private BoldPlagiaristTriggeredAbility(final BoldPlagiaristTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.COUNTERS_ADDED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!game.getOpponents(getControllerId()).contains(event.getPlayerId())) { + return false; + } + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent == null + || !permanent.isCreature() + || !permanent.isControlledBy(event.getPlayerId())) { + return false; + } + getEffects().setValue("counterType", event.getData()); + getEffects().setValue("counterAmount", event.getAmount()); + getEffects().setValue("playerId", event.getPlayerId()); + return true; + } + + @Override + public BoldPlagiaristTriggeredAbility copy() { + return new BoldPlagiaristTriggeredAbility(this); + } + + @Override + public String getRule() { + return "Whenever an opponent puts one or more counters on a creature they control, " + + "they put the same number and kind of counters on {this}."; + } +} + +class BoldPlagiaristEffect extends OneShotEffect { + + BoldPlagiaristEffect() { + super(Outcome.Benefit); + } + + private BoldPlagiaristEffect(final BoldPlagiaristEffect effect) { + super(effect); + } + + @Override + public BoldPlagiaristEffect copy() { + return new BoldPlagiaristEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + CounterType counterType = CounterType.findByName((String) getValue("counterType")); + Integer amount = (Integer) getValue("counterAmount"); + UUID playerId = (UUID) getValue("playerId"); + return permanent != null + && counterType != null + && amount != null + && playerId != null + && permanent.addCounters(counterType.createInstance(amount), playerId, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BoldwyrHeavyweights.java b/Mage.Sets/src/mage/cards/b/BoldwyrHeavyweights.java index 2b1dc76a3498..403f69d1fefd 100644 --- a/Mage.Sets/src/mage/cards/b/BoldwyrHeavyweights.java +++ b/Mage.Sets/src/mage/cards/b/BoldwyrHeavyweights.java @@ -53,7 +53,7 @@ class BoldwyrHeavyweightsEffect extends OneShotEffect { BoldwyrHeavyweightsEffect() { super(Outcome.Detriment); - this.staticText = "each opponent may search their library for a creature card and put it onto the battlefield. Then each player who searched their library this way shuffles it"; + this.staticText = "each opponent may search their library for a creature card and put it onto the battlefield. Then each player who searched their library this way shuffles"; } BoldwyrHeavyweightsEffect(final BoldwyrHeavyweightsEffect effect) { diff --git a/Mage.Sets/src/mage/cards/b/BoltBend.java b/Mage.Sets/src/mage/cards/b/BoltBend.java index 1f27c67d0a0b..21ff34ae7be7 100644 --- a/Mage.Sets/src/mage/cards/b/BoltBend.java +++ b/Mage.Sets/src/mage/cards/b/BoltBend.java @@ -11,7 +11,7 @@ import mage.constants.CardType; import mage.constants.Zone; import mage.filter.FilterStackObject; -import mage.filter.predicate.mageobject.NumberOfTargetsPredicate; +import mage.filter.predicate.other.NumberOfTargetsPredicate; import mage.target.TargetStackObject; /** diff --git a/Mage.Sets/src/mage/cards/b/BoltwingMarauder.java b/Mage.Sets/src/mage/cards/b/BoltwingMarauder.java index 52f9b4237455..0ae84fbe29e3 100644 --- a/Mage.Sets/src/mage/cards/b/BoltwingMarauder.java +++ b/Mage.Sets/src/mage/cards/b/BoltwingMarauder.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/b/BombSquad.java b/Mage.Sets/src/mage/cards/b/BombSquad.java index 3470751fa981..92c5886f90d4 100644 --- a/Mage.Sets/src/mage/cards/b/BombSquad.java +++ b/Mage.Sets/src/mage/cards/b/BombSquad.java @@ -89,7 +89,7 @@ public boolean checkEventType(GameEvent event, Game game) { public boolean checkTrigger(GameEvent event, Game game) { if (event.getData().equals(CounterType.FUSE.getName())) { Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && filter.match(permanent, game)) { + if (filter.match(permanent, game)) { if (4 <= permanent.getCounters(game).getCount(CounterType.FUSE)) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(permanent, game)); diff --git a/Mage.Sets/src/mage/cards/b/BondOfInsight.java b/Mage.Sets/src/mage/cards/b/BondOfInsight.java index fbec825de1b5..960fe72b091e 100644 --- a/Mage.Sets/src/mage/cards/b/BondOfInsight.java +++ b/Mage.Sets/src/mage/cards/b/BondOfInsight.java @@ -27,7 +27,7 @@ public BondOfInsight(UUID ownerId, CardSetInfo setInfo) { // Each player puts the top four cards of their library into their graveyard. Return up to two instant and/or sorcery cards from your graveyard to your hand. Exile Bond of Insight. this.getSpellAbility().addEffect(new BondOfInsightEffect()); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private BondOfInsight(final BondOfInsight card) { diff --git a/Mage.Sets/src/mage/cards/b/BondOfPassion.java b/Mage.Sets/src/mage/cards/b/BondOfPassion.java index 174af8fe9dda..bf7c770cd318 100644 --- a/Mage.Sets/src/mage/cards/b/BondOfPassion.java +++ b/Mage.Sets/src/mage/cards/b/BondOfPassion.java @@ -14,7 +14,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterCreaturePlayerOrPlaneswalker; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/b/BonePicker.java b/Mage.Sets/src/mage/cards/b/BonePicker.java index f5b2418dfec6..86a6574fa2d8 100644 --- a/Mage.Sets/src/mage/cards/b/BonePicker.java +++ b/Mage.Sets/src/mage/cards/b/BonePicker.java @@ -59,7 +59,7 @@ class BonePickerAdjustingCostsEffect extends CostModificationEffectImpl { BonePickerAdjustingCostsEffect() { super(Duration.EndOfGame, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "{this} costs {3} less to cast if a creature died this turn"; + staticText = "this spell costs {3} less to cast if a creature died this turn"; } BonePickerAdjustingCostsEffect(BonePickerAdjustingCostsEffect effect) { diff --git a/Mage.Sets/src/mage/cards/b/BoneyardMycodrax.java b/Mage.Sets/src/mage/cards/b/BoneyardMycodrax.java index 72e7776e2ed4..1d9ccd0a6ece 100644 --- a/Mage.Sets/src/mage/cards/b/BoneyardMycodrax.java +++ b/Mage.Sets/src/mage/cards/b/BoneyardMycodrax.java @@ -15,7 +15,7 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/b/BonusRound.java b/Mage.Sets/src/mage/cards/b/BonusRound.java index c84a417a0eb7..6d330137045b 100644 --- a/Mage.Sets/src/mage/cards/b/BonusRound.java +++ b/Mage.Sets/src/mage/cards/b/BonusRound.java @@ -60,7 +60,7 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && (spell.isInstant() || spell.isSorcery())) { + if (spell != null && spell.isInstantOrSorcery()) { this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId())); return true; } diff --git a/Mage.Sets/src/mage/cards/b/Bookwurm.java b/Mage.Sets/src/mage/cards/b/Bookwurm.java new file mode 100644 index 000000000000..ab4057dd5b69 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/Bookwurm.java @@ -0,0 +1,85 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Bookwurm extends CardImpl { + + public Bookwurm(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{7}{G}"); + + this.subtype.add(SubType.WURM); + this.power = new MageInt(7); + this.toughness = new MageInt(7); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // When Bookwurm enters the battlefield, you gain 3 life and draw a card. + Ability ability = new EntersBattlefieldTriggeredAbility(new GainLifeEffect(3)); + ability.addEffect(new DrawCardSourceControllerEffect(1).concatBy("and")); + this.addAbility(ability); + + // {2}{G}: Put Bookwurm from your graveyard into your library third from the top. + this.addAbility(new SimpleActivatedAbility(Zone.GRAVEYARD, new BookwurmEffect(), new ManaCostsImpl("{2}{G}"))); + } + + private Bookwurm(final Bookwurm card) { + super(card); + } + + @Override + public Bookwurm copy() { + return new Bookwurm(this); + } +} + +class BookwurmEffect extends OneShotEffect { + + BookwurmEffect() { + super(Outcome.Benefit); + staticText = "put {this} from your graveyard into your library third from the top"; + } + + private BookwurmEffect(final BookwurmEffect effect) { + super(effect); + } + + @Override + public BookwurmEffect copy() { + return new BookwurmEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObjectIfItStillExists(game); + return player != null + && sourceObject instanceof Card + && player.putCardOnTopXOfLibrary( + (Card) sourceObject, game, source, 3, true + ); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BoonweaverGiant.java b/Mage.Sets/src/mage/cards/b/BoonweaverGiant.java index c7bd5ffb2ab6..24766e3ecad7 100644 --- a/Mage.Sets/src/mage/cards/b/BoonweaverGiant.java +++ b/Mage.Sets/src/mage/cards/b/BoonweaverGiant.java @@ -54,7 +54,7 @@ class BoonweaverGiantEffect extends OneShotEffect { public BoonweaverGiantEffect() { super(Outcome.UnboostCreature); - this.staticText = "you may search your graveyard, hand, and/or library for an Aura card and put it onto the battlefield attached to {this}. If you search your library this way, shuffle it."; + this.staticText = "you may search your graveyard, hand, and/or library for an Aura card and put it onto the battlefield attached to {this}. If you search your library this way, shuffle."; } public BoonweaverGiantEffect(final BoonweaverGiantEffect effect) { diff --git a/Mage.Sets/src/mage/cards/b/BorderlandBehemoth.java b/Mage.Sets/src/mage/cards/b/BorderlandBehemoth.java index 9e0bc71421ac..2afdf251ddb4 100644 --- a/Mage.Sets/src/mage/cards/b/BorderlandBehemoth.java +++ b/Mage.Sets/src/mage/cards/b/BorderlandBehemoth.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/b/BorderlandExplorer.java b/Mage.Sets/src/mage/cards/b/BorderlandExplorer.java index c404e5caca8f..badb6017815b 100644 --- a/Mage.Sets/src/mage/cards/b/BorderlandExplorer.java +++ b/Mage.Sets/src/mage/cards/b/BorderlandExplorer.java @@ -54,7 +54,7 @@ class BorderlandExplorerEffect extends OneShotEffect { public BorderlandExplorerEffect() { super(Outcome.Neutral); this.staticText = "each player may discard a card. Each player who discarded a card this way may search their library " - + "for a basic land card, reveal it, put it into their hand, then shuffle their library"; + + "for a basic land card, reveal it, put it into their hand, then shuffle"; } public BorderlandExplorerEffect(final BorderlandExplorerEffect effect) { diff --git a/Mage.Sets/src/mage/cards/b/BoreasCharger.java b/Mage.Sets/src/mage/cards/b/BoreasCharger.java index 4094ca574805..fcc80133551b 100644 --- a/Mage.Sets/src/mage/cards/b/BoreasCharger.java +++ b/Mage.Sets/src/mage/cards/b/BoreasCharger.java @@ -1,16 +1,11 @@ package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; +import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; @@ -26,8 +21,9 @@ import mage.target.TargetPlayer; import mage.target.common.TargetCardInLibrary; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class BoreasCharger extends CardImpl { @@ -74,11 +70,10 @@ class BoreasChargerEffect extends OneShotEffect { public BoreasChargerEffect() { super(Outcome.Benefit); - this.staticText = "choose an opponent who controls more lands than you. " - + "Search your library for a number of Plains cards " - + "equal to the difference and reveal them. " - + "Put one of them onto the battlefield tapped " - + "and the rest into your hand. Then shuffle your library"; + this.staticText = "choose an opponent who controls more lands than you. " + + "Search your library for a number of Plains cards equal to the difference, " + + "reveal those cards, put one of them onto the battlefield tapped " + + "and the rest into your hand, then shuffle"; } public BoreasChargerEffect(final BoreasChargerEffect effect) { diff --git a/Mage.Sets/src/mage/cards/b/BoseijuWhoSheltersAll.java b/Mage.Sets/src/mage/cards/b/BoseijuWhoSheltersAll.java index 5aab7b6cdae9..a551870815fa 100644 --- a/Mage.Sets/src/mage/cards/b/BoseijuWhoSheltersAll.java +++ b/Mage.Sets/src/mage/cards/b/BoseijuWhoSheltersAll.java @@ -71,7 +71,7 @@ public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.MANA_PAID) { if (event.getData() != null && event.getData().equals(originalId.toString()) && event.getTargetId() != null) { Card spell = game.getSpell(event.getTargetId()); - if (spell != null && (spell.isInstant() || spell.isSorcery())) { + if (spell != null && spell.isInstantOrSorcery()) { spells.add(new MageObjectReference(game.getObject(event.getTargetId()), game)); } } diff --git a/Mage.Sets/src/mage/cards/b/BoshIronGolem.java b/Mage.Sets/src/mage/cards/b/BoshIronGolem.java index 9ede0fe26e60..efaa22f78368 100644 --- a/Mage.Sets/src/mage/cards/b/BoshIronGolem.java +++ b/Mage.Sets/src/mage/cards/b/BoshIronGolem.java @@ -40,7 +40,7 @@ public BoshIronGolem(UUID ownerId, CardSetInfo setInfo) { // {3}{R}, Sacrifice an artifact: Bosh, Iron Golem deals damage equal to the sacrificed artifact's converted mana cost to any target. Effect effect = new DamageTargetEffect(new SacrificeCostConvertedMana("artifact")); - effect.setText("{this} deals damage equal to the sacrificed artifact's converted mana cost to any target"); + effect.setText("{this} deals damage equal to the sacrificed artifact's mana value to any target"); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{3}{R}")); ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(new FilterControlledArtifactPermanent("an artifact")))); ability.addTarget(new TargetAnyTarget()); diff --git a/Mage.Sets/src/mage/cards/b/BottledCloister.java b/Mage.Sets/src/mage/cards/b/BottledCloister.java index 958274a1be1a..c031230a58d1 100644 --- a/Mage.Sets/src/mage/cards/b/BottledCloister.java +++ b/Mage.Sets/src/mage/cards/b/BottledCloister.java @@ -1,42 +1,41 @@ - package mage.cards.b; -import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.TargetController; import mage.constants.Zone; import mage.game.ExileZone; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.players.Player; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class BottledCloister extends CardImpl { public BottledCloister(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); // At the beginning of each opponent's upkeep, exile all cards from your hand face down. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new BottledCloisterExileEffect(), TargetController.OPPONENT, false)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new BottledCloisterExileEffect(), TargetController.OPPONENT, false + )); + // At the beginning of your upkeep, return all cards you own exiled with Bottled Cloister to your hand, then draw a card. - Ability ability = new BeginningOfUpkeepTriggeredAbility(new BottledCloisterReturnEffect(), TargetController.YOU, false); - Effect effect = new DrawCardSourceControllerEffect(1); - effect.setText(", then draw a card"); - ability.addEffect(effect); - this.addAbility(ability); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new BottledCloisterReturnEffect(), TargetController.YOU, false + )); } private BottledCloister(final BottledCloister card) { @@ -51,12 +50,12 @@ public BottledCloister copy() { class BottledCloisterExileEffect extends OneShotEffect { - public BottledCloisterExileEffect() { + BottledCloisterExileEffect() { super(Outcome.Detriment); this.staticText = "exile all cards from your hand face down"; } - public BottledCloisterExileEffect(final BottledCloisterExileEffect effect) { + private BottledCloisterExileEffect(final BottledCloisterExileEffect effect) { super(effect); } @@ -68,31 +67,31 @@ public BottledCloisterExileEffect copy() { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (controller != null && sourcePermanent != null) { - int numberOfCards = controller.getHand().size(); - if (numberOfCards > 0) { - UUID exileId = CardUtil.getCardExileZoneId(game, source); - for (Card card: controller.getHand().getCards(game)) { - card.moveToExile(exileId, sourcePermanent.getName(), source, game); - card.setFaceDown(true, game); - } - game.informPlayers(sourcePermanent.getName() + ": " + controller.getLogName() + " exiles their hand face down (" + numberOfCards + "card" + (numberOfCards > 1 ?"s":"") + ')'); - } - return true; + MageObject sourceObject = source.getSourceObject(game); + if (controller == null || sourceObject == null) { + return false; + } + Cards cards = new CardsImpl(controller.getHand()); + if (cards.isEmpty()) { + return false; } - return false; + controller.moveCardsToExile(cards.getCards(game), source, game, false, CardUtil.getExileZoneId(game, source), sourceObject.getIdName()); + cards.getCards(game) + .stream() + .filter(c -> game.getState().getZone(c.getId()) == Zone.EXILED) + .forEach(card -> card.setFaceDown(true, game)); + return true; } } class BottledCloisterReturnEffect extends OneShotEffect { - public BottledCloisterReturnEffect() { + BottledCloisterReturnEffect() { super(Outcome.Benefit); - this.staticText = "return all cards you own exiled with {this} to your hand"; + this.staticText = "return all cards you own exiled with {this} to your hand, then draw a card"; } - public BottledCloisterReturnEffect(final BottledCloisterReturnEffect effect) { + private BottledCloisterReturnEffect(final BottledCloisterReturnEffect effect) { super(effect); } @@ -103,26 +102,14 @@ public BottledCloisterReturnEffect copy() { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (controller != null && sourcePermanent != null) { - UUID exileId = CardUtil.getCardExileZoneId(game, source); - int numberOfCards = 0; - ExileZone exileZone = game.getExile().getExileZone(exileId); - if (exileZone != null) { - for (Card card: exileZone.getCards(game)) { - if (card.isOwnedBy(controller.getId())) { - numberOfCards++; - card.moveToZone(Zone.HAND, source, game, true); - card.setFaceDown(false, game); - } - } - } - if (numberOfCards > 0) { - game.informPlayers(sourcePermanent.getLogName() + ": " + controller.getLogName() + " returns "+ numberOfCards + " card" + (numberOfCards > 1 ?"s":"") + " from exile to hand"); - } - return true; + Player player = game.getPlayer(source.getControllerId()); + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + Cards cards = new CardsImpl(exileZone); + cards.removeIf(uuid -> !player.getId().equals(game.getOwnerId(uuid))); + if (!cards.isEmpty()) { + player.moveCards(cards, Zone.HAND, source, game); } - return false; + player.drawCards(1, source, game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/b/Boulderfall.java b/Mage.Sets/src/mage/cards/b/Boulderfall.java index b497229dd8f8..b69157265bf7 100644 --- a/Mage.Sets/src/mage/cards/b/Boulderfall.java +++ b/Mage.Sets/src/mage/cards/b/Boulderfall.java @@ -17,7 +17,6 @@ public final class Boulderfall extends CardImpl { public Boulderfall(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{6}{R}{R}"); - // Boulderfall deals 5 damage divided as you choose among any number of target creatures and/or players. this.getSpellAbility().addEffect(new DamageMultiEffect(5)); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(5)); diff --git a/Mage.Sets/src/mage/cards/b/BoundByMoonsilver.java b/Mage.Sets/src/mage/cards/b/BoundByMoonsilver.java index f8b2bf8b4258..12ac972ffd86 100644 --- a/Mage.Sets/src/mage/cards/b/BoundByMoonsilver.java +++ b/Mage.Sets/src/mage/cards/b/BoundByMoonsilver.java @@ -13,7 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetCreaturePermanent; @@ -45,7 +45,7 @@ public BoundByMoonsilver(UUID ownerId, CardSetInfo setInfo) { // Enchanted permanent can't attack, block, or transform. Effect effect = new CantAttackBlockTransformAttachedEffect(); - effect.setText("Enchanted permanent can't attack, block, or transform."); + effect.setText("Enchanted creature can't attack, block, or transform."); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); // Sacrifice another permanent: Attach Bound by Moonsilver to target creature. Activate this ability only any time you could cast a sorcery and only once each turn. diff --git a/Mage.Sets/src/mage/cards/b/BoundlessRealms.java b/Mage.Sets/src/mage/cards/b/BoundlessRealms.java index 3455ec9a5549..326224da0748 100644 --- a/Mage.Sets/src/mage/cards/b/BoundlessRealms.java +++ b/Mage.Sets/src/mage/cards/b/BoundlessRealms.java @@ -46,7 +46,8 @@ class BoundlessRealmsEffect extends OneShotEffect { public BoundlessRealmsEffect() { super(Outcome.Benefit); - this.staticText = "Search your library for up to X basic land cards, where X is the number of lands you control, and put them onto the battlefield tapped. Then shuffle your library."; + this.staticText = "search your library for up to X basic land cards, where X is the number of " + + "lands you control, put them onto the battlefield tapped, then shuffle"; } public BoundlessRealmsEffect(final BoundlessRealmsEffect effect) { diff --git a/Mage.Sets/src/mage/cards/b/BounteousKirin.java b/Mage.Sets/src/mage/cards/b/BounteousKirin.java index 1ee551b8a7ca..52be3231c383 100644 --- a/Mage.Sets/src/mage/cards/b/BounteousKirin.java +++ b/Mage.Sets/src/mage/cards/b/BounteousKirin.java @@ -49,7 +49,7 @@ class BounteousKirinEffect extends OneShotEffect { public BounteousKirinEffect() { super(Outcome.GainLife); - this.staticText = "you may gain life equal to that spell's converted mana cost"; + this.staticText = "you may gain life equal to that spell's mana value"; } public BounteousKirinEffect(final BounteousKirinEffect effect) { @@ -67,7 +67,7 @@ public boolean apply(Game game, Ability source) { if (spell != null) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - int life = spell.getConvertedManaCost(); + int life = spell.getManaValue(); controller.gainLife(life, game, source); return true; } diff --git a/Mage.Sets/src/mage/cards/b/BrackishTrudge.java b/Mage.Sets/src/mage/cards/b/BrackishTrudge.java new file mode 100644 index 000000000000..2e9224a66374 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BrackishTrudge.java @@ -0,0 +1,56 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.YouGainedLifeCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToHandEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.watchers.common.PlayerGainedLifeWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BrackishTrudge extends CardImpl { + + private static final Condition condition = new YouGainedLifeCondition(ComparisonType.MORE_THAN, 0); + private static final Hint hint = new ConditionHint(condition, "You gained life this turn"); + + public BrackishTrudge(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.FUNGUS); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(4); + this.toughness = new MageInt(2); + + // Brackish Trudge enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {1}{B}: Return Brackish Trudge from your graveyard to your hand. Activate only if you gained life this turn. + this.addAbility(new ActivateIfConditionActivatedAbility( + Zone.GRAVEYARD, new ReturnSourceFromGraveyardToHandEffect(), + new ManaCostsImpl<>("{1}{B}"), condition + ).addHint(hint), new PlayerGainedLifeWatcher()); + } + + private BrackishTrudge(final BrackishTrudge card) { + super(card); + } + + @Override + public BrackishTrudge copy() { + return new BrackishTrudge(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BragoKingEternal.java b/Mage.Sets/src/mage/cards/b/BragoKingEternal.java index ae261051251e..3f1c0f9acfe6 100644 --- a/Mage.Sets/src/mage/cards/b/BragoKingEternal.java +++ b/Mage.Sets/src/mage/cards/b/BragoKingEternal.java @@ -3,8 +3,7 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.ExileTargetForSourceEffect; import mage.abilities.effects.common.ReturnFromExileEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -13,9 +12,10 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.constants.Zone; +import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; -import mage.target.common.TargetControlledPermanent; +import mage.target.TargetPermanent; import java.util.UUID; @@ -24,6 +24,12 @@ */ public final class BragoKingEternal extends CardImpl { + private static final FilterPermanent filter = new FilterControlledPermanent("nonland permanents you control"); + + static { + filter.add(Predicates.not(CardType.LAND.getPredicate())); + } + public BragoKingEternal(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{U}"); addSuperType(SuperType.LEGENDARY); @@ -34,14 +40,11 @@ public BragoKingEternal(UUID ownerId, CardSetInfo setInfo) { // Flying this.addAbility(FlyingAbility.getInstance()); + // When Brago, King Eternal deals combat damage to a player, exile any number of target nonland permanents you control, then return those cards to the battlefield under their owner's control. - Effect effect = new ExileTargetEffect(this.getId(), this.getName(), Zone.BATTLEFIELD); - effect.setText("exile any number of target nonland permanents you control"); - Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(effect, false); - FilterControlledPermanent filterControlledNonlandPermanent = new FilterControlledPermanent(); - filterControlledNonlandPermanent.add(Predicates.not(CardType.LAND.getPredicate())); - ability.addTarget(new TargetControlledPermanent(0, Integer.MAX_VALUE, filterControlledNonlandPermanent, false)); - ability.addEffect(new ReturnFromExileEffect(this.getId(), Zone.BATTLEFIELD, ", then return those cards to the battlefield under their owner's control")); + Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new ExileTargetForSourceEffect().setText("exile any number of target nonland permanents you control"), false); + ability.addTarget(new TargetPermanent(0, Integer.MAX_VALUE, filter, false)); + ability.addEffect(new ReturnFromExileEffect(Zone.BATTLEFIELD, ", then return those cards to the battlefield under their owner's control")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/BragosRepresentative.java b/Mage.Sets/src/mage/cards/b/BragosRepresentative.java new file mode 100644 index 000000000000..dd55580bfc8f --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BragosRepresentative.java @@ -0,0 +1,77 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.VoteEvent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BragosRepresentative extends CardImpl { + + public BragosRepresentative(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ADVISOR); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // While voting, you get an additional vote. + this.addAbility(new SimpleStaticAbility(new BragosRepresentativeReplacementEffect())); + } + + private BragosRepresentative(final BragosRepresentative card) { + super(card); + } + + @Override + public BragosRepresentative copy() { + return new BragosRepresentative(this); + } +} + +class BragosRepresentativeReplacementEffect extends ReplacementEffectImpl { + + BragosRepresentativeReplacementEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "while voting, you get an additional vote"; + } + + private BragosRepresentativeReplacementEffect(final BragosRepresentativeReplacementEffect effect) { + super(effect); + } + + @Override + public BragosRepresentativeReplacementEffect copy() { + return new BragosRepresentativeReplacementEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.VOTE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.isControlledBy(event.getTargetId()); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + ((VoteEvent) event).incrementExtraVotes(); + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BraidwoodSextant.java b/Mage.Sets/src/mage/cards/b/BraidwoodSextant.java index 7587ec8672ed..8de89920eb61 100644 --- a/Mage.Sets/src/mage/cards/b/BraidwoodSextant.java +++ b/Mage.Sets/src/mage/cards/b/BraidwoodSextant.java @@ -27,7 +27,7 @@ public BraidwoodSextant(UUID ownerId, CardSetInfo setInfo) { // {2}, {tap}, Sacrifice Braidwood Sextant: Search your library for a basic land card, reveal that card, and put it into your hand. Then shuffle your library. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 1, StaticFilters.FILTER_CARD_BASIC_LAND), true, true), + new SearchLibraryPutInHandEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true, true), new ManaCostsImpl("{2}")); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); diff --git a/Mage.Sets/src/mage/cards/b/BrainInAJar.java b/Mage.Sets/src/mage/cards/b/BrainInAJar.java index 279560ecdbfa..62ed9f8c0de7 100644 --- a/Mage.Sets/src/mage/cards/b/BrainInAJar.java +++ b/Mage.Sets/src/mage/cards/b/BrainInAJar.java @@ -20,7 +20,7 @@ import mage.counters.CounterType; import mage.filter.FilterCard; import mage.filter.common.FilterInstantOrSorceryCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -71,7 +71,7 @@ class BrainInAJarCastEffect extends OneShotEffect { public BrainInAJarCastEffect() { super(Outcome.PlayForFree); this.staticText = ", then you may cast an instant or sorcery card " - + "with converted mana costs equal to the number of charge " + + "with mana values equal to the number of charge " + "counters on {this} from your hand without paying its mana cost"; } @@ -92,12 +92,12 @@ public boolean apply(Game game, Ability source) { && sourceObject != null) { int counters = sourceObject.getCounters(game).getCount(CounterType.CHARGE); FilterCard filter = new FilterInstantOrSorceryCard(); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, counters)); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, counters)); int cardsToCast = controller.getHand().count(filter, source.getControllerId(), source.getSourceId(), game); if (cardsToCast > 0 && controller.chooseUse(Outcome.PlayForFree, - "Cast an instant or sorcery card with converted mana costs of " + "Cast an instant or sorcery card with mana values of " + counters + " from your hand without paying its mana cost?", source, game)) { TargetCardInHand target = new TargetCardInHand(filter); diff --git a/Mage.Sets/src/mage/cards/b/BrambleSovereign.java b/Mage.Sets/src/mage/cards/b/BrambleSovereign.java index 686031a150a4..a76a4a5183ab 100644 --- a/Mage.Sets/src/mage/cards/b/BrambleSovereign.java +++ b/Mage.Sets/src/mage/cards/b/BrambleSovereign.java @@ -17,7 +17,7 @@ import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/b/BrandedHowler.java b/Mage.Sets/src/mage/cards/b/BrandedHowler.java index 10efaf8fa43d..3b5fb571a4a6 100644 --- a/Mage.Sets/src/mage/cards/b/BrandedHowler.java +++ b/Mage.Sets/src/mage/cards/b/BrandedHowler.java @@ -1,10 +1,10 @@ - package mage.cards.b; import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.TransformSourceEffect; @@ -33,8 +33,7 @@ public BrandedHowler(UUID ownerId, CardSetInfo setInfo) { this.transformable = true; // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Branded Howler. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private BrandedHowler(final BrandedHowler card) { diff --git a/Mage.Sets/src/mage/cards/b/BrashTaunter.java b/Mage.Sets/src/mage/cards/b/BrashTaunter.java index f685d13d59e9..874130ea7a09 100644 --- a/Mage.Sets/src/mage/cards/b/BrashTaunter.java +++ b/Mage.Sets/src/mage/cards/b/BrashTaunter.java @@ -17,7 +17,7 @@ import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetPermanent; diff --git a/Mage.Sets/src/mage/cards/b/BrasssBounty.java b/Mage.Sets/src/mage/cards/b/BrasssBounty.java index de9e67890dff..0f1bb935916e 100644 --- a/Mage.Sets/src/mage/cards/b/BrasssBounty.java +++ b/Mage.Sets/src/mage/cards/b/BrasssBounty.java @@ -22,7 +22,7 @@ public BrasssBounty(UUID ownerId, CardSetInfo setInfo) { // For each land you control, create a colorless Treasure artifact token with "{T}, Sacrifice this artifact: Add one mana of any color." this.getSpellAbility().addEffect( new CreateTokenEffect(new TreasureToken(), new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND)) - .setText("For each land you control, create a colorless Treasure artifact token with \"{T}, Sacrifice this artifact: Add one mana of any color.\"")); + .setText("For each land you control, create a Treasure token")); } private BrasssBounty(final BrasssBounty card) { diff --git a/Mage.Sets/src/mage/cards/b/BreachingHippocamp.java b/Mage.Sets/src/mage/cards/b/BreachingHippocamp.java index 88c9a0e2c740..4f7b44ad426c 100644 --- a/Mage.Sets/src/mage/cards/b/BreachingHippocamp.java +++ b/Mage.Sets/src/mage/cards/b/BreachingHippocamp.java @@ -12,7 +12,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/b/BreakOpen.java b/Mage.Sets/src/mage/cards/b/BreakOpen.java index 8a839958c95f..ae00e8d77620 100644 --- a/Mage.Sets/src/mage/cards/b/BreakOpen.java +++ b/Mage.Sets/src/mage/cards/b/BreakOpen.java @@ -8,7 +8,7 @@ import mage.constants.CardType; import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.other.FaceDownPredicate; +import mage.filter.predicate.card.FaceDownPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/b/BreakneckRider.java b/Mage.Sets/src/mage/cards/b/BreakneckRider.java index 0aae98ce8867..f0936aedf8c0 100644 --- a/Mage.Sets/src/mage/cards/b/BreakneckRider.java +++ b/Mage.Sets/src/mage/cards/b/BreakneckRider.java @@ -1,20 +1,14 @@ - package mage.cards.b; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** * @author fireshoes @@ -32,8 +26,7 @@ public BreakneckRider(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each upkeep, if no spells were cast last turn, transform Breakneck Rider. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private BreakneckRider(final BreakneckRider card) { diff --git a/Mage.Sets/src/mage/cards/b/BreathOfDarigaaz.java b/Mage.Sets/src/mage/cards/b/BreathOfDarigaaz.java index 9e0a5e71dc96..acf7ac7a27e8 100644 --- a/Mage.Sets/src/mage/cards/b/BreathOfDarigaaz.java +++ b/Mage.Sets/src/mage/cards/b/BreathOfDarigaaz.java @@ -35,7 +35,7 @@ public BreathOfDarigaaz(UUID ownerId, CardSetInfo setInfo) { // Breath of Darigaaz deals 1 damage to each creature without flying and each player. If Breath of Darigaaz was kicked, it deals 4 damage to each creature without flying and each player instead. this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new DamageEverythingEffect(4, filter), new DamageEverythingEffect(1, filter), KickedCondition.instance, - "{this} deals 1 damage to each creature without flying and each player. if this spell was kicked, it deals 4 damage to each creature without flying and each player instead.")); + "{this} deals 1 damage to each creature without flying and each player. If this spell was kicked, it deals 4 damage to each creature without flying and each player instead.")); } private BreathOfDarigaaz(final BreathOfDarigaaz card) { diff --git a/Mage.Sets/src/mage/cards/b/BreechesBrazenPlunderer.java b/Mage.Sets/src/mage/cards/b/BreechesBrazenPlunderer.java index 42a7e558f661..9738b9f5eb47 100644 --- a/Mage.Sets/src/mage/cards/b/BreechesBrazenPlunderer.java +++ b/Mage.Sets/src/mage/cards/b/BreechesBrazenPlunderer.java @@ -144,7 +144,7 @@ public boolean apply(Game game, Ability source) { return false; } for (Card card : cards.getCards(game)) { - CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.EndOfTurn); + CardUtil.makeCardPlayable(game, source, card, Duration.EndOfTurn, true); } return true; } diff --git a/Mage.Sets/src/mage/cards/b/BreenaTheDemagogue.java b/Mage.Sets/src/mage/cards/b/BreenaTheDemagogue.java new file mode 100644 index 000000000000..fe2dcbeadbe4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BreenaTheDemagogue.java @@ -0,0 +1,141 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BreenaTheDemagogue extends CardImpl { + + public BreenaTheDemagogue(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.BIRD); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever a player attacks one of your opponents, if that opponent has more life than another of your opponents, that attacking player draws a card and you put two +1/+1 counters on a creature you control. + this.addAbility(new BreenaTheDemagogueTriggeredAbility()); + } + + private BreenaTheDemagogue(final BreenaTheDemagogue card) { + super(card); + } + + @Override + public BreenaTheDemagogue copy() { + return new BreenaTheDemagogue(this); + } +} + +class BreenaTheDemagogueTriggeredAbility extends TriggeredAbilityImpl { + + BreenaTheDemagogueTriggeredAbility() { + super(Zone.BATTLEFIELD, new DrawCardTargetEffect(1), false); + this.addEffect(new BreenaTheDemagogueEffect()); + } + + private BreenaTheDemagogueTriggeredAbility(final BreenaTheDemagogueTriggeredAbility ability) { + super(ability); + } + + @Override + public BreenaTheDemagogueTriggeredAbility copy() { + return new BreenaTheDemagogueTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DEFENDER_ATTACKED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!game.getOpponents(getControllerId()).contains(event.getTargetId())) { + return false; + } + getEffects().setTargetPointer(new FixedTarget(event.getPlayerId())); + getEffects().setValue("attackedPlayer", event.getTargetId()); + return true; + } + + @Override + public boolean checkInterveningIfClause(Game game) { + Player player = game.getPlayer((UUID) getEffects().get(0).getValue("attackedPlayer")); + return player != null + && player.getLife() > game.getOpponents(getControllerId()) + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .mapToInt(Player::getLife) + .min() + .orElse(0); + } + + @Override + public String getRule() { + return "Whenever a player attacks one of your opponents, " + + "if that opponent has more life than another of your opponents, " + + "that attacking player draws a card and you put two +1/+1 counters on a creature you control."; + } +} + +class BreenaTheDemagogueEffect extends OneShotEffect { + + BreenaTheDemagogueEffect() { + super(Outcome.Benefit); + } + + private BreenaTheDemagogueEffect(final BreenaTheDemagogueEffect effect) { + super(effect); + } + + @Override + public BreenaTheDemagogueEffect copy() { + return new BreenaTheDemagogueEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null || game.getBattlefield().count( + StaticFilters.FILTER_CONTROLLED_CREATURE, + source.getSourceId(), source.getControllerId(), game + ) < 1) { + return false; + } + TargetPermanent target = new TargetControlledCreaturePermanent(); + target.setNotTarget(true); + player.choose(outcome, target, source.getSourceId(), game); + Permanent permanent = game.getPermanent(source.getSourceId()); + return permanent != null && permanent.addCounters( + CounterType.P1P1.createInstance(2), + source.getControllerId(), source, game + ); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BriarberryCohort.java b/Mage.Sets/src/mage/cards/b/BriarberryCohort.java index 0d1788a62585..135b78470e50 100644 --- a/Mage.Sets/src/mage/cards/b/BriarberryCohort.java +++ b/Mage.Sets/src/mage/cards/b/BriarberryCohort.java @@ -18,7 +18,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/b/Bribery.java b/Mage.Sets/src/mage/cards/b/Bribery.java index 681bd5052644..ea546ab457a3 100644 --- a/Mage.Sets/src/mage/cards/b/Bribery.java +++ b/Mage.Sets/src/mage/cards/b/Bribery.java @@ -44,7 +44,7 @@ class BriberyEffect extends OneShotEffect { public BriberyEffect() { super(Outcome.PutCardInPlay); - this.staticText = "Search target opponent's library for a creature card and put that card onto the battlefield under your control. Then that player shuffles their library"; + this.staticText = "Search target opponent's library for a creature card and put that card onto the battlefield under your control. Then that player shuffles"; } public BriberyEffect(final BriberyEffect effect) { diff --git a/Mage.Sets/src/mage/cards/b/BrinelinTheMoonKraken.java b/Mage.Sets/src/mage/cards/b/BrinelinTheMoonKraken.java index bb99efe62946..aaa03a6013c1 100644 --- a/Mage.Sets/src/mage/cards/b/BrinelinTheMoonKraken.java +++ b/Mage.Sets/src/mage/cards/b/BrinelinTheMoonKraken.java @@ -73,7 +73,7 @@ public boolean checkTrigger(GameEvent event, Game game) { return false; } Spell spell = game.getSpellOrLKIStack(event.getTargetId()); - return spell != null && spell.getConvertedManaCost() >= 6; + return spell != null && spell.getManaValue() >= 6; case ENTERS_THE_BATTLEFIELD: return event.getTargetId().equals(getSourceId()); default: @@ -83,7 +83,7 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { - return "When {this} enters the battlefield or whenever you cast a spell with converted mana cost " + + return "When {this} enters the battlefield or whenever you cast a spell with mana value " + "6 or greater, you may return target nonland permanent to its owner's hand."; } diff --git a/Mage.Sets/src/mage/cards/b/BringToLight.java b/Mage.Sets/src/mage/cards/b/BringToLight.java index ec2c232a3153..3d725bd97b3f 100644 --- a/Mage.Sets/src/mage/cards/b/BringToLight.java +++ b/Mage.Sets/src/mage/cards/b/BringToLight.java @@ -14,7 +14,7 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; @@ -47,9 +47,9 @@ class BringToLightEffect extends OneShotEffect { public BringToLightEffect() { super(Outcome.PlayForFree); - this.staticText = "Converge — Search your library for a creature, instant, or sorcery card with converted mana " - + "cost less than or equal to the number of colors of mana spent to cast {this}, exile that card, " - + "then shuffle your library. You may cast that card without paying its mana cost"; + this.staticText = "Converge — Search your library for a creature, instant, or sorcery card with mana " + + "value less than or equal to the number of colors of mana spent to cast {this}, exile that card, " + + "then shuffle. You may cast that card without paying its mana cost"; } public BringToLightEffect(final BringToLightEffect effect) { @@ -66,11 +66,11 @@ public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { int numberColors = ColorsOfManaSpentToCastCount.getInstance().calculate(game, source, this); - FilterCard filter = new FilterCard("a creature, instant, or sorcery card with converted mana " - + "cost less than or equal to " + numberColors); + FilterCard filter = new FilterCard("a creature, instant, or sorcery card with mana value " + + "less than or equal to " + numberColors); filter.add(Predicates.or(CardType.CREATURE.getPredicate(), CardType.INSTANT.getPredicate(), CardType.SORCERY.getPredicate())); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, numberColors + 1)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, numberColors + 1)); TargetCardInLibrary target = new TargetCardInLibrary(filter); controller.searchLibrary(target, source, game); Card card = controller.getLibrary().getCard(target.getFirstTarget(), game); diff --git a/Mage.Sets/src/mage/cards/b/BrionStoutarm.java b/Mage.Sets/src/mage/cards/b/BrionStoutarm.java index 988c057167a8..a0a6865c344d 100644 --- a/Mage.Sets/src/mage/cards/b/BrionStoutarm.java +++ b/Mage.Sets/src/mage/cards/b/BrionStoutarm.java @@ -16,7 +16,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetPlayerOrPlaneswalker; diff --git a/Mage.Sets/src/mage/cards/b/BriselaVoiceOfNightmares.java b/Mage.Sets/src/mage/cards/b/BriselaVoiceOfNightmares.java index 832d0b7fd9b6..981bbaffdd16 100644 --- a/Mage.Sets/src/mage/cards/b/BriselaVoiceOfNightmares.java +++ b/Mage.Sets/src/mage/cards/b/BriselaVoiceOfNightmares.java @@ -15,7 +15,6 @@ import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.stack.Spell; import java.util.UUID; @@ -64,7 +63,7 @@ class BriselaVoiceOfNightmaresCantCastEffect extends ContinuousRuleModifyingEffe public BriselaVoiceOfNightmaresCantCastEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "Your opponents can't cast spells with converted mana cost 3 or less"; + staticText = "Your opponents can't cast spells with mana value 3 or less"; } public BriselaVoiceOfNightmaresCantCastEffect(final BriselaVoiceOfNightmaresCantCastEffect effect) { @@ -85,7 +84,7 @@ public boolean apply(Game game, Ability source) { public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source.getSourceId()); if (mageObject != null) { - return "You can't cast spells with converted mana cost 3 or less (" + mageObject.getIdName() + ")."; + return "You can't cast spells with mana value 3 or less (" + mageObject.getIdName() + ")."; } return null; } @@ -100,7 +99,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { Spell spell = game.getStack().getSpell(event.getTargetId()); if (spell != null) { - return spell.getConvertedManaCost() < 4; + return spell.getManaValue() < 4; } } return false; diff --git a/Mage.Sets/src/mage/cards/b/BrokenAmbitions.java b/Mage.Sets/src/mage/cards/b/BrokenAmbitions.java index fb813d16514b..d1899824a35f 100644 --- a/Mage.Sets/src/mage/cards/b/BrokenAmbitions.java +++ b/Mage.Sets/src/mage/cards/b/BrokenAmbitions.java @@ -1,12 +1,10 @@ package mage.cards.b; import mage.abilities.Ability; -import mage.abilities.costs.Cost; -import mage.abilities.costs.mana.ManaCost; -import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.ManacostVariableValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ClashEffect; +import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -15,7 +13,6 @@ import mage.game.stack.Spell; import mage.players.Player; import mage.target.TargetSpell; -import mage.util.ManaUtil; import java.util.UUID; @@ -28,7 +25,8 @@ public BrokenAmbitions(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{U}"); // Counter target spell unless its controller pays {X}. Clash with an opponent. If you win, that spell's controller puts the top four cards of their library into their graveyard. - this.getSpellAbility().addEffect(new BrokenAmbitionsEffect(ManacostVariableValue.instance)); + this.getSpellAbility().addEffect(new CounterUnlessPaysEffect(ManacostVariableValue.instance)); + this.getSpellAbility().addEffect(new BrokenAmbitionsEffect()); this.getSpellAbility().addTarget(new TargetSpell()); } @@ -44,32 +42,13 @@ public BrokenAmbitions copy() { class BrokenAmbitionsEffect extends OneShotEffect { - private static final String effectText = "Counter target spell unless its controller pays {X}. Clash with an opponent. If you win, that spell's controller mills four cards"; - - protected Cost cost; - protected DynamicValue genericMana; - - public BrokenAmbitionsEffect(Cost cost) { + BrokenAmbitionsEffect() { super(Outcome.Benefit); - this.cost = cost; - this.staticText = effectText; - } - - public BrokenAmbitionsEffect(DynamicValue genericMana) { - super(Outcome.Detriment); - this.genericMana = genericMana; - this.staticText = effectText; + this.staticText = "Clash with an opponent. If you win, that spell's controller mills four cards"; } - public BrokenAmbitionsEffect(final BrokenAmbitionsEffect effect) { + private BrokenAmbitionsEffect(final BrokenAmbitionsEffect effect) { super(effect); - if (effect.cost != null) { - this.cost = effect.cost.copy(); - } - if (effect.genericMana != null) { - this.genericMana = effect.genericMana.copy(); - } - this.staticText = effectText; } @Override @@ -79,37 +58,17 @@ public BrokenAmbitionsEffect copy() { @Override public boolean apply(Game game, Ability source) { - Spell spell = (Spell) game.getStack().getStackObject(getTargetPointer().getFirst(game, source)); + Spell spell = game.getSpellOrLKIStack(source.getFirstTarget()); + if (spell == null) { + return false; + } Player player = game.getPlayer(spell.getControllerId()); - if (player != null) { - Cost costToPay; - String costValueMessage; - if (cost != null) { - costToPay = cost.copy(); - costValueMessage = costToPay.getText(); - } else { - costToPay = ManaUtil.createManaCost(genericMana, game, source, this); - costValueMessage = "{" + genericMana.calculate(game, source, this) + "}"; - } - String message; - if (costToPay instanceof ManaCost) { - message = "Would you like to pay " + costValueMessage + " to prevent counter effect?"; - } else { - message = costValueMessage + " to prevent counter effect?"; - } - - costToPay.clearPaid(); - if (!(player.chooseUse(Outcome.Benefit, message, source, game) && costToPay.pay(source, game, source, spell.getControllerId(), false, null))) { - game.informPlayers(player.getLogName() + " chooses not to pay " + costValueMessage + " to prevent the counter effect"); - game.getStack().counter(spell.getId(), source, game); - } - game.informPlayers(player.getLogName() + " chooses to pay " + costValueMessage + " to prevent the counter effect"); - - if (ClashEffect.getInstance().apply(game, source)) { - player.millCards(4, source, game); - } - return true; + if (player == null) { + return false; } - return false; + if (ClashEffect.getInstance().apply(game, source)) { + player.millCards(4, source, game); + } + return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/b/BronzeGuardian.java b/Mage.Sets/src/mage/cards/b/BronzeGuardian.java new file mode 100644 index 000000000000..270039050451 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BronzeGuardian.java @@ -0,0 +1,60 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.ArtifactYouControlCount; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.continuous.SetPowerSourceEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +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 mage.constants.Zone; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BronzeGuardian extends CardImpl { + + public BronzeGuardian(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}{W}"); + + this.subtype.add(SubType.GOLEM); + this.power = new MageInt(0); + this.toughness = new MageInt(5); + + // Double strike + this.addAbility(DoubleStrikeAbility.getInstance()); + + // Ward {2} + this.addAbility(new WardAbility(new ManaCostsImpl<>("{2}"))); + + // Other artifacts you control have ward {2}. + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + new WardAbility(new GenericManaCost(2)), Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENT_ARTIFACTS, true + ))); + + // Bronze Guardian's power is equal to the number of artifacts you control. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerSourceEffect( + ArtifactYouControlCount.instance, Duration.EndOfGame + ))); + } + + private BronzeGuardian(final BronzeGuardian card) { + super(card); + } + + @Override + public BronzeGuardian copy() { + return new BronzeGuardian(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BronzeHorse.java b/Mage.Sets/src/mage/cards/b/BronzeHorse.java index 700ea591be33..50b532c1573b 100644 --- a/Mage.Sets/src/mage/cards/b/BronzeHorse.java +++ b/Mage.Sets/src/mage/cards/b/BronzeHorse.java @@ -21,7 +21,7 @@ import mage.game.events.GameEvent; import mage.game.stack.Spell; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.Target; /** diff --git a/Mage.Sets/src/mage/cards/b/BronzebeakMoa.java b/Mage.Sets/src/mage/cards/b/BronzebeakMoa.java index 949498cd939c..e5a06c6d5f5f 100644 --- a/Mage.Sets/src/mage/cards/b/BronzebeakMoa.java +++ b/Mage.Sets/src/mage/cards/b/BronzebeakMoa.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/b/BronzehideLion.java b/Mage.Sets/src/mage/cards/b/BronzehideLion.java index 6c4f719fdc12..36ef08e2a379 100644 --- a/Mage.Sets/src/mage/cards/b/BronzehideLion.java +++ b/Mage.Sets/src/mage/cards/b/BronzehideLion.java @@ -161,11 +161,12 @@ public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) TargetPermanent auraTarget = new TargetControlledCreaturePermanent(); lion.getSpellAbility().addTarget(auraTarget); lion.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - lion.addAbility(new EnchantAbility(auraTarget.getTargetName())); + lion.addAbility(new EnchantAbility(auraTarget.getTargetName()), source.getSourceId(), game); // add the activated ability activatedAbility.setControllerId(source.getControllerId()); - lion.addAbility(activatedAbility); + lion.addAbility(activatedAbility, source.getSourceId(), game); + break; } return true; } diff --git a/Mage.Sets/src/mage/cards/b/BroodingSaurian.java b/Mage.Sets/src/mage/cards/b/BroodingSaurian.java index 65eadd859f43..02dc1aa657c2 100644 --- a/Mage.Sets/src/mage/cards/b/BroodingSaurian.java +++ b/Mage.Sets/src/mage/cards/b/BroodingSaurian.java @@ -12,7 +12,7 @@ import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/b/BrothersYamazaki.java b/Mage.Sets/src/mage/cards/b/BrothersYamazaki.java index b89bc8b14188..eb339a13ba07 100644 --- a/Mage.Sets/src/mage/cards/b/BrothersYamazaki.java +++ b/Mage.Sets/src/mage/cards/b/BrothersYamazaki.java @@ -17,7 +17,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.NamePredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/b/BroughtBack.java b/Mage.Sets/src/mage/cards/b/BroughtBack.java index a8ede6d62aef..62104591a7f3 100644 --- a/Mage.Sets/src/mage/cards/b/BroughtBack.java +++ b/Mage.Sets/src/mage/cards/b/BroughtBack.java @@ -1,15 +1,12 @@ package mage.cards.b; -import mage.MageObjectReference; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.FilterCard; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.Predicate; -import mage.game.Game; +import mage.filter.predicate.card.PutIntoGraveFromBattlefieldThisTurnPredicate; import mage.target.common.TargetCardInYourGraveyard; import mage.watchers.common.CardsPutIntoGraveyardWatcher; @@ -25,7 +22,7 @@ public final class BroughtBack extends CardImpl { ); static { - filter.add(BroughtBackPredicate.instance); + filter.add(PutIntoGraveFromBattlefieldThisTurnPredicate.instance); } public BroughtBack(UUID ownerId, CardSetInfo setInfo) { @@ -51,14 +48,3 @@ public BroughtBack copy() { return new BroughtBack(this); } } - -enum BroughtBackPredicate implements Predicate { - instance; - - @Override - public boolean apply(Card input, Game game) { - CardsPutIntoGraveyardWatcher watcher = game.getState().getWatcher(CardsPutIntoGraveyardWatcher.class); - return watcher != null - && watcher.getCardsPutToGraveyardFromBattlefield().contains(new MageObjectReference(input, game)); - } -} diff --git a/Mage.Sets/src/mage/cards/b/BrownOuphe.java b/Mage.Sets/src/mage/cards/b/BrownOuphe.java index 2bea108b5f5b..3fa7fdc17efc 100644 --- a/Mage.Sets/src/mage/cards/b/BrownOuphe.java +++ b/Mage.Sets/src/mage/cards/b/BrownOuphe.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterStackObject; -import mage.filter.predicate.ability.ArtifactSourcePredicate; +import mage.filter.predicate.other.ArtifactSourcePredicate; import mage.target.common.TargetActivatedAbility; /** diff --git a/Mage.Sets/src/mage/cards/b/BrunaLightOfAlabaster.java b/Mage.Sets/src/mage/cards/b/BrunaLightOfAlabaster.java index 9258a60e2020..9329557a7b0a 100644 --- a/Mage.Sets/src/mage/cards/b/BrunaLightOfAlabaster.java +++ b/Mage.Sets/src/mage/cards/b/BrunaLightOfAlabaster.java @@ -18,8 +18,8 @@ import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardIdPredicate; -import mage.filter.predicate.other.AuraCardCanAttachToPermanentId; -import mage.filter.predicate.other.AuraPermanentCanAttachToPermanentId; +import mage.filter.predicate.card.AuraCardCanAttachToPermanentId; +import mage.filter.predicate.permanent.AuraPermanentCanAttachToPermanentId; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/b/BrunaTheFadingLight.java b/Mage.Sets/src/mage/cards/b/BrunaTheFadingLight.java index 23af75385ba9..967037b12ea2 100644 --- a/Mage.Sets/src/mage/cards/b/BrunaTheFadingLight.java +++ b/Mage.Sets/src/mage/cards/b/BrunaTheFadingLight.java @@ -1,7 +1,5 @@ - package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; @@ -18,25 +16,28 @@ import mage.constants.SuperType; import mage.constants.Zone; import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.Predicates; import mage.target.common.TargetCardInYourGraveyard; +import java.util.UUID; + /** - * * @author fireshoes */ public final class BrunaTheFadingLight extends CardImpl { - private static final FilterCard filter = new FilterCard("Angel or Human creature card"); + private static final FilterCard filter = new FilterCreatureCard("Angel or Human creature card"); static { - filter.add(Predicates.and(CardType.CREATURE.getPredicate(), - (Predicates.or(SubType.HUMAN.getPredicate(), - (SubType.ANGEL.getPredicate()))))); + filter.add(Predicates.or( + SubType.HUMAN.getPredicate(), + SubType.ANGEL.getPredicate() + )); } public BrunaTheFadingLight(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}{W}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ANGEL, SubType.HORROR); this.power = new MageInt(5); @@ -56,7 +57,7 @@ public BrunaTheFadingLight(UUID ownerId, CardSetInfo setInfo) { this.addAbility(VigilanceAbility.getInstance()); // (Melds with Gisela, the Broken Blade.) - this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("(Melds with Gisela, the Broken Blade.)"))); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("(Melds with Gisela, the Broken Blade.)"))); } private BrunaTheFadingLight(final BrunaTheFadingLight card) { diff --git a/Mage.Sets/src/mage/cards/b/BubblingMuck.java b/Mage.Sets/src/mage/cards/b/BubblingMuck.java index a99653d56ee5..aa6157e4ee55 100644 --- a/Mage.Sets/src/mage/cards/b/BubblingMuck.java +++ b/Mage.Sets/src/mage/cards/b/BubblingMuck.java @@ -68,7 +68,7 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { Permanent land = game.getPermanentOrLKIBattlefield(event.getTargetId()); - if (land != null && filter.match(land, getSourceId(), getControllerId(), game)) { + if (filter.match(land, getSourceId(), getControllerId(), game)) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(land.getControllerId())); } diff --git a/Mage.Sets/src/mage/cards/b/BullAurochs.java b/Mage.Sets/src/mage/cards/b/BullAurochs.java index 783ffdfe39c1..24392488007d 100644 --- a/Mage.Sets/src/mage/cards/b/BullAurochs.java +++ b/Mage.Sets/src/mage/cards/b/BullAurochs.java @@ -14,7 +14,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.common.FilterAttackingCreature; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/b/BullRushBruiser.java b/Mage.Sets/src/mage/cards/b/BullRushBruiser.java index 6a7035b67293..9727549dd373 100644 --- a/Mage.Sets/src/mage/cards/b/BullRushBruiser.java +++ b/Mage.Sets/src/mage/cards/b/BullRushBruiser.java @@ -13,7 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.common.FilterTeamPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/b/BuriedAlive.java b/Mage.Sets/src/mage/cards/b/BuriedAlive.java index c7b8ca6f1fad..5ac713c96e56 100644 --- a/Mage.Sets/src/mage/cards/b/BuriedAlive.java +++ b/Mage.Sets/src/mage/cards/b/BuriedAlive.java @@ -42,7 +42,7 @@ class BuriedAliveEffect extends SearchEffect { public BuriedAliveEffect() { super(new TargetCardInLibrary(0, 3, StaticFilters.FILTER_CARD_CREATURE), Outcome.Detriment); - staticText = "Search your library for up to three creature cards and put them into your graveyard. Then shuffle your library"; + staticText = "Search your library for up to three creature cards and put them into your graveyard. Then shuffle"; } public BuriedAliveEffect(final BuriedAliveEffect effect) { diff --git a/Mage.Sets/src/mage/cards/b/BurningRuneDemon.java b/Mage.Sets/src/mage/cards/b/BurningRuneDemon.java index e9ac2869a547..53e90462739b 100644 --- a/Mage.Sets/src/mage/cards/b/BurningRuneDemon.java +++ b/Mage.Sets/src/mage/cards/b/BurningRuneDemon.java @@ -65,7 +65,7 @@ public BurningRuneDemonEffect() { staticText = "search your library for exactly two cards " + "not named Burning-Rune Demon that have different names. If you do, reveal those cards. " + "An opponent chooses one of them. " - + "Put the chosen card into your hand and the other into your graveyard, then shuffle your library"; + + "Put the chosen card into your hand and the other into your graveyard, then shuffle"; } private BurningRuneDemonEffect(final BurningRuneDemonEffect effect) { diff --git a/Mage.Sets/src/mage/cards/b/BurningWish.java b/Mage.Sets/src/mage/cards/b/BurningWish.java index 9be224e3054a..1d2bf6d72b0d 100644 --- a/Mage.Sets/src/mage/cards/b/BurningWish.java +++ b/Mage.Sets/src/mage/cards/b/BurningWish.java @@ -28,7 +28,7 @@ public BurningWish(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new WishEffect(filter)); // Exile Burning Wish. - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private BurningWish(final BurningWish card) { diff --git a/Mage.Sets/src/mage/cards/b/BurningYardTrainer.java b/Mage.Sets/src/mage/cards/b/BurningYardTrainer.java index 3d37d2b461d7..a95486f9d9e3 100644 --- a/Mage.Sets/src/mage/cards/b/BurningYardTrainer.java +++ b/Mage.Sets/src/mage/cards/b/BurningYardTrainer.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/b/BurnishedHart.java b/Mage.Sets/src/mage/cards/b/BurnishedHart.java index 3213e577ef32..76ad304ba1ec 100644 --- a/Mage.Sets/src/mage/cards/b/BurnishedHart.java +++ b/Mage.Sets/src/mage/cards/b/BurnishedHart.java @@ -33,7 +33,7 @@ public BurnishedHart(UUID ownerId, CardSetInfo setInfo) { // {3}, Sacrifice Burnished Hart: Search your library for up to two basic land cards, put them onto the battlefield tapped, then shuffle your library. Ability ability = new SimpleActivatedAbility( Zone.BATTLEFIELD, - new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0,2, StaticFilters.FILTER_CARD_BASIC_LAND), true, true), + new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0,2, StaticFilters.FILTER_CARD_BASIC_LANDS), true, true), new GenericManaCost(3)); ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/b/BurntOffering.java b/Mage.Sets/src/mage/cards/b/BurntOffering.java index da088573730b..d1b4389bbe84 100644 --- a/Mage.Sets/src/mage/cards/b/BurntOffering.java +++ b/Mage.Sets/src/mage/cards/b/BurntOffering.java @@ -1,25 +1,15 @@ package mage.cards.b; -import java.util.LinkedHashSet; -import java.util.Set; import java.util.UUID; -import mage.Mana; -import mage.abilities.Ability; -import mage.abilities.costs.Cost; import mage.abilities.costs.common.SacrificeTargetCost; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.dynamicvalue.common.SacrificeCostConvertedMana; +import mage.abilities.effects.mana.AddManaInAnyCombinationEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.choices.Choice; -import mage.choices.ChoiceImpl; import mage.constants.CardType; -import mage.constants.Outcome; +import mage.constants.ColoredManaSymbol; import static mage.filter.StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; /** @@ -34,7 +24,10 @@ public BurntOffering(UUID ownerID, CardSetInfo setInfo) { //As an additional cost to cast Burnt Offering, sacrifice a creature. this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); //Add an amount of {B} and/or {R} equal to the sacrificed creature's converted mana cost. - this.getSpellAbility().addEffect(new BurntOfferingEffect()); + SacrificeCostConvertedMana xValue = new SacrificeCostConvertedMana("creature"); + this.getSpellAbility().addEffect(new AddManaInAnyCombinationEffect( + xValue, xValue, ColoredManaSymbol.B, ColoredManaSymbol.R + )); } private BurntOffering(final BurntOffering card) { @@ -46,77 +39,3 @@ public BurntOffering copy() { return new BurntOffering(this); } } - -class BurntOfferingEffect extends OneShotEffect { - - public BurntOfferingEffect() { - super(Outcome.PutManaInPool); - this.staticText = "Add X mana in any combination of {B} and/or {R}," - + " where X is the sacrificed creature's converted mana cost"; - } - - public BurntOfferingEffect(final BurntOfferingEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - Choice manaChoice = new ChoiceImpl(); - Set choices = new LinkedHashSet<>(); - choices.add("Red"); - choices.add("Black"); - manaChoice.setChoices(choices); - manaChoice.setMessage("Select color of mana to add"); - - int xValue = getCost(source); - - for (int i = 0; i < xValue; i++) { - Mana mana = new Mana(); - if (!player.choose(Outcome.Benefit, manaChoice, game)) { - return false; - } - if (manaChoice.getChoice() == null) { //Can happen if player leaves game - return false; - } - switch (manaChoice.getChoice()) { - case "Red": - mana.increaseRed(); - break; - case "Black": - mana.increaseBlack(); - break; - } - player.getManaPool().addMana(mana, game, source); - } - return true; - } - return false; - } - - @Override - public Effect copy() { - return new BurntOfferingEffect(this); - } - - /** - * Helper method to determine the CMC of the sacrificed creature. - * - * @param sourceAbility - * @return - */ - private int getCost(Ability sourceAbility) { - for (Cost cost : sourceAbility.getCosts()) { - if (cost instanceof SacrificeTargetCost) { - SacrificeTargetCost sacrificeCost = (SacrificeTargetCost) cost; - int totalCMC = 0; - for (Permanent permanent : sacrificeCost.getPermanents()) { - totalCMC += permanent.getConvertedManaCost(); - } - return totalCMC; - } - } - return 0; - } -} diff --git a/Mage.Sets/src/mage/cards/b/BurrogBefuddler.java b/Mage.Sets/src/mage/cards/b/BurrogBefuddler.java new file mode 100644 index 000000000000..30ffd840489c --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BurrogBefuddler.java @@ -0,0 +1,46 @@ +package mage.cards.b; + +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 BurrogBefuddler extends CardImpl { + + public BurrogBefuddler(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + + this.subtype.add(SubType.FROG); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // When Burrog Befuddler enters the battlefield, target creature an opponent controls gets -1/-0 until end of turn. + Ability ability = new EntersBattlefieldTriggeredAbility(new BoostTargetEffect(-1, -0)); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + this.addAbility(ability); + } + + private BurrogBefuddler(final BurrogBefuddler card) { + super(card); + } + + @Override + public BurrogBefuddler copy() { + return new BurrogBefuddler(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BurstLightning.java b/Mage.Sets/src/mage/cards/b/BurstLightning.java index db41f741ba93..52c03e02f3ff 100644 --- a/Mage.Sets/src/mage/cards/b/BurstLightning.java +++ b/Mage.Sets/src/mage/cards/b/BurstLightning.java @@ -26,7 +26,7 @@ public BurstLightning(UUID ownerId, CardSetInfo setInfo) { // Burst Lightning deals 2 damage to any target. If Burst Lightning was kicked, it deals 4 damage to that creature or player instead. this.getSpellAbility().addTarget(new TargetAnyTarget()); this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new DamageTargetEffect(4), - new DamageTargetEffect(2), KickedCondition.instance, "{this} deals 2 damage to any target. if this spell was kicked, it deals 4 damage to that permanent or player instead")); + new DamageTargetEffect(2), KickedCondition.instance, "{this} deals 2 damage to any target. If this spell was kicked, it deals 4 damage to that permanent or player instead")); } private BurstLightning(final BurstLightning card) { diff --git a/Mage.Sets/src/mage/cards/b/BuryInBooks.java b/Mage.Sets/src/mage/cards/b/BuryInBooks.java new file mode 100644 index 000000000000..0744a3ca9763 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BuryInBooks.java @@ -0,0 +1,78 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.SourceTargetsPermanentCondition; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterAttackingCreature; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BuryInBooks extends CardImpl { + + private static final FilterPermanent filter = new FilterAttackingCreature("an attacking creature"); + private static final Condition condition = new SourceTargetsPermanentCondition(filter); + + public BuryInBooks(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{U}"); + + // This spell costs {2} less to cast if it targets an attacking creature. + this.addAbility(new SimpleStaticAbility( + Zone.ALL, new SpellCostReductionSourceEffect(2, condition).setCanWorksOnStackOnly(true) + ).setRuleAtTheTop(true)); + + // Put target creature into its owner's library second from the top. + this.getSpellAbility().addEffect(new BuryInBooksEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private BuryInBooks(final BuryInBooks card) { + super(card); + } + + @Override + public BuryInBooks copy() { + return new BuryInBooks(this); + } +} + +class BuryInBooksEffect extends OneShotEffect { + + BuryInBooksEffect() { + super(Outcome.Benefit); + staticText = "put target creature into its owner's library second from the top"; + } + + private BuryInBooksEffect(final BuryInBooksEffect effect) { + super(effect); + } + + @Override + public BuryInBooksEffect copy() { + return new BuryInBooksEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(source.getFirstTarget()); + return player != null + && permanent != null + && player.putCardOnTopXOfLibrary(permanent, game, source, 2, true); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BushmeatPoacher.java b/Mage.Sets/src/mage/cards/b/BushmeatPoacher.java index f7ca63eeacfd..48a98416221f 100644 --- a/Mage.Sets/src/mage/cards/b/BushmeatPoacher.java +++ b/Mage.Sets/src/mage/cards/b/BushmeatPoacher.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/b/BygoneBishop.java b/Mage.Sets/src/mage/cards/b/BygoneBishop.java index da3e6c77abdd..6809610ffc08 100644 --- a/Mage.Sets/src/mage/cards/b/BygoneBishop.java +++ b/Mage.Sets/src/mage/cards/b/BygoneBishop.java @@ -12,7 +12,7 @@ import mage.constants.ComparisonType; import mage.constants.SubType; import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; /** * @@ -20,11 +20,11 @@ */ public final class BygoneBishop extends CardImpl { - private static final FilterSpell filterSpell = new FilterSpell("a creature spell with converted mana cost 3 or less"); + private static final FilterSpell filterSpell = new FilterSpell("a creature spell with mana value 3 or less"); static { filterSpell.add(CardType.CREATURE.getPredicate()); - filterSpell.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filterSpell.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public BygoneBishop(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/c/CabalConditioning.java b/Mage.Sets/src/mage/cards/c/CabalConditioning.java index 0b34f419b3bd..0e678b174229 100644 --- a/Mage.Sets/src/mage/cards/c/CabalConditioning.java +++ b/Mage.Sets/src/mage/cards/c/CabalConditioning.java @@ -2,7 +2,7 @@ package mage.cards.c; import java.util.UUID; -import mage.abilities.dynamicvalue.common.HighestConvertedManaCostValue; +import mage.abilities.dynamicvalue.common.HighestManaValueCount; import mage.abilities.effects.common.discard.DiscardTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -19,8 +19,8 @@ public CabalConditioning(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{6}{B}"); // Any number of target players each discard a number of cards equal to the highest converted mana cost among permanents you control. - this.getSpellAbility().addEffect(new DiscardTargetEffect(new HighestConvertedManaCostValue()) - .setText("Any number of target players each discard a number of cards equal to the highest converted mana cost among permanents you control.") + this.getSpellAbility().addEffect(new DiscardTargetEffect(new HighestManaValueCount()) + .setText("Any number of target players each discard a number of cards equal to the highest mana value among permanents you control.") ); this.getSpellAbility().addTarget(new TargetPlayer(0, Integer.MAX_VALUE, false)); } diff --git a/Mage.Sets/src/mage/cards/c/CabalInquisitor.java b/Mage.Sets/src/mage/cards/c/CabalInquisitor.java index 3247c5c10dc1..b10990d0e8a4 100644 --- a/Mage.Sets/src/mage/cards/c/CabalInquisitor.java +++ b/Mage.Sets/src/mage/cards/c/CabalInquisitor.java @@ -84,6 +84,6 @@ public ActivateAsSorceryConditionalActivatedAbility copy() { @Override public String getRule() { - return super.getRule() + " Activate this ability only any time you could cast a sorcery, and only if seven or more cards are in your graveyard."; + return super.getRule() + " Activate only as a sorcery, and only if seven or more cards are in your graveyard."; } } diff --git a/Mage.Sets/src/mage/cards/c/CalamityBearer.java b/Mage.Sets/src/mage/cards/c/CalamityBearer.java index 52c5420652c4..f69213fdb010 100644 --- a/Mage.Sets/src/mage/cards/c/CalamityBearer.java +++ b/Mage.Sets/src/mage/cards/c/CalamityBearer.java @@ -72,8 +72,7 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { @Override public boolean checksEventType(GameEvent event, Game game) { switch (event.getType()) { - case DAMAGE_CREATURE: - case DAMAGE_PLANESWALKER: + case DAMAGE_PERMANENT: case DAMAGE_PLAYER: return true; default: diff --git a/Mage.Sets/src/mage/cards/c/CallOfTheDeathDweller.java b/Mage.Sets/src/mage/cards/c/CallOfTheDeathDweller.java index 34af149da95e..583d2ad7fa5e 100644 --- a/Mage.Sets/src/mage/cards/c/CallOfTheDeathDweller.java +++ b/Mage.Sets/src/mage/cards/c/CallOfTheDeathDweller.java @@ -13,7 +13,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.filter.predicate.permanent.PermanentIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; @@ -55,7 +55,7 @@ class CallOfTheDeathDwellerEffect extends OneShotEffect { CallOfTheDeathDwellerEffect() { super(Outcome.Benefit); - staticText = "Return up to two target creature cards with total converted mana cost 3 or less " + + staticText = "Return up to two target creature cards with total mana value 3 or less " + "from your graveyard to the battlefield. Put a deathtouch counter on either of them. " + "Then put a menace counter on either of them."; } @@ -117,10 +117,10 @@ public boolean apply(Game game, Ability source) { class CallOfTheDeathDwellerTarget extends TargetCardInYourGraveyard { private static final FilterCard filter - = new FilterCreatureCard("creature cards with total converted mana cost 3 or less from your graveyard"); + = new FilterCreatureCard("creature cards with total mana value 3 or less from your graveyard"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } CallOfTheDeathDwellerTarget() { @@ -146,7 +146,7 @@ public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) this.getTargets() .stream() .map(game::getCard) - .mapToInt(Card::getConvertedManaCost) - .sum() + card.getConvertedManaCost() <= 3; + .mapToInt(Card::getManaValue) + .sum() + card.getManaValue() <= 3; } } diff --git a/Mage.Sets/src/mage/cards/c/CallToTheKindred.java b/Mage.Sets/src/mage/cards/c/CallToTheKindred.java index 0f99bf6fd2a5..27894b52efea 100644 --- a/Mage.Sets/src/mage/cards/c/CallToTheKindred.java +++ b/Mage.Sets/src/mage/cards/c/CallToTheKindred.java @@ -11,7 +11,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.permanent.SharesCreatureTypePredicate; +import mage.filter.predicate.mageobject.SharesCreatureTypePredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/c/CallerOfTheHunt.java b/Mage.Sets/src/mage/cards/c/CallerOfTheHunt.java index 7789349340c4..6a10b85f2b35 100644 --- a/Mage.Sets/src/mage/cards/c/CallerOfTheHunt.java +++ b/Mage.Sets/src/mage/cards/c/CallerOfTheHunt.java @@ -4,26 +4,30 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.CostAdjuster; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.choices.Choice; import mage.choices.ChoiceCreatureType; import mage.constants.*; +import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.players.Player; -import java.util.UUID; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; -import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; -import mage.filter.common.FilterCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + /** - * @author jeffwadsworth + * @author jeffwadsworth, JayDi85 */ public final class CallerOfTheHunt extends CardImpl { @@ -56,25 +60,67 @@ enum CallerOfTheHuntAdjuster implements CostAdjuster { @Override public void adjustCosts(Ability ability, Game game) { - MageObject mageObject = game.getObject(ability.getSourceId()); - Effect effect = new ChooseCreatureTypeEffect(Outcome.Benefit); - if (mageObject != null) { - effect.apply(game, ability); + if (game.inCheckPlayableState()) { + return; + } + + Player controller = game.getPlayer(ability.getControllerId()); + if (controller == null) { + return; + } + + MageObject sourceObject = game.getObject(ability.getSourceId()); + if (sourceObject == null) { + return; } - if (mageObject != null) { - SubType typeChoice = (SubType) game.getState().getValue(mageObject.getId() + "_type"); - if (typeChoice != null) { - FilterCreaturePermanent filter = new FilterCreaturePermanent("chosen creature type"); - filter.add(typeChoice.getPredicate()); - ContinuousEffect effectPowerToughness = new SetPowerToughnessSourceEffect( - new PermanentsOnBattlefieldCount(filter), Duration.EndOfGame); - effectPowerToughness.setText(""); - SimpleStaticAbility setPT = new SimpleStaticAbility(Zone.ALL, effectPowerToughness); - GainAbilityTargetEffect gainAbility = new GainAbilityTargetEffect(setPT, Duration.EndOfGame); - gainAbility.setTargetPointer(new FixedTarget(ability.getSourceId())); - game.getState().addEffect(gainAbility, ability); + + // AI hint - find best creature type with max permanents, all creature type supports too + Map usedSubTypeStats = new HashMap<>(); + game.getBattlefield().getActivePermanents(ability.getControllerId(), game) + .stream() + .map(permanent -> permanent.getSubtype(game)) + .flatMap(Collection::stream) + .distinct() + .forEach(subType -> { + FilterCreaturePermanent filter = new FilterCreaturePermanent(); + filter.add(subType.getPredicate()); + int amount = new PermanentsOnBattlefieldCount(filter).calculate(game, ability, null); + usedSubTypeStats.put(subType, amount); + }); + int maxAmount = 0; + SubType maxSubType = null; + for (Map.Entry entry : usedSubTypeStats.entrySet()) { + if (entry.getValue() > maxAmount) { + maxSubType = entry.getKey(); + maxAmount = entry.getValue(); } } + + // choose creature type + SubType typeChoice; + if (controller.isComputer()) { + // AI hint - simulate type choose + game.getState().setValue(sourceObject.getId() + "_type", maxSubType); + } else { + // human choose + Effect effect = new ChooseCreatureTypeEffect(Outcome.Benefit); + effect.apply(game, ability); + } + typeChoice = (SubType) game.getState().getValue(sourceObject.getId() + "_type"); + if (typeChoice == null) { + return; + } + + // apply boost + FilterCreaturePermanent filter = new FilterCreaturePermanent("chosen creature type"); + filter.add(typeChoice.getPredicate()); + ContinuousEffect effectPowerToughness = new SetPowerToughnessSourceEffect( + new PermanentsOnBattlefieldCount(filter), Duration.EndOfGame); + effectPowerToughness.setText(""); + SimpleStaticAbility setPT = new SimpleStaticAbility(Zone.ALL, effectPowerToughness); + GainAbilityTargetEffect gainAbility = new GainAbilityTargetEffect(setPT, Duration.EndOfGame); + gainAbility.setTargetPointer(new FixedTarget(ability.getSourceId())); + game.getState().addEffect(gainAbility, ability); } } diff --git a/Mage.Sets/src/mage/cards/c/CallousBloodmage.java b/Mage.Sets/src/mage/cards/c/CallousBloodmage.java new file mode 100644 index 000000000000..243c2b55c78a --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CallousBloodmage.java @@ -0,0 +1,57 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.ExileGraveyardAllTargetPlayerEffect; +import mage.abilities.effects.common.LoseLifeSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.WitherbloomToken; +import mage.target.TargetPlayer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CallousBloodmage extends CardImpl { + + public CallousBloodmage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // When Callous Bloodmage enters the battlefield, choose one — + // • Create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life." + Ability ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new WitherbloomToken())); + + // • You draw a card and you lose 1 life. + Mode mode = new Mode(new DrawCardSourceControllerEffect(1).setText("you draw a card")); + mode.addEffect(new LoseLifeSourceControllerEffect(1).concatBy("and")); + ability.addMode(mode); + + // • Exile target player's graveyard. + mode = new Mode(new ExileGraveyardAllTargetPlayerEffect().setText("exile target player's graveyard")); + mode.addTarget(new TargetPlayer()); + ability.addMode(mode); + this.addAbility(ability); + } + + private CallousBloodmage(final CallousBloodmage card) { + super(card); + } + + @Override + public CallousBloodmage copy() { + return new CallousBloodmage(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/Camel.java b/Mage.Sets/src/mage/cards/c/Camel.java index 0ceffdec3642..17ba9344c4ac 100644 --- a/Mage.Sets/src/mage/cards/c/Camel.java +++ b/Mage.Sets/src/mage/cards/c/Camel.java @@ -15,8 +15,8 @@ import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.game.Game; +import mage.game.events.DamageEvent; import mage.game.events.GameEvent; -import mage.game.events.DamageCreatureEvent; import mage.game.permanent.Permanent; /** @@ -73,14 +73,14 @@ public CamelEffect copy() { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (super.applies(event, source, game) && event instanceof DamageCreatureEvent && event.getAmount() > 0) { - DamageCreatureEvent damageEvent = (DamageCreatureEvent) event; + if (super.applies(event, source, game) && event instanceof DamageEvent && event.getAmount() > 0) { + DamageEvent damageEvent = (DamageEvent) event; Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (sourcePermanent != null && sourcePermanent.isAttacking() && (event.getTargetId().equals(source.getSourceId()) || sourcePermanent.getBandedCards().contains(event.getTargetId()))) { Permanent permanent = game.getPermanentOrLKIBattlefield(damageEvent.getSourceId()); - if (permanent != null && filter.match(permanent, game)) { + if (filter.match(permanent, game)) { return true; } } diff --git a/Mage.Sets/src/mage/cards/c/CampusGuide.java b/Mage.Sets/src/mage/cards/c/CampusGuide.java new file mode 100644 index 000000000000..7c22b7956db6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CampusGuide.java @@ -0,0 +1,41 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.search.SearchLibraryPutOnLibraryEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CampusGuide extends CardImpl { + + public CampusGuide(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}"); + + this.subtype.add(SubType.GOLEM); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // When Campus Guide enters the battlefield, you may search your library for a basic land card, reveal it, then shuffle and put that card on top. + this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutOnLibraryEffect( + new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true, true + ), true)); + } + + private CampusGuide(final CampusGuide card) { + super(card); + } + + @Override + public CampusGuide copy() { + return new CampusGuide(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CanopySurge.java b/Mage.Sets/src/mage/cards/c/CanopySurge.java index 0197b2532b0c..80ffa621cfc9 100644 --- a/Mage.Sets/src/mage/cards/c/CanopySurge.java +++ b/Mage.Sets/src/mage/cards/c/CanopySurge.java @@ -33,7 +33,7 @@ public CanopySurge(UUID ownerId, CardSetInfo setInfo) { // Canopy Surge deals 1 damage to each creature with flying and each player. If Canopy Surge was kicked, it deals 4 damage to each creature with flying and each player instead. this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new DamageEverythingEffect(4, filter), new DamageEverythingEffect(1, filter), KickedCondition.instance, - "{this} deals 1 damage to each creature with flying and each player. if this spell was kicked, it deals 4 damage to each creature with flying and each player instead.")); + "{this} deals 1 damage to each creature with flying and each player. If this spell was kicked, it deals 4 damage to each creature with flying and each player instead.")); } private CanopySurge(final CanopySurge card) { diff --git a/Mage.Sets/src/mage/cards/c/CapitalPunishment.java b/Mage.Sets/src/mage/cards/c/CapitalPunishment.java index 5be6bf08af86..b84613cb15f5 100644 --- a/Mage.Sets/src/mage/cards/c/CapitalPunishment.java +++ b/Mage.Sets/src/mage/cards/c/CapitalPunishment.java @@ -1,24 +1,23 @@ package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.CouncilsDilemmaVoteEffect; +import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.SacrificeOpponentsEffect; import mage.abilities.effects.common.discard.DiscardEachPlayerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.choices.TwoChoiceVote; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.TargetController; import mage.filter.StaticFilters; import mage.game.Game; -import mage.players.Player; + +import java.util.UUID; /** - * - * @author JRHerlehy + * @author JRHerlehy, TheElk801 */ public final class CapitalPunishment extends CardImpl { @@ -26,7 +25,7 @@ public CapitalPunishment(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}{B}"); // Council's dilemma — Starting with you, each player votes for death or taxes. Each opponent sacrifices a creature for each death vote and discards a card for each taxes vote. - this.getSpellAbility().addEffect(new CapitalPunishmentDilemmaEffect()); + this.getSpellAbility().addEffect(new CapitalPunishmentEffect()); } private CapitalPunishment(final CapitalPunishment card) { @@ -39,45 +38,42 @@ public CapitalPunishment copy() { } } -class CapitalPunishmentDilemmaEffect extends CouncilsDilemmaVoteEffect { +class CapitalPunishmentEffect extends OneShotEffect { - public CapitalPunishmentDilemmaEffect() { - super(Outcome.Detriment); - this.staticText = "Council's dilemma — Starting with you, each player votes for death or taxes. Each opponent sacrifices a creature for each death vote and discards a card for each taxes vote"; + CapitalPunishmentEffect() { + super(Outcome.Benefit); + this.staticText = "Council's dilemma — Starting with you, each player votes for death or taxes. " + + "Each opponent sacrifices a creature for each death vote and discards a card for each taxes vote"; } - public CapitalPunishmentDilemmaEffect(final CapitalPunishmentDilemmaEffect effect) { + private CapitalPunishmentEffect(final CapitalPunishmentEffect effect) { super(effect); } @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - - //If no controller, exit out here and do not vote. - if (controller == null) { - return false; - } - - this.vote("death", "taxes", controller, game, source); + public CapitalPunishmentEffect copy() { + return new CapitalPunishmentEffect(this); + } - //Death Votes - if (voteOneCount > 0) { - Effect sacrificeEffect = new SacrificeOpponentsEffect(voteOneCount, StaticFilters.FILTER_CONTROLLED_CREATURE); - sacrificeEffect.apply(game, source); + @Override + public boolean apply(Game game, Ability source) { + // Outcome.Detriment - AI will discard a card all the time (taxes choice) + // TODO: add AI hint logic in the choice method, see Tyrant's Choice as example + TwoChoiceVote vote = new TwoChoiceVote("Death (sacrifice creature)", "Taxes (discard card)", Outcome.Detriment); + vote.doVotes(source, game); + + int deathCount = vote.getVoteCount(true); + int taxesCount = vote.getVoteCount(false); + if (deathCount > 0) { + new SacrificeOpponentsEffect( + deathCount, StaticFilters.FILTER_CONTROLLED_CREATURE + ).apply(game, source); } - - //Taxes Votes - if (voteTwoCount > 0) { - Effect discardEffect = new DiscardEachPlayerEffect(StaticValue.get(voteTwoCount), false, TargetController.OPPONENT); - discardEffect.apply(game, source); + if (taxesCount > 0) { + new DiscardEachPlayerEffect( + StaticValue.get(taxesCount), false, TargetController.OPPONENT + ).apply(game, source); } - return true; } - - @Override - public CapitalPunishmentDilemmaEffect copy() { - return new CapitalPunishmentDilemmaEffect(this); - } } diff --git a/Mage.Sets/src/mage/cards/c/CaptainOfTheMists.java b/Mage.Sets/src/mage/cards/c/CaptainOfTheMists.java index afdd38ef50cd..31273351d968 100644 --- a/Mage.Sets/src/mage/cards/c/CaptainOfTheMists.java +++ b/Mage.Sets/src/mage/cards/c/CaptainOfTheMists.java @@ -17,7 +17,7 @@ import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; diff --git a/Mage.Sets/src/mage/cards/c/CaravanVigil.java b/Mage.Sets/src/mage/cards/c/CaravanVigil.java index 00e8f3079f47..fd702a6e0ee4 100644 --- a/Mage.Sets/src/mage/cards/c/CaravanVigil.java +++ b/Mage.Sets/src/mage/cards/c/CaravanVigil.java @@ -43,7 +43,7 @@ class CaravanVigilEffect extends OneShotEffect { public CaravanVigilEffect() { super(Outcome.PutLandInPlay); - this.staticText = "Search your library for a basic land card, reveal it, put it into your hand, then shuffle your library.
" + this.staticText = "Search your library for a basic land card, reveal it, put it into your hand, then shuffle.
" + "Morbid — You may put that card onto the battlefield instead of putting it into your hand if a creature died this turn"; } @@ -67,7 +67,7 @@ public boolean apply(Game game, Ability source) { if (card != null) { Cards cards = new CardsImpl(card); if (MorbidCondition.instance.apply(game, source) - && controller.chooseUse(Outcome.PutLandInPlay, "Do you wish to put the card onto the battlefield instead?", source, game)) { + && controller.chooseUse(Outcome.PutLandInPlay, "Put the card onto the battlefield instead?", source, game)) { controller.moveCards(card, Zone.BATTLEFIELD, source, game); } else { controller.moveCards(card, Zone.HAND, source, game); diff --git a/Mage.Sets/src/mage/cards/c/CarnifexDemon.java b/Mage.Sets/src/mage/cards/c/CarnifexDemon.java index 41f5cef598ce..93b6260494c5 100644 --- a/Mage.Sets/src/mage/cards/c/CarnifexDemon.java +++ b/Mage.Sets/src/mage/cards/c/CarnifexDemon.java @@ -17,7 +17,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/c/Carom.java b/Mage.Sets/src/mage/cards/c/Carom.java index a6244f9f7a85..71b86294535c 100644 --- a/Mage.Sets/src/mage/cards/c/Carom.java +++ b/Mage.Sets/src/mage/cards/c/Carom.java @@ -11,7 +11,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/c/Carrionette.java b/Mage.Sets/src/mage/cards/c/Carrionette.java index 27186f20f299..b9ce4c55d26b 100644 --- a/Mage.Sets/src/mage/cards/c/Carrionette.java +++ b/Mage.Sets/src/mage/cards/c/Carrionette.java @@ -31,7 +31,7 @@ public Carrionette(UUID ownerId, CardSetInfo setInfo) { // {2}{B}{B}: Exile Carrionette and target creature unless that creature's controller pays {2}. Activate this ability only if Carrionette is in your graveyard. DoUnlessTargetPlayerOrTargetsControllerPaysEffect effect = new DoUnlessTargetPlayerOrTargetsControllerPaysEffect(new ExileTargetEffect(), new ManaCostsImpl("{2}")); effect.addEffect(new ExileSourceEffect()); - effect.setText("Exile {this} and target creature unless that creature's controller pays {2}. Activate this ability only if {this} is in your graveyard"); + effect.setText("Exile {this} and target creature unless that creature's controller pays {2}. Activate only if {this} is in your graveyard"); Ability ability = new SimpleActivatedAbility(Zone.GRAVEYARD, effect, new ManaCostsImpl("{2}{B}{B}")); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/c/CartographersHawk.java b/Mage.Sets/src/mage/cards/c/CartographersHawk.java index a9168c1635fc..9b4e90b17416 100644 --- a/Mage.Sets/src/mage/cards/c/CartographersHawk.java +++ b/Mage.Sets/src/mage/cards/c/CartographersHawk.java @@ -85,7 +85,7 @@ public boolean checkTrigger(GameEvent event, Game game) { public String getRule() { return "When {this} deals combat damage to a player who controls more lands than you, " + "return it to its owner's hand. If you do, you may search your library for a Plains card, " + - "put it onto the battlefield tapped, then shuffle your library."; + "put it onto the battlefield tapped, then shuffle."; } } diff --git a/Mage.Sets/src/mage/cards/c/CatacombSifter.java b/Mage.Sets/src/mage/cards/c/CatacombSifter.java index a7f70d1c714b..f31d57b63112 100644 --- a/Mage.Sets/src/mage/cards/c/CatacombSifter.java +++ b/Mage.Sets/src/mage/cards/c/CatacombSifter.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.EldraziScionToken; /** diff --git a/Mage.Sets/src/mage/cards/c/CateranBrute.java b/Mage.Sets/src/mage/cards/c/CateranBrute.java index 063dbdfd2894..939942429bbd 100644 --- a/Mage.Sets/src/mage/cards/c/CateranBrute.java +++ b/Mage.Sets/src/mage/cards/c/CateranBrute.java @@ -16,7 +16,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; /** @@ -25,11 +25,11 @@ */ public final class CateranBrute extends CardImpl { - private static final FilterPermanentCard filter = new FilterPermanentCard("Mercenary permanent card with converted mana cost 2 or less"); + private static final FilterPermanentCard filter = new FilterPermanentCard("Mercenary permanent card with mana value 2 or less"); static { filter.add(SubType.MERCENARY.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); } public CateranBrute(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/c/CateranEnforcer.java b/Mage.Sets/src/mage/cards/c/CateranEnforcer.java index 4eb5ca912ad3..379b0f498c1d 100644 --- a/Mage.Sets/src/mage/cards/c/CateranEnforcer.java +++ b/Mage.Sets/src/mage/cards/c/CateranEnforcer.java @@ -17,7 +17,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; /** @@ -26,11 +26,11 @@ */ public final class CateranEnforcer extends CardImpl { - private static final FilterPermanentCard filter = new FilterPermanentCard("Mercenary permanent card with converted mana cost 4 or less"); + private static final FilterPermanentCard filter = new FilterPermanentCard("Mercenary permanent card with mana value 4 or less"); static { filter.add(SubType.MERCENARY.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 5)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 5)); } public CateranEnforcer(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/c/CateranKidnappers.java b/Mage.Sets/src/mage/cards/c/CateranKidnappers.java index 94e37aecb7d1..6d0167036173 100644 --- a/Mage.Sets/src/mage/cards/c/CateranKidnappers.java +++ b/Mage.Sets/src/mage/cards/c/CateranKidnappers.java @@ -16,7 +16,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; /** @@ -24,11 +24,11 @@ */ public final class CateranKidnappers extends CardImpl { - private static final FilterPermanentCard filter = new FilterPermanentCard("Mercenary permanent card with converted mana cost 3 or less"); + private static final FilterPermanentCard filter = new FilterPermanentCard("Mercenary permanent card with mana value 3 or less"); static { filter.add(SubType.MERCENARY.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public CateranKidnappers(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/c/CateranOverlord.java b/Mage.Sets/src/mage/cards/c/CateranOverlord.java index ccbdd238aa8b..267e6b266489 100644 --- a/Mage.Sets/src/mage/cards/c/CateranOverlord.java +++ b/Mage.Sets/src/mage/cards/c/CateranOverlord.java @@ -18,7 +18,7 @@ import mage.constants.Zone; import static mage.filter.StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetControlledCreaturePermanent; @@ -28,11 +28,11 @@ */ public final class CateranOverlord extends CardImpl { - private static final FilterPermanentCard filter = new FilterPermanentCard("Mercenary permanent card with converted mana cost 6 or less"); + private static final FilterPermanentCard filter = new FilterPermanentCard("Mercenary permanent card with mana value 6 or less"); static { filter.add(SubType.MERCENARY.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 7)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 7)); } public CateranOverlord(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/c/CateranPersuader.java b/Mage.Sets/src/mage/cards/c/CateranPersuader.java index f6a8bcc0452a..caeacfd882c8 100644 --- a/Mage.Sets/src/mage/cards/c/CateranPersuader.java +++ b/Mage.Sets/src/mage/cards/c/CateranPersuader.java @@ -16,7 +16,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; /** @@ -25,11 +25,11 @@ */ public final class CateranPersuader extends CardImpl { - private static final FilterPermanentCard filter = new FilterPermanentCard("Mercenary permanent card with converted mana cost 1 or less"); + private static final FilterPermanentCard filter = new FilterPermanentCard("Mercenary permanent card with mana value 1 or less"); static { filter.add(SubType.MERCENARY.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 2)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 2)); } public CateranPersuader(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/c/CateranSlaver.java b/Mage.Sets/src/mage/cards/c/CateranSlaver.java index b0ef6b00d47d..1ee40bdd05c4 100644 --- a/Mage.Sets/src/mage/cards/c/CateranSlaver.java +++ b/Mage.Sets/src/mage/cards/c/CateranSlaver.java @@ -17,7 +17,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; /** @@ -26,11 +26,11 @@ */ public final class CateranSlaver extends CardImpl { - private static final FilterPermanentCard filter = new FilterPermanentCard("Mercenary permanent card with converted mana cost 5 or less"); + private static final FilterPermanentCard filter = new FilterPermanentCard("Mercenary permanent card with mana value 5 or less"); static { filter.add(SubType.MERCENARY.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 6)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 6)); } public CateranSlaver(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/c/CauterySliver.java b/Mage.Sets/src/mage/cards/c/CauterySliver.java index 4a44395bd0ac..e3a443e068ef 100644 --- a/Mage.Sets/src/mage/cards/c/CauterySliver.java +++ b/Mage.Sets/src/mage/cards/c/CauterySliver.java @@ -5,7 +5,7 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.SacrificeSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.PreventDamageToTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityAllEffect; @@ -14,7 +14,6 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePlayerOrPlaneswalker; import mage.target.common.TargetAnyTarget; @@ -27,6 +26,8 @@ public final class CauterySliver extends CardImpl { private static final FilterPermanent filter = new FilterPermanent(SubType.SLIVER, "All Slivers"); + private static final FilterCreaturePlayerOrPlaneswalker filter2 + = new FilterCreaturePlayerOrPlaneswalker("player, planeswalker, or Sliver creature", SubType.SLIVER); public CauterySliver(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{W}"); @@ -35,21 +36,27 @@ public CauterySliver(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(2); // All Slivers have "{1}, Sacrifice this permanent: This permanent deals 1 damage to any target." - Ability ability1 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new ManaCostsImpl("1")); - ability1.addCost(new SacrificeSourceCost()); - ability1.addTarget(new TargetAnyTarget()); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new GainAbilityAllEffect(ability1, Duration.WhileOnBattlefield, filter, - "All Slivers have \"{1}, Sacrifice this permanent: This permanent deals 1 damage to any target.\""))); + Ability ability = new SimpleActivatedAbility( + new DamageTargetEffect(1, "this permanent"), new GenericManaCost(1) + ); + ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new TargetAnyTarget()); + this.addAbility(new SimpleStaticAbility(new GainAbilityAllEffect( + ability, Duration.WhileOnBattlefield, filter, "All Slivers have \"{1}, " + + "Sacrifice this permanent: This permanent deals 1 damage to any target.\"" + ))); - // All Slivers have "{1}, Sacrifice this permanent: Prevent the next 1 damage that would be dealt to target Sliver creature or player this turn." // All Slivers have "{1}, Sacrifice this permanent: Prevent the next 1 damage that would be dealt to target player, planeswalker, or Sliver creature this turn." - Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PreventDamageToTargetEffect(Duration.EndOfTurn, 1), new ManaCostsImpl("1")); - ability2.addCost(new SacrificeSourceCost()); - ability2.addTarget(new TargetAnyTarget(new FilterCreatureOrPlayerByType(SubType.SLIVER, "Sliver creature or player"))); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new GainAbilityAllEffect(ability2, Duration.WhileOnBattlefield, filter, - "All Slivers have \"{1}, Sacrifice this permanent: Prevent the next 1 damage that would be dealt to target Sliver creature or player this turn.\""))); + ability = new SimpleActivatedAbility( + new PreventDamageToTargetEffect(Duration.EndOfTurn, 1), new GenericManaCost(1) + ); + ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new TargetAnyTarget(filter2)); + this.addAbility(new SimpleStaticAbility(new GainAbilityAllEffect( + ability, Duration.WhileOnBattlefield, filter, "All Slivers have " + + "\"{1}, Sacrifice this permanent: Prevent the next 1 damage " + + "that would be dealt to target player, planeswalker, or Sliver creature this turn.\"" + ))); } private CauterySliver(final CauterySliver card) { @@ -61,11 +68,3 @@ public CauterySliver copy() { return new CauterySliver(this); } } - -class FilterCreatureOrPlayerByType extends FilterCreaturePlayerOrPlaneswalker { - - public FilterCreatureOrPlayerByType(SubType subType, String name) { - super(name); - this.getPermanentFilter().add(subType.getPredicate()); - } -} diff --git a/Mage.Sets/src/mage/cards/c/CavalierOfFlame.java b/Mage.Sets/src/mage/cards/c/CavalierOfFlame.java index cd31d66bf15a..995f91e5d696 100644 --- a/Mage.Sets/src/mage/cards/c/CavalierOfFlame.java +++ b/Mage.Sets/src/mage/cards/c/CavalierOfFlame.java @@ -8,22 +8,18 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageAllEffect; import mage.abilities.effects.common.DamagePlayersEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.discard.DiscardAndDrawThatManyEffect; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.CardsImpl; import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterPlaneswalkerPermanent; -import mage.game.Game; -import mage.players.Player; -import mage.target.common.TargetCardInHand; import java.util.UUID; @@ -58,7 +54,7 @@ public CavalierOfFlame(UUID ownerId, CardSetInfo setInfo) { this.addAbility(ability); // When Cavalier of Flame enters the battlefield, discard any number of cards, then draw that many cards. - this.addAbility(new EntersBattlefieldTriggeredAbility(new CavalierOfFlameEffect())); + this.addAbility(new EntersBattlefieldTriggeredAbility(new DiscardAndDrawThatManyEffect(Integer.MAX_VALUE))); // When Cavalier of Flame dies, it deals X damage to each opponent and each planeswalker they control, where X is the number of land cards in your graveyard. ability = new DiesSourceTriggeredAbility(new DamagePlayersEffect( @@ -79,34 +75,3 @@ public CavalierOfFlame copy() { return new CavalierOfFlame(this); } } - -class CavalierOfFlameEffect extends OneShotEffect { - - CavalierOfFlameEffect() { - super(Outcome.Benefit); - staticText = "discard any number of cards, then draw that many cards."; - } - - private CavalierOfFlameEffect(final CavalierOfFlameEffect effect) { - super(effect); - } - - @Override - public CavalierOfFlameEffect copy() { - return new CavalierOfFlameEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player == null) { - return false; - } - TargetCardInHand target = new TargetCardInHand(0, player.getHand().size(), StaticFilters.FILTER_CARD); - if (player.choose(Outcome.Discard, player.getHand(), target, game)) { - int counter = player.discard(new CardsImpl(target.getTargets()), false, source, game).size(); - player.drawCards(counter, source, game); - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/c/CavalierOfNight.java b/Mage.Sets/src/mage/cards/c/CavalierOfNight.java index 11b65d700402..6c0ab003f5d1 100644 --- a/Mage.Sets/src/mage/cards/c/CavalierOfNight.java +++ b/Mage.Sets/src/mage/cards/c/CavalierOfNight.java @@ -19,8 +19,8 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetOpponentsCreaturePermanent; @@ -35,11 +35,11 @@ public final class CavalierOfNight extends CardImpl { private static final FilterControlledPermanent filter = new FilterControlledCreaturePermanent("another creature"); private static final FilterCard filter2 - = new FilterCreatureCard("creature card with converted mana cost 3 or less from your graveyard"); + = new FilterCreatureCard("creature card with mana value 3 or less from your graveyard"); static { filter.add(AnotherPredicate.instance); - filter2.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter2.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public CavalierOfNight(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/c/CavalierOfThorns.java b/Mage.Sets/src/mage/cards/c/CavalierOfThorns.java index 14b84ac4a131..6f65acd4a1db 100644 --- a/Mage.Sets/src/mage/cards/c/CavalierOfThorns.java +++ b/Mage.Sets/src/mage/cards/c/CavalierOfThorns.java @@ -16,7 +16,7 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.common.FilterLandCard; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; diff --git a/Mage.Sets/src/mage/cards/c/CeaseFire.java b/Mage.Sets/src/mage/cards/c/CeaseFire.java index 12cfa696a456..dc559d1afe95 100644 --- a/Mage.Sets/src/mage/cards/c/CeaseFire.java +++ b/Mage.Sets/src/mage/cards/c/CeaseFire.java @@ -80,7 +80,7 @@ public boolean checksEventType(GameEvent event, Game game) { public boolean applies(GameEvent event, Ability source, Game game) { if (event.getPlayerId().equals(getTargetPointer().getFirst(game, source))) { Spell spell = game.getStack().getSpell(event.getSourceId()); - if (spell != null && filter.match(spell, game)) { + if (filter.match(spell, game)) { return true; } } diff --git a/Mage.Sets/src/mage/cards/c/CelestialDawn.java b/Mage.Sets/src/mage/cards/c/CelestialDawn.java index 4b7f53b7295f..26fe0d830b9a 100644 --- a/Mage.Sets/src/mage/cards/c/CelestialDawn.java +++ b/Mage.Sets/src/mage/cards/c/CelestialDawn.java @@ -88,7 +88,7 @@ public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); break; case TypeChangingEffects_4: - land.removeAllSubTypes(game,SubTypeSet.NonBasicLandType); + land.removeAllSubTypes(game, SubTypeSet.NonBasicLandType); land.addSubType(game, SubType.PLAINS); break; } @@ -241,7 +241,9 @@ public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) { if (mana.getWhite() == 0) { return ManaType.COLORLESS; + } else { + // must return manaType cause applied all the time + return manaType; } - return manaType; } } diff --git a/Mage.Sets/src/mage/cards/c/CelestialKirin.java b/Mage.Sets/src/mage/cards/c/CelestialKirin.java index 4f4321881fd9..1340fae59e22 100644 --- a/Mage.Sets/src/mage/cards/c/CelestialKirin.java +++ b/Mage.Sets/src/mage/cards/c/CelestialKirin.java @@ -13,7 +13,7 @@ import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.stack.Spell; @@ -52,7 +52,7 @@ class CelestialKirinEffect extends OneShotEffect { public CelestialKirinEffect() { super(Outcome.GainLife); - this.staticText = "destroy all permanents with that spell's converted mana cost"; + this.staticText = "destroy all permanents with that spell's mana value"; } public CelestialKirinEffect(final CelestialKirinEffect effect) { @@ -68,9 +68,9 @@ public CelestialKirinEffect copy() { public boolean apply(Game game, Ability source) { Spell spell = game.getSpellOrLKIStack(this.getTargetPointer().getFirst(game, source)); if (spell != null) { - int cmc = spell.getConvertedManaCost(); + int cmc = spell.getManaValue(); FilterPermanent filter = new FilterPermanent(); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, cmc)); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, cmc)); return new DestroyAllEffect(filter).apply(game, source); } return false; diff --git a/Mage.Sets/src/mage/cards/c/CennsHeir.java b/Mage.Sets/src/mage/cards/c/CennsHeir.java index 6263ade2ce0c..4c7973f4ec38 100644 --- a/Mage.Sets/src/mage/cards/c/CennsHeir.java +++ b/Mage.Sets/src/mage/cards/c/CennsHeir.java @@ -12,7 +12,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.common.FilterAttackingCreature; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/c/CerebralEruption.java b/Mage.Sets/src/mage/cards/c/CerebralEruption.java index 64a852a2f9fc..518413092ca9 100644 --- a/Mage.Sets/src/mage/cards/c/CerebralEruption.java +++ b/Mage.Sets/src/mage/cards/c/CerebralEruption.java @@ -43,7 +43,7 @@ class CerebralEruptionEffect extends OneShotEffect { CerebralEruptionEffect() { super(Outcome.Damage); - staticText = "Target opponent reveals the top card of their library. {this} deals damage equal to the revealed card's converted mana cost to that player and each creature they control. If a land card is revealed this way, return {this} to its owner's hand"; + staticText = "Target opponent reveals the top card of their library. {this} deals damage equal to the revealed card's mana value to that player and each creature they control. If a land card is revealed this way, return {this} to its owner's hand"; } CerebralEruptionEffect(final CerebralEruptionEffect effect) { @@ -59,7 +59,7 @@ public boolean apply(Game game, Ability source) { Cards cards = new CardsImpl(card); player.revealCards(sourceObject.getIdName(), cards, game); game.getState().setValue(source.getSourceId().toString(), card); - int damage = card.getConvertedManaCost(); + int damage = card.getManaValue(); player.damage(damage, source.getSourceId(), source, game); for (Permanent perm : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURES, player.getId(), game)) { perm.damage(damage, source.getSourceId(), source, game, false, true); diff --git a/Mage.Sets/src/mage/cards/c/ChainLightning.java b/Mage.Sets/src/mage/cards/c/ChainLightning.java index 3f9eb89e1219..9bfc6111a6e4 100644 --- a/Mage.Sets/src/mage/cards/c/ChainLightning.java +++ b/Mage.Sets/src/mage/cards/c/ChainLightning.java @@ -79,7 +79,6 @@ public boolean apply(Game game, Ability source) { Spell spell = game.getStack().getSpell(source.getSourceId()); if (spell != null) { spell.createCopyOnStack(game, source, affectedPlayer.getId(), true); - game.informPlayers(affectedPlayer.getLogName() + " copies " + spell.getName() + '.'); } } } diff --git a/Mage.Sets/src/mage/cards/c/ChainOfAcid.java b/Mage.Sets/src/mage/cards/c/ChainOfAcid.java index 91f78897ca49..30383dc46d18 100644 --- a/Mage.Sets/src/mage/cards/c/ChainOfAcid.java +++ b/Mage.Sets/src/mage/cards/c/ChainOfAcid.java @@ -81,7 +81,6 @@ public boolean apply(Game game, Ability source) { Spell spell = game.getStack().getSpell(source.getSourceId()); if (spell != null) { spell.createCopyOnStack(game, source, affectedPlayer.getId(), true); - game.informPlayers(affectedPlayer.getLogName() + " copies " + spell.getName() + '.'); } } return true; diff --git a/Mage.Sets/src/mage/cards/c/ChainOfPlasma.java b/Mage.Sets/src/mage/cards/c/ChainOfPlasma.java index ed904c4e67ec..34145dcfb391 100644 --- a/Mage.Sets/src/mage/cards/c/ChainOfPlasma.java +++ b/Mage.Sets/src/mage/cards/c/ChainOfPlasma.java @@ -79,7 +79,6 @@ public boolean apply(Game game, Ability source) { Spell spell = game.getStack().getSpell(source.getSourceId()); if (spell != null) { spell.createCopyOnStack(game, source, affectedPlayer.getId(), true); - game.informPlayers(affectedPlayer.getLogName() + " copies " + spell.getName() + '.'); } } } diff --git a/Mage.Sets/src/mage/cards/c/ChainOfSilence.java b/Mage.Sets/src/mage/cards/c/ChainOfSilence.java index ae2c779b8d2c..d6112076c972 100644 --- a/Mage.Sets/src/mage/cards/c/ChainOfSilence.java +++ b/Mage.Sets/src/mage/cards/c/ChainOfSilence.java @@ -1,7 +1,6 @@ package mage.cards.c; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; @@ -17,20 +16,20 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.Spell; -import mage.game.stack.StackObject; import mage.players.Player; import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class ChainOfSilence extends CardImpl { public ChainOfSilence(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); - + // Prevent all damage target creature would deal this turn. That creature's controller may sacrifice a land. If the player does, they may copy this spell and may choose a new target for that copy. this.getSpellAbility().addEffect(new ChainOfSilenceEffect()); @@ -46,6 +45,7 @@ public ChainOfSilence copy() { return new ChainOfSilence(this); } } + class ChainOfSilenceEffect extends OneShotEffect { public ChainOfSilenceEffect() { @@ -80,14 +80,7 @@ public boolean apply(Game game, Ability source) { if (player.chooseUse(outcome, "Copy the spell?", source, game)) { Spell spell = game.getStack().getSpell(source.getSourceId()); if (spell != null) { - StackObject newStackObject = spell.createCopyOnStack(game, source, player.getId(), true); - if (newStackObject instanceof Spell) { - String activateMessage = ((Spell) newStackObject).getActivatedMessage(game); - if (activateMessage.startsWith(" casts ")) { - activateMessage = activateMessage.substring(6); - } - game.informPlayers(player.getLogName() + ' ' + activateMessage); - } + spell.createCopyOnStack(game, source, player.getId(), true); } } } diff --git a/Mage.Sets/src/mage/cards/c/ChainOfVapor.java b/Mage.Sets/src/mage/cards/c/ChainOfVapor.java index fcf7bd84e14d..908da1164f44 100644 --- a/Mage.Sets/src/mage/cards/c/ChainOfVapor.java +++ b/Mage.Sets/src/mage/cards/c/ChainOfVapor.java @@ -1,7 +1,6 @@ package mage.cards.c; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; @@ -15,19 +14,19 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.Spell; -import mage.game.stack.StackObject; import mage.players.Player; import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetNonlandPermanent; +import java.util.UUID; + /** - * * @author Plopman */ public final class ChainOfVapor extends CardImpl { public ChainOfVapor(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}"); // Return target nonland permanent to its owner's hand. Then that permanent's controller may sacrifice a land. If the player does, they may copy this spell and may choose a new target for that copy. this.getSpellAbility().addEffect(new ChainOfVaporEffect()); @@ -77,14 +76,7 @@ public boolean apply(Game game, Ability source) { if (player.chooseUse(outcome, "Copy the spell?", source, game)) { Spell spell = game.getStack().getSpell(source.getSourceId()); if (spell != null) { - StackObject newStackObject = spell.createCopyOnStack(game, source, player.getId(), true); - if (newStackObject instanceof Spell) { - String activateMessage = ((Spell) newStackObject).getActivatedMessage(game); - if (activateMessage.startsWith(" casts ")) { - activateMessage = activateMessage.substring(6); - } - game.informPlayers(player.getLogName() + ' ' + activateMessage); - } + spell.createCopyOnStack(game, source, player.getId(), true); } } } diff --git a/Mage.Sets/src/mage/cards/c/ChainStasis.java b/Mage.Sets/src/mage/cards/c/ChainStasis.java index ebf77a821441..a16edebb9a76 100644 --- a/Mage.Sets/src/mage/cards/c/ChainStasis.java +++ b/Mage.Sets/src/mage/cards/c/ChainStasis.java @@ -1,7 +1,6 @@ package mage.cards.c; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; @@ -17,13 +16,13 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.Spell; -import mage.game.stack.StackObject; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class ChainStasis extends CardImpl { @@ -74,7 +73,7 @@ public boolean apply(Game game, Ability source) { effect.setTargetPointer(new FixedTarget(source.getFirstTarget())); effect.apply(game, source); Player player = game.getPlayer(permanent.getControllerId()); - if(player == null){ + if (player == null) { return false; } Cost cost = new ManaCostsImpl("{2}{U}"); @@ -82,14 +81,7 @@ public boolean apply(Game game, Ability source) { if (player.chooseUse(outcome, "Copy the spell?", source, game)) { Spell spell = game.getStack().getSpell(source.getSourceId()); if (spell != null) { - StackObject newStackObject = spell.createCopyOnStack(game, source, player.getId(), true); - if (newStackObject instanceof Spell) { - String activateMessage = ((Spell) newStackObject).getActivatedMessage(game); - if (activateMessage.startsWith(" casts ")) { - activateMessage = activateMessage.substring(6); - } - game.informPlayers(player.getLogName() + ' ' + activateMessage); - } + spell.createCopyOnStack(game, source, player.getId(), true); } } } diff --git a/Mage.Sets/src/mage/cards/c/ChaliceOfTheVoid.java b/Mage.Sets/src/mage/cards/c/ChaliceOfTheVoid.java index e5086cd9cc01..b18f9649fe62 100644 --- a/Mage.Sets/src/mage/cards/c/ChaliceOfTheVoid.java +++ b/Mage.Sets/src/mage/cards/c/ChaliceOfTheVoid.java @@ -68,7 +68,7 @@ public boolean checkEventType(GameEvent event, Game game) { public boolean checkTrigger(GameEvent event, Game game) { Permanent chalice = game.getPermanent(getSourceId()); Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && chalice != null && spell.getConvertedManaCost() == chalice.getCounters(game).getCount(CounterType.CHARGE)) { + if (spell != null && chalice != null && spell.getManaValue() == chalice.getCounters(game).getCount(CounterType.CHARGE)) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getTargetId())); } @@ -79,6 +79,6 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { - return "Whenever a player casts a spell with converted mana cost equal to the number of charge counters on {this}, counter that spell."; + return "Whenever a player casts a spell with mana value equal to the number of charge counters on {this}, counter that spell."; } } diff --git a/Mage.Sets/src/mage/cards/c/ChampionOfLambholt.java b/Mage.Sets/src/mage/cards/c/ChampionOfLambholt.java index 22b4af1ad5ec..b75014c8a3ec 100644 --- a/Mage.Sets/src/mage/cards/c/ChampionOfLambholt.java +++ b/Mage.Sets/src/mage/cards/c/ChampionOfLambholt.java @@ -11,7 +11,7 @@ import mage.constants.*; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/c/ChampionOfStraySouls.java b/Mage.Sets/src/mage/cards/c/ChampionOfStraySouls.java index 53dea0d3c9bb..be43b08456e5 100644 --- a/Mage.Sets/src/mage/cards/c/ChampionOfStraySouls.java +++ b/Mage.Sets/src/mage/cards/c/ChampionOfStraySouls.java @@ -1,4 +1,3 @@ - package mage.cards.c; import mage.MageInt; @@ -16,9 +15,10 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetadjustment.TargetAdjuster; @@ -51,11 +51,11 @@ public ChampionOfStraySouls(UUID ownerId, CardSetInfo setInfo) { */ // {3}{B}{B}, {T}, Sacrifice X other creatures: Return X target creatures from your graveyard to the battlefield. Effect effect = new ReturnFromGraveyardToBattlefieldTargetEffect(); - effect.setText("Return X target creatures from your graveyard to the battlefield"); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{3}{B}{B}")); + effect.setText("Return X target creature cards from your graveyard to the battlefield"); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl<>("{3}{B}{B}")); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeXTargetCost(filter)); - ability.addTarget(new TargetCardInYourGraveyard(0, Integer.MAX_VALUE, new FilterCreatureCard("creature cards from your graveyard"))); + ability.addTarget(new TargetCardInYourGraveyard(0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); ability.setTargetAdjuster(ChampionOfStraySoulsAdjuster.instance); this.addAbility(ability); @@ -88,4 +88,4 @@ public void adjustTargets(Ability ability, Game game) { } } } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/c/ChampionOfTheParish.java b/Mage.Sets/src/mage/cards/c/ChampionOfTheParish.java index 57d072c493ec..005635f2d921 100644 --- a/Mage.Sets/src/mage/cards/c/ChampionOfTheParish.java +++ b/Mage.Sets/src/mage/cards/c/ChampionOfTheParish.java @@ -12,7 +12,7 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/c/ChandraAcolyteOfFlame.java b/Mage.Sets/src/mage/cards/c/ChandraAcolyteOfFlame.java index 5c87e62c390c..acce249973cb 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraAcolyteOfFlame.java +++ b/Mage.Sets/src/mage/cards/c/ChandraAcolyteOfFlame.java @@ -21,14 +21,13 @@ import mage.filter.common.FilterControlledPlaneswalkerPermanent; import mage.filter.common.FilterInstantOrSorceryCard; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.game.permanent.token.RedElementalToken; import mage.game.permanent.token.Token; -import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; @@ -44,11 +43,11 @@ public final class ChandraAcolyteOfFlame extends CardImpl { private static final FilterPermanent filter = new FilterControlledPlaneswalkerPermanent("red planeswalker you control"); private static final FilterCard filter2 - = new FilterInstantOrSorceryCard("instant or sorcery card with converted mana cost 3 or less"); + = new FilterInstantOrSorceryCard("instant or sorcery card with mana value 3 or less"); static { filter.add(new ColorPredicate(ObjectColor.RED)); - filter2.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter2.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public ChandraAcolyteOfFlame(UUID ownerId, CardSetInfo setInfo) { @@ -129,7 +128,7 @@ class ChandraAcolyteOfFlameGraveyardEffect extends OneShotEffect { ChandraAcolyteOfFlameGraveyardEffect() { super(Benefit); this.staticText = "You may cast target instant or sorcery card " + - "with converted mana cost 3 or less from your graveyard this turn. " + + "with mana value 3 or less from your graveyard this turn. " + "If that card would be put into your graveyard this turn, exile it instead"; } diff --git a/Mage.Sets/src/mage/cards/c/ChandraHeartOfFire.java b/Mage.Sets/src/mage/cards/c/ChandraHeartOfFire.java index 767111168a64..1356bb3de48d 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraHeartOfFire.java +++ b/Mage.Sets/src/mage/cards/c/ChandraHeartOfFire.java @@ -81,7 +81,7 @@ class ChandraHeartOfFireUltimateEffect extends OneShotEffect { ChandraHeartOfFireUltimateEffect() { super(Outcome.Benefit); - staticText = "Search your graveyard and library for any number of red instant and/or sorcery cards, exile them, then shuffle your library. You may cast them this turn"; + staticText = "Search your graveyard and library for any number of red instant and/or sorcery cards, exile them, then shuffle. You may cast them this turn"; } private ChandraHeartOfFireUltimateEffect(ChandraHeartOfFireUltimateEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/ChandraPyromaster.java b/Mage.Sets/src/mage/cards/c/ChandraPyromaster.java index e0ea599dbde0..48f06046a40c 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraPyromaster.java +++ b/Mage.Sets/src/mage/cards/c/ChandraPyromaster.java @@ -231,21 +231,21 @@ public boolean apply(Game game, Ability source) { Card card = cards.get(target.getFirstTarget(), game); if (card != null) { ApprovingObject approvingObject = new ApprovingObject(source, game); - if (controller.chooseUse(outcome, "Do you wish to cast copy 1 of " + card.getName(), source, game)) { + if (controller.chooseUse(outcome, "Cast copy 1 of " + card.getName(), source, game)) { Card copy1 = game.copyCard(card, source, source.getControllerId()); game.getState().setValue("PlayFromNotOwnHandZone" + copy1.getId(), Boolean.TRUE); controller.cast(controller.chooseAbilityForCast(copy1, game, true), game, true, approvingObject); game.getState().setValue("PlayFromNotOwnHandZone" + copy1.getId(), null); } - if (controller.chooseUse(outcome, "Do you wish to cast copy 2 of " + card.getName(), source, game)) { + if (controller.chooseUse(outcome, "Cast copy 2 of " + card.getName(), source, game)) { Card copy2 = game.copyCard(card, source, source.getControllerId()); game.getState().setValue("PlayFromNotOwnHandZone" + copy2.getId(), Boolean.TRUE); controller.cast(controller.chooseAbilityForCast(copy2, game, true), game, true, approvingObject); game.getState().setValue("PlayFromNotOwnHandZone" + copy2.getId(), null); } - if (controller.chooseUse(outcome, "Do you wish to cast copy 3 of " + card.getName(), source, game)) { + if (controller.chooseUse(outcome, "Cast copy 3 of " + card.getName(), source, game)) { Card copy3 = game.copyCard(card, source, source.getControllerId()); game.getState().setValue("PlayFromNotOwnHandZone" + copy3.getId(), Boolean.TRUE); controller.cast(controller.chooseAbilityForCast(copy3, game, true), diff --git a/Mage.Sets/src/mage/cards/c/ChandraTheFirebrand.java b/Mage.Sets/src/mage/cards/c/ChandraTheFirebrand.java index c767841f9450..b384ba898503 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraTheFirebrand.java +++ b/Mage.Sets/src/mage/cards/c/ChandraTheFirebrand.java @@ -86,7 +86,7 @@ public boolean checkEventType(GameEvent event, Game game) { public boolean checkTrigger(GameEvent event, Game game) { if (event.getPlayerId().equals(this.getControllerId())) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && (spell.isInstant() || spell.isSorcery())) { + if (spell != null && spell.isInstantOrSorcery()) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getTargetId())); } diff --git a/Mage.Sets/src/mage/cards/c/ChandrasRegulator.java b/Mage.Sets/src/mage/cards/c/ChandrasRegulator.java index 0546755dae85..418bf743b26f 100644 --- a/Mage.Sets/src/mage/cards/c/ChandrasRegulator.java +++ b/Mage.Sets/src/mage/cards/c/ChandrasRegulator.java @@ -103,7 +103,6 @@ public boolean apply(Game game, Ability source) { return false; } ability.createCopyOnStack(game, source, source.getControllerId(), true); - game.informPlayers(sourcePermanent.getIdName() + ": " + controller.getLogName() + " copied activated ability"); return true; } } diff --git a/Mage.Sets/src/mage/cards/c/ChannelHarm.java b/Mage.Sets/src/mage/cards/c/ChannelHarm.java index d3717f715016..0572080c55b9 100644 --- a/Mage.Sets/src/mage/cards/c/ChannelHarm.java +++ b/Mage.Sets/src/mage/cards/c/ChannelHarm.java @@ -70,7 +70,7 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { if (preventionData.getPreventedDamage() > 0) { Permanent targetCreature = game.getPermanent(source.getFirstTarget()); if (targetCreature != null) { - if (sourceController != null && sourceController.chooseUse(outcome, "Would you like to have " + preventionData.getPreventedDamage() + " damage dealt to " + targetCreature.getLogName() + "?", source, game)) { + if (sourceController != null && sourceController.chooseUse(outcome, "Have " + preventionData.getPreventedDamage() + " damage dealt to " + targetCreature.getLogName() + "?", source, game)) { targetCreature.damage(preventionData.getPreventedDamage(), source.getSourceId(), source, game, false, true); } } diff --git a/Mage.Sets/src/mage/cards/c/ChaosMaw.java b/Mage.Sets/src/mage/cards/c/ChaosMaw.java index c53c0263ea3b..773f13e5f873 100644 --- a/Mage.Sets/src/mage/cards/c/ChaosMaw.java +++ b/Mage.Sets/src/mage/cards/c/ChaosMaw.java @@ -9,7 +9,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; public final class ChaosMaw extends CardImpl { private static FilterCreaturePermanent filter = new FilterCreaturePermanent("other creature"); diff --git a/Mage.Sets/src/mage/cards/c/ChaosMoon.java b/Mage.Sets/src/mage/cards/c/ChaosMoon.java index 79af06b24366..9835bad390c4 100644 --- a/Mage.Sets/src/mage/cards/c/ChaosMoon.java +++ b/Mage.Sets/src/mage/cards/c/ChaosMoon.java @@ -119,7 +119,7 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { Permanent land = game.getPermanentOrLKIBattlefield(event.getTargetId()); - if (land != null && filter.match(land, getSourceId(), getControllerId(), game)) { + if (filter.match(land, getSourceId(), getControllerId(), game)) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(land.getControllerId())); } diff --git a/Mage.Sets/src/mage/cards/c/ChaosWand.java b/Mage.Sets/src/mage/cards/c/ChaosWand.java index 906984011c65..fc6f3a33aad8 100644 --- a/Mage.Sets/src/mage/cards/c/ChaosWand.java +++ b/Mage.Sets/src/mage/cards/c/ChaosWand.java @@ -82,7 +82,7 @@ public boolean apply(Game game, Ability source) { } opponent.moveCards(card, Zone.EXILED, source, game); controller.revealCards(source, new CardsImpl(card), game); - if (card.isInstant() || card.isSorcery()) { + if (card.isInstantOrSorcery()) { boolean cardWasCast = false; if (controller.chooseUse(Outcome.PlayForFree, "Cast " + card.getName() + " without paying its mana cost?", source, game)) { diff --git a/Mage.Sets/src/mage/cards/c/ChaosWarp.java b/Mage.Sets/src/mage/cards/c/ChaosWarp.java index 12252c7d4f66..3d24bfa2ec74 100644 --- a/Mage.Sets/src/mage/cards/c/ChaosWarp.java +++ b/Mage.Sets/src/mage/cards/c/ChaosWarp.java @@ -79,7 +79,7 @@ class ChaosWarpRevealEffect extends OneShotEffect { public ChaosWarpRevealEffect() { super(Outcome.PutCardInPlay); - this.staticText = "then reveals the top card of their library. If it's a permanent card, they put it onto the battlefield"; + this.staticText = ", then reveals the top card of their library. If it's a permanent card, they put it onto the battlefield"; } public ChaosWarpRevealEffect(final ChaosWarpRevealEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/ChargeThrough.java b/Mage.Sets/src/mage/cards/c/ChargeThrough.java new file mode 100644 index 000000000000..7f4dd51fdd1a --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChargeThrough.java @@ -0,0 +1,39 @@ +package mage.cards.c; + +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.DrawCardTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ChargeThrough extends CardImpl { + + public ChargeThrough(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}"); + + // Target creature gains trample until end of turn. + this.getSpellAbility().addEffect(new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + + // Draw a card. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); + } + + private ChargeThrough(final ChargeThrough card) { + super(card); + } + + @Override + public ChargeThrough copy() { + return new ChargeThrough(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CharmbreakerDevils.java b/Mage.Sets/src/mage/cards/c/CharmbreakerDevils.java index 07005990438b..aece380a1650 100644 --- a/Mage.Sets/src/mage/cards/c/CharmbreakerDevils.java +++ b/Mage.Sets/src/mage/cards/c/CharmbreakerDevils.java @@ -1,54 +1,45 @@ - package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.OnEventTriggeredAbility; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.filter.FilterSpell; -import mage.filter.predicate.Predicates; +import mage.constants.*; +import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.events.GameEvent.EventType; import mage.players.Player; -import mage.util.RandomUtil; +import mage.target.TargetCard; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; /** - * * @author North */ public final class CharmbreakerDevils extends CardImpl { - private static final FilterSpell filter = new FilterSpell("instant or sorcery card"); - - static { - filter.add(Predicates.or( - CardType.INSTANT.getPredicate(), - CardType.SORCERY.getPredicate())); - } - public CharmbreakerDevils(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{R}"); this.subtype.add(SubType.DEVIL); this.power = new MageInt(4); this.toughness = new MageInt(4); // At the beginning of your upkeep, return an instant or sorcery card at random from your graveyard to your hand. - this.addAbility(new OnEventTriggeredAbility(EventType.UPKEEP_STEP_PRE, "beginning of your upkeep", new CharmbreakerDevilsEffect(), false)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new CharmbreakerDevilsEffect(), TargetController.YOU, false + )); + // Whenever you cast an instant or sorcery spell, Charmbreaker Devils gets +4/+0 until end of turn. - this.addAbility(new SpellCastControllerTriggeredAbility(new BoostSourceEffect(4, 0, Duration.EndOfTurn), filter, false)); + this.addAbility(new SpellCastControllerTriggeredAbility( + new BoostSourceEffect(4, 0, Duration.EndOfTurn), + StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, false + )); } private CharmbreakerDevils(final CharmbreakerDevils card) { @@ -63,12 +54,12 @@ public CharmbreakerDevils copy() { class CharmbreakerDevilsEffect extends OneShotEffect { - public CharmbreakerDevilsEffect() { + CharmbreakerDevilsEffect() { super(Outcome.ReturnToHand); this.staticText = "return an instant or sorcery card at random from your graveyard to your hand"; } - public CharmbreakerDevilsEffect(final CharmbreakerDevilsEffect effect) { + private CharmbreakerDevilsEffect(final CharmbreakerDevilsEffect effect) { super(effect); } @@ -80,19 +71,14 @@ public CharmbreakerDevilsEffect copy() { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - FilterCard filter = new FilterCard("instant or sorcery card"); - filter.add(Predicates.or( - CardType.INSTANT.getPredicate(), - CardType.SORCERY.getPredicate())); - Card[] cards = player.getGraveyard().getCards(filter, game).toArray(new Card[0]); - if (cards.length > 0) { - Card card = cards[RandomUtil.nextInt(cards.length)]; - card.moveToZone(Zone.HAND, source, game, true); - game.informPlayers("Charmbreaker Devils: " + card.getName() + " returned to the hand of " + player.getLogName()); - return true; - } + if (player == null || player.getGraveyard().count(StaticFilters.FILTER_CARD_INSTANT_AND_SORCERY, game) < 1) { + return false; } - return false; + TargetCard target = new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_INSTANT_AND_SORCERY); + target.setRandom(true); + target.setNotTarget(true); + player.chooseTarget(outcome, target, source, game); + Card card = game.getCard(target.getFirstTarget()); + return card != null && player.moveCards(card, Zone.HAND, source, game); } } diff --git a/Mage.Sets/src/mage/cards/c/CharmedPendant.java b/Mage.Sets/src/mage/cards/c/CharmedPendant.java index 88192e7181f2..087d309d8c9a 100644 --- a/Mage.Sets/src/mage/cards/c/CharmedPendant.java +++ b/Mage.Sets/src/mage/cards/c/CharmedPendant.java @@ -80,7 +80,7 @@ public CharmedPendantAbility copy() { @Override public String getRule() { - return super.getRule() + " Activate this ability only any time you could cast an instant."; + return super.getRule() + " Activate only as an instant."; } } diff --git a/Mage.Sets/src/mage/cards/c/CharmedStray.java b/Mage.Sets/src/mage/cards/c/CharmedStray.java index a684da406344..237bee52f3cf 100644 --- a/Mage.Sets/src/mage/cards/c/CharmedStray.java +++ b/Mage.Sets/src/mage/cards/c/CharmedStray.java @@ -12,7 +12,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.NamePredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/c/CharmingPrince.java b/Mage.Sets/src/mage/cards/c/CharmingPrince.java index 24a21d37b417..da9fde85774e 100644 --- a/Mage.Sets/src/mage/cards/c/CharmingPrince.java +++ b/Mage.Sets/src/mage/cards/c/CharmingPrince.java @@ -16,7 +16,7 @@ import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/c/ChecksAndBalances.java b/Mage.Sets/src/mage/cards/c/ChecksAndBalances.java index 619a9c71b4aa..f6fb669a3a3e 100644 --- a/Mage.Sets/src/mage/cards/c/ChecksAndBalances.java +++ b/Mage.Sets/src/mage/cards/c/ChecksAndBalances.java @@ -12,6 +12,7 @@ import mage.constants.Outcome; import mage.constants.SetTargetPointer; import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.stack.Spell; import mage.players.Player; @@ -31,7 +32,7 @@ public ChecksAndBalances(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new CastOnlyIfConditionIsTrueAbility(ChecksAndBalancesCondition.instance, "Cast this spell only if there are three or more players in the game")); // Whenever a player casts a spell, each of that player’s opponents may discard a card. If they do, counter that spell. - this.addAbility(new SpellCastAllTriggeredAbility(new ChecksAndBalancesEffect(), new FilterSpell("a spell"), false, SetTargetPointer.SPELL)); + this.addAbility(new SpellCastAllTriggeredAbility(new ChecksAndBalancesEffect(), StaticFilters.FILTER_SPELL_A, false, SetTargetPointer.SPELL)); } private ChecksAndBalances(final ChecksAndBalances card) { @@ -90,7 +91,7 @@ public boolean apply(Game game, Ability source) { for (UUID uuid : game.getOpponents(spell.getControllerId())) { Player player = game.getPlayer(uuid); if (player != null) { - if (!player.chooseUse(outcome, "Do you wish to discard a card to counter " + spell.getLogName() + '?', source, game)) { + if (!player.chooseUse(outcome, "Discard a card to counter " + spell.getLogName() + '?', source, game)) { game.informPlayers(player.getLogName() + " refuses to discard a card to counter " + spell.getLogName()); return true; } else { diff --git a/Mage.Sets/src/mage/cards/c/CherishedHatchling.java b/Mage.Sets/src/mage/cards/c/CherishedHatchling.java index cc72a885507f..8babe2d93c8b 100644 --- a/Mage.Sets/src/mage/cards/c/CherishedHatchling.java +++ b/Mage.Sets/src/mage/cards/c/CherishedHatchling.java @@ -17,7 +17,7 @@ import mage.constants.SubType; import mage.filter.FilterCard; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; diff --git a/Mage.Sets/src/mage/cards/c/Chewbacca.java b/Mage.Sets/src/mage/cards/c/Chewbacca.java index 166a0c6ddc57..0a7b260d4fcb 100644 --- a/Mage.Sets/src/mage/cards/c/Chewbacca.java +++ b/Mage.Sets/src/mage/cards/c/Chewbacca.java @@ -14,7 +14,7 @@ import mage.constants.Duration; import mage.constants.SuperType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/c/ChewbaccaTheBeast.java b/Mage.Sets/src/mage/cards/c/ChewbaccaTheBeast.java index e58b771fe76a..117a730c9103 100644 --- a/Mage.Sets/src/mage/cards/c/ChewbaccaTheBeast.java +++ b/Mage.Sets/src/mage/cards/c/ChewbaccaTheBeast.java @@ -11,7 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.AttackingPredicate; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/c/ChiefChirpa.java b/Mage.Sets/src/mage/cards/c/ChiefChirpa.java index 7f9096482d03..0892f3c1e36f 100644 --- a/Mage.Sets/src/mage/cards/c/ChiefChirpa.java +++ b/Mage.Sets/src/mage/cards/c/ChiefChirpa.java @@ -20,7 +20,7 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.EwokToken; import mage.target.common.TargetControlledCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/c/ChiefOfTheFoundry.java b/Mage.Sets/src/mage/cards/c/ChiefOfTheFoundry.java index 364f78eb0de8..d9bbd798c8a1 100644 --- a/Mage.Sets/src/mage/cards/c/ChiefOfTheFoundry.java +++ b/Mage.Sets/src/mage/cards/c/ChiefOfTheFoundry.java @@ -1,32 +1,22 @@ - package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author fireshoes */ public final class ChiefOfTheFoundry extends CardImpl { - private static final FilterCreaturePermanent filterBoosted = new FilterCreaturePermanent("Other artifact creatures you control"); - - static { - filterBoosted.add(CardType.ARTIFACT.getPredicate()); - filterBoosted.add(TargetController.YOU.getControllerPredicate()); - } - public ChiefOfTheFoundry(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); this.subtype.add(SubType.CONSTRUCT); @@ -34,7 +24,10 @@ public ChiefOfTheFoundry(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(3); // Other artifact creatures you control get +1/+1. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filterBoosted, true))); + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 1, 1, Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENTS_ARTIFACT_CREATURE, true + ))); } private ChiefOfTheFoundry(final ChiefOfTheFoundry card) { diff --git a/Mage.Sets/src/mage/cards/c/ChoiceOfDamnations.java b/Mage.Sets/src/mage/cards/c/ChoiceOfDamnations.java index 4ab2e64b8df9..ae860b899dad 100644 --- a/Mage.Sets/src/mage/cards/c/ChoiceOfDamnations.java +++ b/Mage.Sets/src/mage/cards/c/ChoiceOfDamnations.java @@ -66,7 +66,7 @@ public boolean apply(Game game, Ability source) { // AI hint int amount; - if (!targetPlayer.isHuman() && !targetPlayer.isTestMode()) { + if (targetPlayer.isComputer()) { // AI as defender int safeLifeToLost = Math.max(0, targetPlayer.getLife() / 2); amount = Math.min(numberPermanents, safeLifeToLost); @@ -80,7 +80,7 @@ public boolean apply(Game game, Ability source) { // AI hint boolean chooseLoseLife; - if (!targetPlayer.isHuman() && !targetPlayer.isTestMode()) { + if (targetPlayer.isComputer()) { // AI as attacker chooseLoseLife = (numberPermanents == 0 || amount <= numberPermanents || targetPlayer.getLife() < amount); } else { diff --git a/Mage.Sets/src/mage/cards/c/ChorusOfTheConclave.java b/Mage.Sets/src/mage/cards/c/ChorusOfTheConclave.java index 712be1e5b35e..767bb402a595 100644 --- a/Mage.Sets/src/mage/cards/c/ChorusOfTheConclave.java +++ b/Mage.Sets/src/mage/cards/c/ChorusOfTheConclave.java @@ -90,7 +90,7 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { int xCost = 0; Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - if (controller.chooseUse(Outcome.Benefit, "Do you wish to pay the additonal cost to add +1/+1 counters to the creature you cast?", source, game)) { + if (controller.chooseUse(Outcome.Benefit, "Pay the additonal cost to add +1/+1 counters to the creature you cast?", source, game)) { xCost += ManaUtil.playerPaysXGenericMana(false, "Chorus of the Conclave", controller, source, game); // save the x value to be available for ETB replacement effect Object object = game.getState().getValue("spellX" + source.getSourceId()); diff --git a/Mage.Sets/src/mage/cards/c/ChronatogTotem.java b/Mage.Sets/src/mage/cards/c/ChronatogTotem.java index 63194e7d22ac..c43f6848e1d5 100644 --- a/Mage.Sets/src/mage/cards/c/ChronatogTotem.java +++ b/Mage.Sets/src/mage/cards/c/ChronatogTotem.java @@ -1,16 +1,11 @@ - package mage.cards.c; -import java.util.UUID; - import mage.abilities.Ability; import mage.abilities.common.LimitedTimesPerTurnActivatedAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.condition.Condition; -import mage.abilities.costs.Cost; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; -import mage.abilities.effects.Effects; import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.effects.common.turn.SkipNextTurnSourceEffect; @@ -18,14 +13,15 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.EffectType; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.custom.CreatureToken; +import java.util.UUID; + /** * @author emerald000 */ @@ -38,19 +34,21 @@ public ChronatogTotem(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new BlueManaAbility()); // {1}{U}: Chronatog Totem becomes a 1/2 blue Atog artifact creature until end of turn. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect( + this.addAbility(new SimpleActivatedAbility(new BecomesCreatureSourceEffect( new CreatureToken(1, 2, "1/2 blue Atog artifact creature") .withColor("U") .withSubType(SubType.ATOG) .withType(CardType.ARTIFACT), - "", Duration.EndOfTurn), new ManaCostsImpl<>("{1}{U}"))); + "", Duration.EndOfTurn + ), new ManaCostsImpl<>("{1}{U}"))); // {0}: Chronatog Totem gets +3/+3 until end of turn. You skip your next turn. Activate this ability only once each turn and only if Chronatog Totem is a creature. - Ability ability = new ChronatogTotemAbility( + Ability ability = new LimitedTimesPerTurnActivatedAbility( Zone.BATTLEFIELD, new BoostSourceEffect(3, 3, Duration.EndOfTurn), - new ManaCostsImpl<>("{0}"), - new ChronatogTotemCondition()); + new GenericManaCost(0), 1, + ChronatogTotemCondition.instance + ); ability.addEffect(new SkipNextTurnSourceEffect()); this.addAbility(ability); } @@ -65,42 +63,8 @@ public ChronatogTotem copy() { } } -class ChronatogTotemAbility extends LimitedTimesPerTurnActivatedAbility { - - private static final Effects emptyEffects = new Effects(); - - public ChronatogTotemAbility(Zone zone, Effect effect, Cost cost, Condition condition) { - super(zone, effect, cost); - this.condition = condition; - } - - public ChronatogTotemAbility(ChronatogTotemAbility ability) { - super(ability); - } - - @Override - public Effects getEffects(Game game, EffectType effectType) { - if (!condition.apply(game, this)) { - return emptyEffects; - } - return super.getEffects(game, effectType); - } - - @Override - public ChronatogTotemAbility copy() { - return new ChronatogTotemAbility(this); - } - - @Override - public String getRule() { - StringBuilder sb = new StringBuilder(super.getRule()); - sb.deleteCharAt(sb.length() - 1); // remove last '.' - sb.append(" and only if ").append(condition.toString()).append('.'); - return sb.toString(); - } -} - -class ChronatogTotemCondition implements Condition { +enum ChronatogTotemCondition implements Condition { + instance; @Override public boolean apply(Game game, Ability source) { diff --git a/Mage.Sets/src/mage/cards/c/ChronomanticEscape.java b/Mage.Sets/src/mage/cards/c/ChronomanticEscape.java index 9bef667ce3a8..a6057b39bc5c 100644 --- a/Mage.Sets/src/mage/cards/c/ChronomanticEscape.java +++ b/Mage.Sets/src/mage/cards/c/ChronomanticEscape.java @@ -28,7 +28,7 @@ public ChronomanticEscape(UUID ownerId, CardSetInfo setInfo) { // Until your next turn, creatures can't attack you. Exile Chronomantic Escape with three time counters on it. getSpellAbility().addEffect(new CantAttackYouAllEffect(Duration.UntilYourNextTurn, StaticFilters.FILTER_PERMANENT_CREATURES)); - getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + getSpellAbility().addEffect(new ExileSpellEffect()); Effect effect = new AddCountersSourceEffect(CounterType.TIME.createInstance(), StaticValue.get(3), true, true); effect.setText("with 3 time counters on it"); getSpellAbility().addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/c/CinderGiant.java b/Mage.Sets/src/mage/cards/c/CinderGiant.java index 64f04cd4a506..7069f741c17f 100644 --- a/Mage.Sets/src/mage/cards/c/CinderGiant.java +++ b/Mage.Sets/src/mage/cards/c/CinderGiant.java @@ -11,7 +11,7 @@ import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/c/CinderheartGiant.java b/Mage.Sets/src/mage/cards/c/CinderheartGiant.java index b9c8710b74c1..262149ea57d5 100644 --- a/Mage.Sets/src/mage/cards/c/CinderheartGiant.java +++ b/Mage.Sets/src/mage/cards/c/CinderheartGiant.java @@ -3,16 +3,19 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DiesSourceTriggeredAbility; -import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.OneShotEffect; 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.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; -import mage.target.Target; -import mage.target.common.TargetCreaturePermanent; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetOpponentsCreaturePermanent; import java.util.UUID; @@ -21,12 +24,6 @@ */ public final class CinderheartGiant extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public CinderheartGiant(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{R}{R}"); @@ -39,13 +36,7 @@ public CinderheartGiant(UUID ownerId, CardSetInfo setInfo) { this.addAbility(TrampleAbility.getInstance()); // When Cinderheart Giant dies, it deals 7 damage to a creature an opponent controls chosen at random. - Ability ability = new DiesSourceTriggeredAbility( - new DamageTargetEffect(7).setText("it deals 7 damage to a creature an opponent controls chosen at random")); - Target target = new TargetCreaturePermanent(1, 1, filter, true); - target.setRandom(true); - ability.addTarget(target); - - this.addAbility(ability); + this.addAbility(new DiesSourceTriggeredAbility(new CinderheartGiantEffect())); } private CinderheartGiant(final CinderheartGiant card) { @@ -57,3 +48,37 @@ public CinderheartGiant copy() { return new CinderheartGiant(this); } } + +class CinderheartGiantEffect extends OneShotEffect { + + CinderheartGiantEffect() { + super(Outcome.Benefit); + staticText = "it deals 7 damage to a creature an opponent controls chosen at random"; + } + + private CinderheartGiantEffect(final CinderheartGiantEffect effect) { + super(effect); + } + + @Override + public CinderheartGiantEffect copy() { + return new CinderheartGiantEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null || game.getBattlefield().count( + StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE, + source.getSourceId(), source.getControllerId(), game + ) < 1) { + return false; + } + TargetPermanent target = new TargetOpponentsCreaturePermanent(); + target.setNotTarget(true); + target.setRandom(true); + player.chooseTarget(outcome, target, source, game); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + return permanent != null && permanent.damage(7, source.getSourceId(), source, game) > 0; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CitanulFlute.java b/Mage.Sets/src/mage/cards/c/CitanulFlute.java index 739e7a47d9e6..fe4fa883753f 100644 --- a/Mage.Sets/src/mage/cards/c/CitanulFlute.java +++ b/Mage.Sets/src/mage/cards/c/CitanulFlute.java @@ -1,35 +1,38 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.ComparisonType; import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; +import java.util.UUID; + /** - * * @author Backfir3 */ public final class CitanulFlute extends CardImpl { public CitanulFlute(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{5}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{5}"); // {X}, {T}: Search your library for a creature card with converted mana cost X or less, reveal it, // and put it into your hand. Then shuffle your library. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CitanulFluteSearchEffect(), new ManaCostsImpl("{X}")); + Ability ability = new SimpleActivatedAbility(new CitanulFluteSearchEffect(), new ManaCostsImpl("{X}")); ability.addCost(new TapSourceCost()); this.addAbility(ability); } @@ -46,12 +49,13 @@ public CitanulFlute copy() { class CitanulFluteSearchEffect extends OneShotEffect { - public CitanulFluteSearchEffect() { + CitanulFluteSearchEffect() { super(Outcome.DrawCard); - staticText = "Search your library for a creature card with converted mana cost X or less, reveal it, and put it into your hand. Then shuffle your library"; + staticText = "Search your library for a creature card with mana value X or less, " + + "reveal it, and put it into your hand. Then shuffle"; } - public CitanulFluteSearchEffect(final CitanulFluteSearchEffect effect) { + private CitanulFluteSearchEffect(final CitanulFluteSearchEffect effect) { super(effect); } @@ -66,32 +70,19 @@ public boolean apply(Game game, Ability source) { if (player == null) { return false; } - - FilterCard filter = new FilterCard("creature card with converted mana cost X or less"); - filter.add(CardType.CREATURE.getPredicate()); + + FilterCard filter = new FilterCreatureCard("creature card with mana value X or less"); //Set the mana cost one higher to 'emulate' a less than or equal to comparison. - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, source.getManaCostsToPay().getX() + 1)); - - TargetCardInLibrary target = new TargetCardInLibrary(filter); - if (player.searchLibrary(target, source, game)) { - if (!target.getTargets().isEmpty()) { - Card card = player.getLibrary().getCard(target.getFirstTarget(), game); - Cards cards = new CardsImpl(); - if (card != null){ - card.moveToZone(Zone.HAND, source, game, false); - cards.add(card); - } - String name = "Reveal"; - Card sourceCard = game.getCard(source.getSourceId()); - if (sourceCard != null) { - name = sourceCard.getName(); - } - player.revealCards(name, cards, game); - } - player.shuffleLibrary(source, game); - return true; + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, source.getManaCostsToPay().getX() + 1)); + + TargetCardInLibrary target = new TargetCardInLibrary(filter); + player.searchLibrary(target, source, game); + Card card = player.getLibrary().getCard(target.getFirstTarget(), game); + if (card != null) { + player.revealCards(source, new CardsImpl(card), game); + player.moveCards(card, Zone.HAND, source, game); } - player.shuffleLibrary(source, game); - return false; + player.shuffleLibrary(source, game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/c/CityInABottle.java b/Mage.Sets/src/mage/cards/c/CityInABottle.java index 0e8886203945..10fdf2cf6102 100644 --- a/Mage.Sets/src/mage/cards/c/CityInABottle.java +++ b/Mage.Sets/src/mage/cards/c/CityInABottle.java @@ -239,6 +239,6 @@ public boolean checksEventType(GameEvent event, Game game) { @Override public boolean applies(GameEvent event, Ability source, Game game) { Card card = game.getCard(event.getSourceId()); - return card != null && filter.match(card, source.getSourceId(), source.getControllerId(), game); + return filter.match(card, source.getSourceId(), source.getControllerId(), game); } } diff --git a/Mage.Sets/src/mage/cards/c/CivicWayfinder.java b/Mage.Sets/src/mage/cards/c/CivicWayfinder.java index c11434c53517..8c706e8ff7e4 100644 --- a/Mage.Sets/src/mage/cards/c/CivicWayfinder.java +++ b/Mage.Sets/src/mage/cards/c/CivicWayfinder.java @@ -29,7 +29,7 @@ public CivicWayfinder(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(2); // When Civic Wayfinder enters the battlefield, you may search your library for a basic land card, reveal it, and put it into your hand. If you do, shuffle your library. - this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true, false), true)); + this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true), true)); } private CivicWayfinder(final CivicWayfinder card) { diff --git a/Mage.Sets/src/mage/cards/c/ClaimFame.java b/Mage.Sets/src/mage/cards/c/ClaimFame.java index db7768b88560..fcd9a4c36aab 100644 --- a/Mage.Sets/src/mage/cards/c/ClaimFame.java +++ b/Mage.Sets/src/mage/cards/c/ClaimFame.java @@ -15,7 +15,7 @@ import mage.constants.SpellAbilityType; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetCreaturePermanent; @@ -25,10 +25,10 @@ */ public final class ClaimFame extends SplitCard { - private static final FilterCard filter = new FilterCreatureCard("creature card with converted mana cost 2 or less from your graveyard"); + private static final FilterCard filter = new FilterCreatureCard("creature card with mana value 2 or less from your graveyard"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); } public ClaimFame(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/c/ClaimTheFirstborn.java b/Mage.Sets/src/mage/cards/c/ClaimTheFirstborn.java index 8d328a64278f..a84d62c24d64 100644 --- a/Mage.Sets/src/mage/cards/c/ClaimTheFirstborn.java +++ b/Mage.Sets/src/mage/cards/c/ClaimTheFirstborn.java @@ -11,7 +11,7 @@ import mage.constants.Duration; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetPermanent; import java.util.UUID; @@ -22,10 +22,10 @@ public final class ClaimTheFirstborn extends CardImpl { private static final FilterPermanent filter - = new FilterCreaturePermanent("creature with converted mana cost 3 or less"); + = new FilterCreaturePermanent("creature with mana value 3 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public ClaimTheFirstborn(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/c/ClarionUltimatum.java b/Mage.Sets/src/mage/cards/c/ClarionUltimatum.java index 7519fadcd4c1..40b21884879a 100644 --- a/Mage.Sets/src/mage/cards/c/ClarionUltimatum.java +++ b/Mage.Sets/src/mage/cards/c/ClarionUltimatum.java @@ -48,7 +48,7 @@ public ClarionUltimatumEffect() { super(Outcome.PutCreatureInPlay); this.staticText = "Choose five permanents you control. For each of those permanents, " + "you may search your library for a card with the same name as that permanent. " + - "Put those cards onto the battlefield tapped, then shuffle your library"; + "Put those cards onto the battlefield tapped, then shuffle"; } public ClarionUltimatumEffect(final ClarionUltimatumEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/ClashOfTitans.java b/Mage.Sets/src/mage/cards/c/ClashOfTitans.java index 3c2b45967ef0..616d399b27ff 100644 --- a/Mage.Sets/src/mage/cards/c/ClashOfTitans.java +++ b/Mage.Sets/src/mage/cards/c/ClashOfTitans.java @@ -5,7 +5,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/c/CleansingWildfire.java b/Mage.Sets/src/mage/cards/c/CleansingWildfire.java index 1d2545a60a92..c42493fa97e1 100644 --- a/Mage.Sets/src/mage/cards/c/CleansingWildfire.java +++ b/Mage.Sets/src/mage/cards/c/CleansingWildfire.java @@ -33,7 +33,7 @@ public CleansingWildfire(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addTarget(new TargetLandPermanent()); // Draw a card. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); } private CleansingWildfire(final CleansingWildfire card) { @@ -51,7 +51,7 @@ class CleansingWildfireEffect extends OneShotEffect { CleansingWildfireEffect() { super(Outcome.Benefit); staticText = "Its controller may search their library for a basic land card, " + - "put it onto the battlefield tapped, then shuffle their library."; + "put it onto the battlefield tapped, then shuffle."; } private CleansingWildfireEffect(final CleansingWildfireEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/ClericOfLifesBond.java b/Mage.Sets/src/mage/cards/c/ClericOfLifesBond.java index 3e084e9d2a15..f79903978fb3 100644 --- a/Mage.Sets/src/mage/cards/c/ClericOfLifesBond.java +++ b/Mage.Sets/src/mage/cards/c/ClericOfLifesBond.java @@ -12,7 +12,7 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/c/CleverLumimancer.java b/Mage.Sets/src/mage/cards/c/CleverLumimancer.java new file mode 100644 index 000000000000..b1823372f68d --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CleverLumimancer.java @@ -0,0 +1,39 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CleverLumimancer extends CardImpl { + + public CleverLumimancer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(0); + this.toughness = new MageInt(1); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, Clever Lumimancer gets +2/+2 until end of turn. + this.addAbility(new MagecraftAbility(new BoostSourceEffect(2, 2, Duration.EndOfTurn))); + } + + private CleverLumimancer(final CleverLumimancer card) { + super(card); + } + + @Override + public CleverLumimancer copy() { + return new CleverLumimancer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CloakOfConfusion.java b/Mage.Sets/src/mage/cards/c/CloakOfConfusion.java index 7d60886faa43..65694193d228 100644 --- a/Mage.Sets/src/mage/cards/c/CloakOfConfusion.java +++ b/Mage.Sets/src/mage/cards/c/CloakOfConfusion.java @@ -2,21 +2,18 @@ import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.discard.DiscardTargetEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; -import mage.game.combat.CombatGroup; import mage.game.events.DamageEvent; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.targetpointer.FixedTarget; @@ -43,7 +40,6 @@ public CloakOfConfusion(UUID ownerId, CardSetInfo setInfo) { // Whenever enchanted creature attacks and isn't blocked, you may have it assign no combat damage this turn. // If you do, defending player discards a card at random. this.addAbility(new CloakOfConfusionTriggeredAbility()); - } private CloakOfConfusion(final CloakOfConfusion card) { @@ -58,11 +54,12 @@ public CloakOfConfusion copy() { class CloakOfConfusionTriggeredAbility extends TriggeredAbilityImpl { - public CloakOfConfusionTriggeredAbility() { - super(Zone.BATTLEFIELD, new CloakOfConfusionEffect(), true); + CloakOfConfusionTriggeredAbility() { + super(Zone.BATTLEFIELD, new DiscardTargetEffect(1, true), false); + this.addEffect(new CloakOfConfusionEffect()); } - public CloakOfConfusionTriggeredAbility(final CloakOfConfusionTriggeredAbility ability) { + private CloakOfConfusionTriggeredAbility(final CloakOfConfusionTriggeredAbility ability) { super(ability); } @@ -73,45 +70,34 @@ public CloakOfConfusionTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARE_BLOCKERS_STEP; + return event.getType() == EventType.UNBLOCKED_ATTACKER; } @Override public boolean checkTrigger(GameEvent event, Game game) { - Permanent aura = game.getPermanentOrLKIBattlefield(getSourceId()); - if (aura != null) { - Permanent enchantedCreature = game.getPermanent(aura.getAttachedTo()); - if (enchantedCreature != null - && enchantedCreature.isAttacking()) { - for (CombatGroup combatGroup : game.getCombat().getGroups()) { - if (combatGroup.getBlockers().isEmpty() - && combatGroup.getAttackers().contains(enchantedCreature.getId())) { - this.getEffects().setTargetPointer( - new FixedTarget(game.getCombat().getDefendingPlayerId( - enchantedCreature.getId(), game))); - return true; - } - } - } + Permanent aura = getSourcePermanentOrLKI(game); + if (aura != null && event.getTargetId().equals(aura.getAttachedTo())) { + this.getEffects().setTargetPointer(new FixedTarget(game.getCombat().getDefendingPlayerId(aura.getAttachedTo(), game))); + return true; } return false; } @Override public String getRule() { - return "Whenever enchanted creature attacks and isn't blocked, " + super.getRule(); + return "Whenever enchanted creature attacks and isn't blocked, " + + "you may have it assign no combat damage this turn. " + + "If you do, defending player discards a card at random"; } } -class CloakOfConfusionEffect extends OneShotEffect { +class CloakOfConfusionEffect extends ReplacementEffectImpl { - public CloakOfConfusionEffect() { - super(Outcome.Neutral); - this.staticText = "you may have it assign no combat damage this turn. " - + "If you do, defending player discards a card at random"; + CloakOfConfusionEffect() { + super(Duration.EndOfTurn, Outcome.Neutral); } - public CloakOfConfusionEffect(final CloakOfConfusionEffect effect) { + private CloakOfConfusionEffect(final CloakOfConfusionEffect effect) { super(effect); } @@ -120,41 +106,6 @@ public CloakOfConfusionEffect copy() { return new CloakOfConfusionEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent enchantedCreature = game.getPermanent(game.getPermanent(source.getSourceId()).getAttachedTo()); - if (controller != null && controller.chooseUse(outcome, "Do you wish to not assign combat damage from " - + enchantedCreature.getName() + " and have the defending player discard a card at random?", source, game)) { - ContinuousEffect effect = new AssignNoCombatDamageTargetEffect(); - effect.setTargetPointer(new FixedTarget(enchantedCreature.getId())); - game.addEffect(effect, source); - Player defendingPlayer = game.getPlayer(targetPointer.getFirst(game, source)); - if (defendingPlayer != null) { - defendingPlayer.discard(1, true, false, source, game); - } - return true; - } - - return false; - } -} - -class AssignNoCombatDamageTargetEffect extends ReplacementEffectImpl { - - public AssignNoCombatDamageTargetEffect() { - super(Duration.EndOfTurn, Outcome.Neutral); - } - - public AssignNoCombatDamageTargetEffect(final AssignNoCombatDamageTargetEffect effect) { - super(effect); - } - - @Override - public AssignNoCombatDamageTargetEffect copy() { - return new AssignNoCombatDamageTargetEffect(this); - } - @Override public boolean apply(Game game, Ability source) { return true; @@ -168,9 +119,8 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { @Override public boolean checksEventType(GameEvent event, Game game) { switch (event.getType()) { - case DAMAGE_CREATURE: + case DAMAGE_PERMANENT: case DAMAGE_PLAYER: - case DAMAGE_PLANESWALKER: return true; default: return false; @@ -179,8 +129,10 @@ public boolean checksEventType(GameEvent event, Game game) { @Override public boolean applies(GameEvent event, Ability source, Game game) { - DamageEvent damageEvent = (DamageEvent) event; - return event.getSourceId().equals(targetPointer.getFirst(game, source)) - && damageEvent.isCombatDamage(); + if (!((DamageEvent) event).isCombatDamage()) { + return false; + } + Permanent aura = source.getSourcePermanentOrLKI(game); + return aura != null && event.getSourceId().equals(aura.getAttachedTo()); } } diff --git a/Mage.Sets/src/mage/cards/c/ClosingStatement.java b/Mage.Sets/src/mage/cards/c/ClosingStatement.java new file mode 100644 index 000000000000..83c29a92f1de --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ClosingStatement.java @@ -0,0 +1,97 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.IsPhaseCondition; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.ConditionHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetCreatureOrPlaneswalker; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class ClosingStatement extends CardImpl { + + private static final FilterCreatureOrPlaneswalkerPermanent filter = new FilterCreatureOrPlaneswalkerPermanent("creature or planeswalker you don't control"); + + static { + filter.add(TargetController.NOT_YOU.getControllerPredicate()); + } + + public ClosingStatement(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{W}{B}"); + + // This spell costs {2} less to cast during your end step. + IsPhaseCondition condition = new IsPhaseCondition(TurnPhase.END, true); + SimpleStaticAbility ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(2, condition).setText("this spell costs {2} less to cast during your end step")); + ability.addHint(new ConditionHint(condition, "On your end step")); + ability.setRuleAtTheTop(true); + this.addAbility(ability); + + // Destroy target creature or planeswalker you don't control. Put a +1/+1 counter on up to one target creature you control. + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker(1, 1, filter, false)); + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + Target target = new TargetControlledCreaturePermanent(0, 1); + target.setTargetTag(2); + this.getSpellAbility().addTarget(target); + this.getSpellAbility().addEffect(new ClosingStatementEffect()); + } + + private ClosingStatement(final ClosingStatement card) { + super(card); + } + + @Override + public ClosingStatement copy() { + return new ClosingStatement(this); + } +} + +class ClosingStatementEffect extends OneShotEffect { + + ClosingStatementEffect() { + super(Outcome.Benefit); + staticText = "put a +1/+1 counter on up to one target creature you control"; + } + + private ClosingStatementEffect(final ClosingStatementEffect effect) { + super(effect); + } + + @Override + public ClosingStatementEffect copy() { + return new ClosingStatementEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Target target = source.getTargets().stream() + .filter(t -> t.getTargetTag() == 2) + .findFirst() + .orElseThrow(() -> new IllegalStateException("Expected to find target with tag 2 but none exists")); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent != null) { + return permanent.addCounters(CounterType.P1P1.createInstance(), source.getControllerId(), source, game); + } + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/c/CloudhoofKirin.java b/Mage.Sets/src/mage/cards/c/CloudhoofKirin.java index ea015a23d3c4..b3fcff2f4e2d 100644 --- a/Mage.Sets/src/mage/cards/c/CloudhoofKirin.java +++ b/Mage.Sets/src/mage/cards/c/CloudhoofKirin.java @@ -54,7 +54,7 @@ class CloudhoofKirinEffect extends OneShotEffect { CloudhoofKirinEffect() { super(Outcome.Detriment); - this.staticText = "have target player mill X cards, where X is that spell's converted mana cost"; + this.staticText = "target player mill X cards, where X is that spell's mana value"; } private CloudhoofKirinEffect(final CloudhoofKirinEffect effect) { @@ -76,7 +76,7 @@ public boolean apply(Game game, Ability source) { targetPlayer = game.getPlayer(target.getFirstTarget()); } } - int cmc = spell.getConvertedManaCost(); + int cmc = spell.getManaValue(); if (targetPlayer != null && cmc > 0) { targetPlayer.millCards(cmc, source, game); return true; diff --git a/Mage.Sets/src/mage/cards/c/CloudshredderSliver.java b/Mage.Sets/src/mage/cards/c/CloudshredderSliver.java index a0a1e6f56e95..15a194d3a3aa 100644 --- a/Mage.Sets/src/mage/cards/c/CloudshredderSliver.java +++ b/Mage.Sets/src/mage/cards/c/CloudshredderSliver.java @@ -31,7 +31,7 @@ public CloudshredderSliver(UUID ownerId, CardSetInfo setInfo) { Ability ability = new SimpleStaticAbility(new GainAbilityControlledEffect( FlyingAbility.getInstance(), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS - )); + ).setText("Sliver creatures you control have flying")); ability.addEffect(new GainAbilityControlledEffect( HasteAbility.getInstance(), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS diff --git a/Mage.Sets/src/mage/cards/c/CoastalPiracy.java b/Mage.Sets/src/mage/cards/c/CoastalPiracy.java index 86aca22b8641..eb0c2aaf5d0b 100644 --- a/Mage.Sets/src/mage/cards/c/CoastalPiracy.java +++ b/Mage.Sets/src/mage/cards/c/CoastalPiracy.java @@ -1,30 +1,31 @@ - package mage.cards.c; -import java.util.UUID; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; +import mage.constants.TargetController; import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.DamagedPlayerEvent; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author Xavierv3131 */ public final class CoastalPiracy extends CardImpl { public CoastalPiracy(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}{U}"); // Whenever a creature you control deals combat damage to an opponent, you may draw a card. - this.addAbility(new CoastalPiracyTriggeredAbility()); + this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility( + Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), + StaticFilters.FILTER_CONTROLLED_CREATURE, true, SetTargetPointer.PLAYER, + true, true, TargetController.OPPONENT + )); } private CoastalPiracy(final CoastalPiracy card) { @@ -36,43 +37,3 @@ public CoastalPiracy copy() { return new CoastalPiracy(this); } } - -class CoastalPiracyTriggeredAbility extends TriggeredAbilityImpl { - - public CoastalPiracyTriggeredAbility() { - super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1)); - this.optional = true; - } - - public CoastalPiracyTriggeredAbility(final CoastalPiracyTriggeredAbility ability) { - super(ability); - } - - @Override - public CoastalPiracyTriggeredAbility copy() { - return new CoastalPiracyTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (((DamagedPlayerEvent) event).isCombatDamage() - && game.getOpponents(this.controllerId).contains(event.getPlayerId())) { - Permanent creature = game.getPermanent(event.getSourceId()); - if (creature != null && creature.isControlledBy(controllerId)) { - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever a creature you control deals combat damage to an opponent, you may draw a card."; - } - -} diff --git a/Mage.Sets/src/mage/cards/c/CoastalWizard.java b/Mage.Sets/src/mage/cards/c/CoastalWizard.java index 69e085db3e9b..b476e1b3b5d8 100644 --- a/Mage.Sets/src/mage/cards/c/CoastalWizard.java +++ b/Mage.Sets/src/mage/cards/c/CoastalWizard.java @@ -16,7 +16,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/c/CodieVociferousCodex.java b/Mage.Sets/src/mage/cards/c/CodieVociferousCodex.java new file mode 100644 index 000000000000..52b6aadb06d1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CodieVociferousCodex.java @@ -0,0 +1,176 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.*; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CodieVociferousCodex extends CardImpl { + + public CodieVociferousCodex(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.CONSTRUCT); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // You can't cast permanent spells. + this.addAbility(new SimpleStaticAbility(new CodieVociferousCodexCantCastEffect())); + + // {4}, {T}: Add {W}{U}{B}{R}{G}. When you cast your next spell this turn, exile cards from the top of your library until you exile an instant or sorcery card with lesser mana value. Until end of turn, you may cast that card without paying its mana cost. Put each other card exiled this way on the bottom of your library in a random order. + Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, new Mana( + 1, 1, 1, 1, 1, 0, 0, 0 + ), new GenericManaCost(4)); + ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new CodieVociferousCodexDelayedTriggeredAbility())); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private CodieVociferousCodex(final CodieVociferousCodex card) { + super(card); + } + + @Override + public CodieVociferousCodex copy() { + return new CodieVociferousCodex(this); + } +} + +class CodieVociferousCodexCantCastEffect extends ContinuousRuleModifyingEffectImpl { + + CodieVociferousCodexCantCastEffect() { + super(Duration.WhileOnBattlefield, Outcome.Detriment); + staticText = "you can't cast permanent spells"; + } + + private CodieVociferousCodexCantCastEffect(final CodieVociferousCodexCantCastEffect effect) { + super(effect); + } + + @Override + public CodieVociferousCodexCantCastEffect copy() { + return new CodieVociferousCodexCantCastEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.CAST_SPELL; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (!source.isControlledBy(event.getPlayerId())) { + return false; + } + Card card = game.getCard(event.getSourceId()); + return card != null && card.isPermanent(); + } +} + +class CodieVociferousCodexDelayedTriggeredAbility extends DelayedTriggeredAbility { + + CodieVociferousCodexDelayedTriggeredAbility() { + super(new CodieVociferousCodexEffect(), Duration.EndOfTurn, true, false); + } + + private CodieVociferousCodexDelayedTriggeredAbility(final CodieVociferousCodexDelayedTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!isControlledBy(event.getPlayerId())) { + return false; + } + Spell spell = game.getStack().getSpell(event.getTargetId()); + if (spell == null) { + return false; + } + this.getEffects().setValue("spellCast", spell); + return true; + } + + @Override + public CodieVociferousCodexDelayedTriggeredAbility copy() { + return new CodieVociferousCodexDelayedTriggeredAbility(this); + } + + @Override + public String getRule() { + return "When you cast your next spell this turn, exile cards from the top of your library " + + "until you exile an instant or sorcery card with lesser mana value. Until end of turn, " + + "you may cast that card without paying its mana cost. Put each other card exiled this way " + + "on the bottom of your library in a random order."; + } +} + +class CodieVociferousCodexEffect extends OneShotEffect { + + CodieVociferousCodexEffect() { + super(Outcome.Benefit); + } + + private CodieVociferousCodexEffect(final CodieVociferousCodexEffect effect) { + super(effect); + } + + @Override + public CodieVociferousCodexEffect copy() { + return new CodieVociferousCodexEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Spell spell = (Spell) getValue("spellCast"); + if (player == null || spell == null) { + return false; + } + Cards toExile = new CardsImpl(); + Card toCast = null; + for (Card card : player.getLibrary().getCards(game)) { + toExile.add(card); + if (card.isInstantOrSorcery() && card.getManaValue() < spell.getManaValue()) { + toCast = card; + break; + } + } + if (toCast == null) { + player.moveCards(toExile, Zone.EXILED, source, game); + player.putCardsOnBottomOfLibrary(toExile, game, source, false); + return true; + } + PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile( + game, source, toExile.getCards(game), TargetController.YOU, + Duration.EndOfTurn, true, true + ); + toExile.remove(toCast); + player.putCardsOnBottomOfLibrary(toExile, game, source, false); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CoercivePortal.java b/Mage.Sets/src/mage/cards/c/CoercivePortal.java index d54ca01311bd..6f974f82fdd3 100644 --- a/Mage.Sets/src/mage/cards/c/CoercivePortal.java +++ b/Mage.Sets/src/mage/cards/c/CoercivePortal.java @@ -1,32 +1,39 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyAllEffect; -import mage.abilities.effects.common.SacrificeSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.choices.TwoChoiceVote; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.TargetController; -import mage.filter.common.FilterNonlandPermanent; +import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** - * - * @author fireshoes + * @author fireshoes, TheElk801 */ public final class CoercivePortal extends CardImpl { public CoercivePortal(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); // Will of the council - At the beginning of your upkeep, starting with you, each player votes for carnage or homage. If carnage gets more votes, sacrifice Coercive Portal and destroy all nonland permanents. If homage gets more votes or the vote is tied, draw a card. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new CoercivePortalEffect(), TargetController.YOU, false)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + Zone.BATTLEFIELD, new CoercivePortalEffect(), TargetController.YOU, + false, false, "Will of the council — " + + "At the beginning of your upkeep, starting with you, each player votes for carnage or homage. " + + "If carnage gets more votes, sacrifice {this} and destroy all nonland permanents. " + + "If homage gets more votes or the vote is tied, draw a card" + )); } private CoercivePortal(final CoercivePortal card) { @@ -43,10 +50,9 @@ class CoercivePortalEffect extends OneShotEffect { CoercivePortalEffect() { super(Outcome.Benefit); - this.staticText = "Will of the council — At the beginning of your upkeep, starting with you, each player votes for carnage or homage. If carnage gets more votes, sacrifice Coercive Portal and destroy all nonland permanents. If homage gets more votes or the vote is tied, draw a card"; } - CoercivePortalEffect(final CoercivePortalEffect effect) { + private CoercivePortalEffect(final CoercivePortalEffect effect) { super(effect); } @@ -57,30 +63,27 @@ public CoercivePortalEffect copy() { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - int carnageCount = 0; - int homageCount = 0; - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - if (player.chooseUse(Outcome.DestroyPermanent, "Choose carnage?", source, game)) { - carnageCount++; - game.informPlayers(player.getLogName() + " has voted for carnage"); - } else { - homageCount++; - game.informPlayers(player.getLogName() + " has voted for homage"); - } - } + // Outcome.Detriment - AI will draw a card all the time (Homage choice) + // TODO: add AI hint logic in the choice method, see Tyrant's Choice as example + TwoChoiceVote vote = new TwoChoiceVote("Carnage (sacrifice and destroy)", "Homage (draw a card)", Outcome.Detriment); + vote.doVotes(source, game); + + int carnageCount = vote.getVoteCount(true); + int homageCount = vote.getVoteCount(false); + if (carnageCount > homageCount) { + // carnage + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent != null && permanent.isControlledBy(source.getControllerId())) { + permanent.sacrifice(source, game); } - if (carnageCount > homageCount) { - new SacrificeSourceEffect().apply(game, source); - new DestroyAllEffect(new FilterNonlandPermanent()).apply(game, source); - } else { - controller.drawCards(1, source, game); + new DestroyAllEffect(StaticFilters.FILTER_PERMANENT_NON_LAND).apply(game, source); + } else { + // homage or tied + Player player = game.getPlayer(source.getControllerId()); + if (player != null) { + player.drawCards(1, source, game); } - return true; } - return false; + return true; } } diff --git a/Mage.Sets/src/mage/cards/c/CoffinPuppets.java b/Mage.Sets/src/mage/cards/c/CoffinPuppets.java index 9e1b2b5d1970..67c9fde117cf 100644 --- a/Mage.Sets/src/mage/cards/c/CoffinPuppets.java +++ b/Mage.Sets/src/mage/cards/c/CoffinPuppets.java @@ -29,7 +29,7 @@ public final class CoffinPuppets extends CardImpl { private static final FilterControlledPermanent filter = new FilterControlledPermanent("you control a Swamp"); private static final FilterControlledPermanent filter2 - = new FilterControlledLandPermanent("two lands"); + = new FilterControlledLandPermanent("lands"); static { filter.add(SubType.SWAMP.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/c/CogworkArchivist.java b/Mage.Sets/src/mage/cards/c/CogworkArchivist.java new file mode 100644 index 000000000000..f3cb77530b2d --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CogworkArchivist.java @@ -0,0 +1,48 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.PutOnLibraryTargetEffect; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetCardInGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CogworkArchivist extends CardImpl { + + public CogworkArchivist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{6}"); + + this.subtype.add(SubType.CONSTRUCT); + this.power = new MageInt(4); + this.toughness = new MageInt(5); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // {2}, {T}: Put target card from a graveyard on the bottom of its owner's library. + Ability ability = new SimpleActivatedAbility(new PutOnLibraryTargetEffect(false), new GenericManaCost(2)); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetCardInGraveyard()); + this.addAbility(ability); + } + + private CogworkArchivist(final CogworkArchivist card) { + super(card); + } + + @Override + public CogworkArchivist copy() { + return new CogworkArchivist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CollectedCompany.java b/Mage.Sets/src/mage/cards/c/CollectedCompany.java index 05138dcf02a2..8035c40fd81e 100644 --- a/Mage.Sets/src/mage/cards/c/CollectedCompany.java +++ b/Mage.Sets/src/mage/cards/c/CollectedCompany.java @@ -9,7 +9,7 @@ import mage.constants.ComparisonType; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; /** * @@ -17,11 +17,11 @@ */ public final class CollectedCompany extends CardImpl { - private static final FilterCard filter = new FilterCard("up to two creature cards with converted mana cost 3 or less"); + private static final FilterCard filter = new FilterCard("up to two creature cards with mana value 3 or less"); static { filter.add(CardType.CREATURE.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public CollectedCompany(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/c/CollectedConjuring.java b/Mage.Sets/src/mage/cards/c/CollectedConjuring.java index 3e6ef64ae5df..9ee09215d222 100644 --- a/Mage.Sets/src/mage/cards/c/CollectedConjuring.java +++ b/Mage.Sets/src/mage/cards/c/CollectedConjuring.java @@ -9,7 +9,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; @@ -45,23 +45,23 @@ public CollectedConjuring copy() { class CollectedConjuringEffect extends OneShotEffect { private static final FilterCard filter = new FilterCard( - "sorcery cards with converted mana cost 3 or less"); + "sorcery cards with mana value 3 or less"); static { filter.add(CardType.SORCERY.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } private static final FilterCard filter2 = filter.copy(); static { - filter2.setMessage("sorcery card with converted mana cost 3 or less"); + filter2.setMessage("sorcery card with mana value 3 or less"); } CollectedConjuringEffect() { super(Outcome.PlayForFree); this.staticText = "Exile the top six cards of your library. " - + "You may cast up to two sorcery cards with converted mana costs 3 or less from among them " + + "You may cast up to two sorcery cards with mana values 3 or less from among them " + "without paying their mana cost. Put the exiled cards not cast this way " + "on the bottom of your library in a random order."; } diff --git a/Mage.Sets/src/mage/cards/c/CollectiveVoyage.java b/Mage.Sets/src/mage/cards/c/CollectiveVoyage.java index ccc096e238c4..cfd1a1ba43cd 100644 --- a/Mage.Sets/src/mage/cards/c/CollectiveVoyage.java +++ b/Mage.Sets/src/mage/cards/c/CollectiveVoyage.java @@ -46,7 +46,7 @@ class CollectiveVoyageEffect extends OneShotEffect { public CollectiveVoyageEffect() { super(Outcome.Detriment); - this.staticText = "Join forces — Starting with you, each player may pay any amount of mana. Each player searches their library for up to X basic land cards, where X is the total amount of mana paid this way, puts them onto the battlefield tapped, then shuffles their library"; + this.staticText = "Join forces — Starting with you, each player may pay any amount of mana. Each player searches their library for up to X basic land cards, where X is the total amount of mana paid this way, puts them onto the battlefield tapped, then shuffles"; } public CollectiveVoyageEffect(final CollectiveVoyageEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/CombatCalligrapher.java b/Mage.Sets/src/mage/cards/c/CombatCalligrapher.java new file mode 100644 index 000000000000..98bbde793e07 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CombatCalligrapher.java @@ -0,0 +1,125 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.RestrictionEffect; +import mage.abilities.effects.common.CreateTokenTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.SilverquillToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CombatCalligrapher extends CardImpl { + + public CombatCalligrapher(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.subtype.add(SubType.BIRD); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Inklings can't attack you or planeswalkers you control. + this.addAbility(new SimpleStaticAbility(new CombatCalligrapherEffect())); + + // Whenever a player attacks one of your opponents, that attacking player creates a tapped 2/1 white and black Inkling creature token with flying that's attacking that opponent. + this.addAbility(new CombatCalligrapherTriggeredAbility()); + } + + private CombatCalligrapher(final CombatCalligrapher card) { + super(card); + } + + @Override + public CombatCalligrapher copy() { + return new CombatCalligrapher(this); + } +} + +class CombatCalligrapherTriggeredAbility extends TriggeredAbilityImpl { + + CombatCalligrapherTriggeredAbility() { + super(Zone.BATTLEFIELD, new CreateTokenTargetEffect( + new SilverquillToken(), StaticValue.get(1), true, true + ), false); + } + + private CombatCalligrapherTriggeredAbility(final CombatCalligrapherTriggeredAbility ability) { + super(ability); + } + + @Override + public CombatCalligrapherTriggeredAbility copy() { + return new CombatCalligrapherTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DEFENDER_ATTACKED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!game.getOpponents(getControllerId()).contains(event.getTargetId())) { + return false; + } + getEffects().setValue("playerToAttack", event.getPlayerId()); + return true; + } + + @Override + public String getRule() { + return "Whenever a player attacks one of your opponents, that attacking player creates " + + "a tapped 2/1 white and black Inkling creature token with flying that's attacking that opponent."; + } +} + +class CombatCalligrapherEffect extends RestrictionEffect { + + public CombatCalligrapherEffect() { + super(Duration.WhileOnBattlefield); + this.staticText = "Inklings can't attack you or planeswalkers you control"; + } + + public CombatCalligrapherEffect(final CombatCalligrapherEffect effect) { + super(effect); + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + return permanent.hasSubtype(SubType.INKLING, game); + } + + @Override + public boolean canAttack(Permanent attacker, UUID defenderId, Ability source, Game game, boolean canUseChooseDialogs) { + if (source.isControlledBy(defenderId)) { + return false; + } + Permanent planeswalker = game.getPermanent(defenderId); + return planeswalker == null || !planeswalker.isControlledBy(source.getControllerId()); + } + + + @Override + public CombatCalligrapherEffect copy() { + return new CombatCalligrapherEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CombatProfessor.java b/Mage.Sets/src/mage/cards/c/CombatProfessor.java new file mode 100644 index 000000000000..16e67fdb78a6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CombatProfessor.java @@ -0,0 +1,57 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +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.Duration; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CombatProfessor extends CardImpl { + + public CombatProfessor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.subtype.add(SubType.BIRD); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // At the beginning of combat on your turn, target creature you control gets +1/+0 and gains vigilance until end of turn. + Ability ability = new BeginningOfCombatTriggeredAbility( + new BoostTargetEffect(1, 0) + .setText("target creature you control gets +1/+0"), + TargetController.YOU, false + ); + ability.addEffect(new GainAbilityTargetEffect( + VigilanceAbility.getInstance(), Duration.EndOfTurn + ).setText("and gains vigilance until end of turn")); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + private CombatProfessor(final CombatProfessor card) { + super(card); + } + + @Override + public CombatProfessor copy() { + return new CombatProfessor(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CombustibleGearhulk.java b/Mage.Sets/src/mage/cards/c/CombustibleGearhulk.java index f394f41dd20f..62bf22d5bccb 100644 --- a/Mage.Sets/src/mage/cards/c/CombustibleGearhulk.java +++ b/Mage.Sets/src/mage/cards/c/CombustibleGearhulk.java @@ -54,7 +54,7 @@ class CombustibleGearhulkEffect extends OneShotEffect { public CombustibleGearhulkEffect() { super(Outcome.AIDontUseIt); - staticText = "target opponent may have you draw three cards. If the player doesn't, put the top three cards of your library into your graveyard, then {this} deals damage to that player equal to the total converted mana cost of those cards"; + staticText = "target opponent may have you draw three cards. If the player doesn't, you mill three cards, then {this} deals damage to that player equal to the total mana value of those cards"; } public CombustibleGearhulkEffect(final CombustibleGearhulkEffect effect) { @@ -95,7 +95,6 @@ class CombustibleGearhulkMillAndDamageEffect extends OneShotEffect { public CombustibleGearhulkMillAndDamageEffect() { super(Outcome.Damage); - staticText = "mill three cards, then {this} deals damage to that player equal to the total converted mana cost of those cards."; } public CombustibleGearhulkMillAndDamageEffect(final CombustibleGearhulkMillAndDamageEffect effect) { @@ -110,7 +109,7 @@ public boolean apply(Game game, Ability source) { .millCards(3, source, game) .getCards(game) .stream() - .mapToInt(MageObject::getConvertedManaCost) + .mapToInt(MageObject::getManaValue) .sum(); Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source)); if (targetPlayer != null) { diff --git a/Mage.Sets/src/mage/cards/c/CommandTheDreadhorde.java b/Mage.Sets/src/mage/cards/c/CommandTheDreadhorde.java index 7dc5cd57b06d..b9c405f118a0 100644 --- a/Mage.Sets/src/mage/cards/c/CommandTheDreadhorde.java +++ b/Mage.Sets/src/mage/cards/c/CommandTheDreadhorde.java @@ -51,7 +51,7 @@ class CommandTheDreadhordeEffect extends OneShotEffect { CommandTheDreadhordeEffect() { super(Outcome.Benefit); staticText = "Choose any number of target creature and/or planeswalker cards in graveyards. " + - "{this} deals damage to you equal to the total converted mana cost of those cards. " + + "{this} deals damage to you equal to the total mana value of those cards. " + "Put them onto the battlefield under your control."; } @@ -71,7 +71,7 @@ public boolean apply(Game game, Ability source) { return false; } Cards cards = new CardsImpl(source.getTargets().get(0).getTargets()); - int damage = cards.getCards(game).stream().mapToInt(Card::getConvertedManaCost).sum(); + int damage = cards.getCards(game).stream().mapToInt(Card::getManaValue).sum(); player.damage(damage, source.getSourceId(), source, game); return player.moveCards(cards, Zone.BATTLEFIELD, source, game); } diff --git a/Mage.Sets/src/mage/cards/c/CommandersInsight.java b/Mage.Sets/src/mage/cards/c/CommandersInsight.java new file mode 100644 index 000000000000..3a33d50b93e9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CommandersInsight.java @@ -0,0 +1,101 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.hint.Hint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetPlayer; +import mage.watchers.common.CommanderPlaysCountWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CommandersInsight extends CardImpl { + + public CommandersInsight(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{U}{U}{U}"); + + // Target player draws X cards plus an additional card for each time they've cast a commander from the command zone this game. + this.getSpellAbility().addEffect(new CommandersInsightEffect()); + this.getSpellAbility().addTarget(new TargetPlayer()); + this.getSpellAbility().addWatcher(new CommanderPlaysCountWatcher()); + } + + private CommandersInsight(final CommandersInsight card) { + super(card); + } + + @Override + public CommandersInsight copy() { + return new CommandersInsight(this); + } +} + +enum CommandersInsightHint implements Hint { + instance; + + @Override + public String getText(Game game, Ability ability) { + CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class); + if (watcher == null) { + return null; + } + StringBuilder sb = new StringBuilder("Commander cast counts — "); + boolean flag = false; + for (UUID playerId : game.getState().getPlayersInRange(ability.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + if (flag) { + sb.append(',').append(' '); + } + flag = true; + sb.append(player.getName()); + sb.append(": "); + sb.append(watcher.getPlayerCount(playerId)); + } + return sb.toString(); + } + + @Override + public CommandersInsightHint copy() { + return instance; + } +} + +class CommandersInsightEffect extends OneShotEffect { + + CommandersInsightEffect() { + super(Outcome.Benefit); + staticText = "target player draws X cards plus an additional card " + + "for each time they've cast a commander from the command zone this game"; + } + + private CommandersInsightEffect(final CommandersInsightEffect effect) { + super(effect); + } + + @Override + public CommandersInsightEffect copy() { + return new CommandersInsightEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); + CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class); + if (player == null || watcher == null) { + return false; + } + int toDraw = watcher.getPlayerCount(player.getId()) + source.getManaCostsToPay().getX(); + return player.drawCards(toDraw, source, game) > 0; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CommandersPlate.java b/Mage.Sets/src/mage/cards/c/CommandersPlate.java index ae791be00b4f..9655e8bfdb85 100644 --- a/Mage.Sets/src/mage/cards/c/CommandersPlate.java +++ b/Mage.Sets/src/mage/cards/c/CommandersPlate.java @@ -15,7 +15,7 @@ import mage.filter.FilterMana; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.CommanderPredicate; +import mage.filter.predicate.mageobject.CommanderPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/c/ConduitOfRuin.java b/Mage.Sets/src/mage/cards/c/ConduitOfRuin.java index 1eadbaef31f4..3e4fdcfcd4a7 100644 --- a/Mage.Sets/src/mage/cards/c/ConduitOfRuin.java +++ b/Mage.Sets/src/mage/cards/c/ConduitOfRuin.java @@ -22,7 +22,7 @@ import mage.filter.predicate.ObjectPlayer; import mage.filter.predicate.ObjectPlayerPredicate; import mage.filter.predicate.mageobject.ColorlessPredicate; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Controllable; import mage.game.Game; import mage.game.events.GameEvent; @@ -35,12 +35,12 @@ */ public final class ConduitOfRuin extends CardImpl { - private static final FilterCreatureCard filter = new FilterCreatureCard("a colorless creature card with converted mana cost 7 or greater"); + private static final FilterCreatureCard filter = new FilterCreatureCard("a colorless creature card with mana value 7 or greater"); private static final FilterCreatureCard filterCost = new FilterCreatureCard("The first creature spell"); static { filter.add(ColorlessPredicate.instance); - filter.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 6)); + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 6)); filterCost.add(new FirstCastCreatureSpellPredicate()); } diff --git a/Mage.Sets/src/mage/cards/c/ConeOfFlame.java b/Mage.Sets/src/mage/cards/c/ConeOfFlame.java index 3985be503e1a..c02a90518afc 100644 --- a/Mage.Sets/src/mage/cards/c/ConeOfFlame.java +++ b/Mage.Sets/src/mage/cards/c/ConeOfFlame.java @@ -7,7 +7,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.filter.common.FilterCreaturePlayerOrPlaneswalker; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/c/ConfiscationCoup.java b/Mage.Sets/src/mage/cards/c/ConfiscationCoup.java index 3a07467ecd10..720ec792b88e 100644 --- a/Mage.Sets/src/mage/cards/c/ConfiscationCoup.java +++ b/Mage.Sets/src/mage/cards/c/ConfiscationCoup.java @@ -28,7 +28,7 @@ */ public final class ConfiscationCoup extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("creature or artifact"); + private static final FilterPermanent filter = new FilterPermanent("artifact or creature"); static { filter.add(Predicates.or(CardType.CREATURE.getPredicate(), CardType.ARTIFACT.getPredicate())); @@ -56,7 +56,7 @@ class ConfiscationCoupEffect extends OneShotEffect { public ConfiscationCoupEffect() { super(Outcome.GainControl); - this.staticText = "Choose target creature or artifact. You get {E}{E}{E}{E}, then you may pay an amount of {E} equal to that permanent's converted mana cost. If you do, gain control of it"; + this.staticText = "Choose target creature or artifact. You get {E}{E}{E}{E}, then you may pay an amount of {E} equal to that permanent's mana value. If you do, gain control of it"; } public ConfiscationCoupEffect(final ConfiscationCoupEffect effect) { @@ -75,11 +75,11 @@ public boolean apply(Game game, Ability source) { new GetEnergyCountersControllerEffect(4).apply(game, source); Permanent targetPermanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (targetPermanent != null) { - Cost cost = new PayEnergyCost(targetPermanent.getManaCost().convertedManaCost()); + Cost cost = new PayEnergyCost(targetPermanent.getManaCost().manaValue()); if (cost.canPay(source, source, source.getControllerId(), game)) { - int convertedManaCost = targetPermanent.getManaCost().convertedManaCost(); - StringBuilder energy = new StringBuilder(convertedManaCost); - for (int i = 0; i < convertedManaCost; i++) { + int manaValue = targetPermanent.getManaCost().manaValue(); + StringBuilder energy = new StringBuilder(manaValue); + for (int i = 0; i < manaValue; i++) { energy.append("{E}"); } if (controller.chooseUse(outcome, "Pay " + energy + " to get control of " + targetPermanent.getLogName() + '?', source, game)) { diff --git a/Mage.Sets/src/mage/cards/c/Conflux.java b/Mage.Sets/src/mage/cards/c/Conflux.java index 40f44e2329d2..524fe867c807 100644 --- a/Mage.Sets/src/mage/cards/c/Conflux.java +++ b/Mage.Sets/src/mage/cards/c/Conflux.java @@ -25,7 +25,7 @@ public Conflux(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect( new ConfluxTarget(), true, true ).setText("search your library for a white card, a blue card, a black card, a red card, and a green card. " + - "Reveal those cards and put them into your hand. Then shuffle your library")); + "Reveal those cards and put them into your hand. Then shuffle")); } private Conflux(final Conflux card) { @@ -47,7 +47,7 @@ class ConfluxTarget extends TargetCardInLibrary { filter.add(Predicates.not(ColorlessPredicate.instance)); } - private static final ColorAssignment colorAssigner = new ColorAssignment(); + private static final ColorAssignment colorAssigner = new ColorAssignment("W", "U", "B", "R", "G"); ConfluxTarget() { super(0, 5, filter); diff --git a/Mage.Sets/src/mage/cards/c/Confound.java b/Mage.Sets/src/mage/cards/c/Confound.java index 2cc7c1064911..c4bba5826d2d 100644 --- a/Mage.Sets/src/mage/cards/c/Confound.java +++ b/Mage.Sets/src/mage/cards/c/Confound.java @@ -9,7 +9,7 @@ import mage.constants.CardType; import mage.filter.FilterSpell; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.other.TargetsPermanentPredicate; +import mage.filter.predicate.mageobject.TargetsPermanentPredicate; import mage.target.TargetSpell; /** diff --git a/Mage.Sets/src/mage/cards/c/ConfrontThePast.java b/Mage.Sets/src/mage/cards/c/ConfrontThePast.java new file mode 100644 index 000000000000..19b1507a8f48 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ConfrontThePast.java @@ -0,0 +1,102 @@ +package mage.cards.c; + +import com.google.common.collect.Iterables; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.common.FilterPermanentCard; +import mage.filter.common.FilterPlaneswalkerPermanent; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.common.TargetPlaneswalkerPermanent; +import mage.target.targetadjustment.TargetAdjuster; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class ConfrontThePast extends CardImpl { + + public static final FilterPlaneswalkerPermanent filter = new FilterPlaneswalkerPermanent(); + + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + } + + public ConfrontThePast(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{B}"); + + this.subtype.add(SubType.LESSON); + + // Choose one — + // • Return target planeswalker card with mana value X or less from your graveyard to the battlefield. + this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect() + .setText("return target planeswalker card with mana value X or less from your graveyard to the battlefield")); + this.getSpellAbility().setTargetAdjuster(ConfrontThePastAdjuster.instance); + + // • Remove twice X loyalty counters from target planeswalker an opponent controls. + Mode mode = new Mode(); + mode.addEffect(new ConfrontThePastLoyaltyEffect()); + mode.addTarget(new TargetPlaneswalkerPermanent(filter)); + this.getSpellAbility().addMode(mode); + } + + private ConfrontThePast(final ConfrontThePast card) { + super(card); + } + + @Override + public ConfrontThePast copy() { + return new ConfrontThePast(this); + } +} + +enum ConfrontThePastAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + if (Iterables.getOnlyElement(ability.getEffects()) instanceof ReturnFromGraveyardToBattlefieldTargetEffect) { + int xValue = ability.getManaCostsToPay().getX(); + ability.getTargets().clear(); + FilterPermanentCard filter = new FilterPermanentCard("planeswalker card with mana value X or less"); + filter.add(CardType.PLANESWALKER.getPredicate()); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + } + } +} + +class ConfrontThePastLoyaltyEffect extends OneShotEffect { + + ConfrontThePastLoyaltyEffect() { + super(Outcome.Benefit); + staticText = "remove twice X loyalty counters from target planeswalker an opponent controls"; + } + + public ConfrontThePastLoyaltyEffect(ConfrontThePastLoyaltyEffect effect) { + super(effect); + } + + @Override + public ConfrontThePastLoyaltyEffect copy() { + return new ConfrontThePastLoyaltyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int xValue = source.getManaCostsToPay().getX(); + Permanent target = game.getPermanent(source.getFirstTarget()); + target.removeCounters(CounterType.LOYALTY.createInstance(xValue * 2), source, game); + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/c/CongregationAtDawn.java b/Mage.Sets/src/mage/cards/c/CongregationAtDawn.java index 415c84c3b717..3c106d17b350 100644 --- a/Mage.Sets/src/mage/cards/c/CongregationAtDawn.java +++ b/Mage.Sets/src/mage/cards/c/CongregationAtDawn.java @@ -45,7 +45,8 @@ class CongregationAtDawnEffect extends OneShotEffect { public CongregationAtDawnEffect() { super(Outcome.Benefit); - this.staticText = "Search your library for up to three creature cards and reveal them. Shuffle your library, then put those cards on top of it in any order"; + this.staticText = "search your library for up to three creature cards, reveal them, " + + "then shuffle and put those cards on top in any order"; } public CongregationAtDawnEffect(final CongregationAtDawnEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/ConquerorsPledge.java b/Mage.Sets/src/mage/cards/c/ConquerorsPledge.java index 6015229adaa4..a88267056d95 100644 --- a/Mage.Sets/src/mage/cards/c/ConquerorsPledge.java +++ b/Mage.Sets/src/mage/cards/c/ConquerorsPledge.java @@ -24,7 +24,7 @@ public ConquerorsPledge(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new CreateTokenEffect(new KorSoldierToken(), 12), new CreateTokenEffect(new KorSoldierToken(), 6), KickedCondition.instance, - "Create six 1/1 white Kor Soldier creature tokens. if this spell was kicked, create twelve of those tokens instead")); + "Create six 1/1 white Kor Soldier creature tokens. If this spell was kicked, create twelve of those tokens instead")); } private ConquerorsPledge(final ConquerorsPledge card) { diff --git a/Mage.Sets/src/mage/cards/c/ConsecratedByBlood.java b/Mage.Sets/src/mage/cards/c/ConsecratedByBlood.java index 087ba5d3b284..1c7ef9daa7e3 100644 --- a/Mage.Sets/src/mage/cards/c/ConsecratedByBlood.java +++ b/Mage.Sets/src/mage/cards/c/ConsecratedByBlood.java @@ -17,7 +17,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/c/ConspicuousSnoop.java b/Mage.Sets/src/mage/cards/c/ConspicuousSnoop.java index 1edb15357c26..a3ef5dfbb945 100644 --- a/Mage.Sets/src/mage/cards/c/ConspicuousSnoop.java +++ b/Mage.Sets/src/mage/cards/c/ConspicuousSnoop.java @@ -14,7 +14,6 @@ import java.util.UUID; /** - * * @author htrajan */ public final class ConspicuousSnoop extends CardImpl { @@ -27,7 +26,7 @@ public final class ConspicuousSnoop extends CardImpl { public ConspicuousSnoop(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{R}"); - + this.subtype.add(SubType.GOBLIN); this.subtype.add(SubType.ROGUE); this.power = new MageInt(2); @@ -37,7 +36,7 @@ public ConspicuousSnoop(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new SimpleStaticAbility(new PlayWithTheTopCardRevealedEffect())); // You may cast Goblin spells from the top of your library. - this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter))); + this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false))); // As long as the top card of your library is a Goblin card, Conspicuous Snoop has all activated abilities of that card. this.addAbility(new SimpleStaticAbility(new GainActivatedAbilitiesOfTopCardEffect(filter.copy().withMessage("a Goblin card")))); diff --git a/Mage.Sets/src/mage/cards/c/ConspiracyTheorist.java b/Mage.Sets/src/mage/cards/c/ConspiracyTheorist.java new file mode 100644 index 000000000000..5b8bd309e174 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ConspiracyTheorist.java @@ -0,0 +1,143 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.costs.CompositeCost; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.DiscardedCardsEvent; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.target.TargetCard; +import mage.util.CardUtil; + +import java.util.Set; +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class ConspiracyTheorist extends CardImpl { + + public ConspiracyTheorist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever Conspiracy Theorist attacks, you may pay {1} and discard a card. If you do, draw a card. + this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), + new CompositeCost(new ManaCostsImpl<>("{1}"), new DiscardCardCost(), "pay {1} and discard a card")) + .setText("you may pay {1} and discard a card. If you do, draw a card"), false)); + + // Whenever you discard one or more nonland cards, you may exile one of them from your graveyard. If you do, you may cast it this turn. + this.addAbility(new ConspiracyTheoristAbility()); + } + + private ConspiracyTheorist(final ConspiracyTheorist card) { + super(card); + } + + @Override + public ConspiracyTheorist copy() { + return new ConspiracyTheorist(this); + } +} + +class ConspiracyTheoristAbility extends TriggeredAbilityImpl { + + ConspiracyTheoristAbility() { + super(Zone.BATTLEFIELD, null); + } + + ConspiracyTheoristAbility(ConspiracyTheoristAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DISCARDED_CARDS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getPlayerId().equals(getControllerId())) { + DiscardedCardsEvent discardedCardsEvent = (DiscardedCardsEvent) event; + Set discardedNonLandCards = discardedCardsEvent.getDiscardedCards().getCards(StaticFilters.FILTER_CARD_NON_LAND, game); + if (discardedNonLandCards.size() > 0) { + this.getEffects().clear(); + this.getEffects().add(new ConspiracyTheoristEffect(discardedNonLandCards)); + return true; + } + } + return false; + } + + @Override + public ConspiracyTheoristAbility copy() { + return new ConspiracyTheoristAbility(this); + } + + @Override + public String getRule() { + return "Whenever you discard one or more nonland cards, you may exile one of them from your graveyard. If you do, you may cast it this turn."; + } +} + +class ConspiracyTheoristEffect extends OneShotEffect { + + private final Set discardedCards; + + ConspiracyTheoristEffect(Set discardedCards) { + super(Outcome.Benefit); + this.discardedCards = discardedCards; + } + + ConspiracyTheoristEffect(ConspiracyTheoristEffect effect) { + super(effect); + this.discardedCards = effect.discardedCards; + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + CardsImpl cards = new CardsImpl(discardedCards); + TargetCard target = new TargetCard(Zone.GRAVEYARD, new FilterCard("card to exile")); + boolean validTarget = cards.stream() + .anyMatch(card -> target.canTarget(card, game)); + if (validTarget && controller.chooseUse(Outcome.Benefit, "Exile a card?", source, game)) { + if (controller.choose(Outcome.Benefit, cards, target, game)) { + Card card = cards.get(target.getFirstTarget(), game); + if (card != null && controller.moveCards(card, Zone.EXILED, source, game)) { + // you may cast it this turn + CardUtil.makeCardPlayable(game, source, card, Duration.EndOfTurn, false); + } + } + } + return true; + } + return false; + } + + @Override + public ConspiracyTheoristEffect copy() { + return new ConspiracyTheoristEffect(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/c/ConsulsShieldguard.java b/Mage.Sets/src/mage/cards/c/ConsulsShieldguard.java index eed6249e0e13..93d11a8282ee 100644 --- a/Mage.Sets/src/mage/cards/c/ConsulsShieldguard.java +++ b/Mage.Sets/src/mage/cards/c/ConsulsShieldguard.java @@ -15,7 +15,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.common.FilterAttackingCreature; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/c/ConsumeStrength.java b/Mage.Sets/src/mage/cards/c/ConsumeStrength.java index 08e85c374612..d6b0522b1ab2 100644 --- a/Mage.Sets/src/mage/cards/c/ConsumeStrength.java +++ b/Mage.Sets/src/mage/cards/c/ConsumeStrength.java @@ -10,7 +10,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/c/ConsumeTheMeek.java b/Mage.Sets/src/mage/cards/c/ConsumeTheMeek.java index cabb07a20585..e1494d83f4da 100644 --- a/Mage.Sets/src/mage/cards/c/ConsumeTheMeek.java +++ b/Mage.Sets/src/mage/cards/c/ConsumeTheMeek.java @@ -8,7 +8,7 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; /** * @@ -16,10 +16,10 @@ */ public final class ConsumeTheMeek extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures with converted mana cost 3 or less"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures with mana value 3 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public ConsumeTheMeek(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/c/ContainmentBreach.java b/Mage.Sets/src/mage/cards/c/ContainmentBreach.java new file mode 100644 index 000000000000..b5b4ff81d7b4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ContainmentBreach.java @@ -0,0 +1,72 @@ +package mage.cards.c; + +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.constants.SubType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.WitherbloomToken; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ContainmentBreach extends CardImpl { + + public ContainmentBreach(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{G}"); + + this.subtype.add(SubType.LESSON); + + // Destroy target artifact or enchantment. If its mana value is 2 or less, create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life." + this.getSpellAbility().addEffect(new ContainmentBreachEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); + } + + private ContainmentBreach(final ContainmentBreach card) { + super(card); + } + + @Override + public ContainmentBreach copy() { + return new ContainmentBreach(this); + } +} + +class ContainmentBreachEffect extends OneShotEffect { + + ContainmentBreachEffect() { + super(Outcome.Benefit); + staticText = "destroy target artifact or enchantment. If its mana value is 2 or less, " + + "create a 1/1 black and green Pest creature token with \"When this creature dies, you gain 1 life.\""; + } + + private ContainmentBreachEffect(final ContainmentBreachEffect effect) { + super(effect); + } + + @Override + public ContainmentBreachEffect copy() { + return new ContainmentBreachEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + permanent.destroy(source, game, false); + if (permanent.getManaValue() <= 2) { + new WitherbloomToken().putOntoBattlefield(1, game, source, source.getControllerId()); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/c/ConvictedKiller.java b/Mage.Sets/src/mage/cards/c/ConvictedKiller.java index 21fd1411e86e..b81f22ce7e1d 100644 --- a/Mage.Sets/src/mage/cards/c/ConvictedKiller.java +++ b/Mage.Sets/src/mage/cards/c/ConvictedKiller.java @@ -1,4 +1,3 @@ - package mage.cards.c; import java.util.UUID; @@ -6,6 +5,7 @@ import mage.MageInt; import mage.abilities.TriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.TransformSourceEffect; @@ -33,8 +33,7 @@ public ConvictedKiller(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each upkeep, if no spells were cast last turn, transform Convicted Killer. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private ConvictedKiller(final ConvictedKiller card) { diff --git a/Mage.Sets/src/mage/cards/c/CoralFighters.java b/Mage.Sets/src/mage/cards/c/CoralFighters.java index 169ff7fbb83a..7b2869a92e66 100644 --- a/Mage.Sets/src/mage/cards/c/CoralFighters.java +++ b/Mage.Sets/src/mage/cards/c/CoralFighters.java @@ -66,7 +66,7 @@ public boolean apply(Game game, Ability source) { if(card != null) { Cards cards = new CardsImpl(card); controller.lookAtCards("Coral Fighters", cards, game); - if (controller.chooseUse(outcome, "Do you wish to put card on the bottom of player's library?", source, game)) { + if (controller.chooseUse(outcome, "Put that card on the bottom of its owner's library?", source, game)) { controller.moveCardToLibraryWithInfo(card, source, game, Zone.LIBRARY, false, false); } else { diff --git a/Mage.Sets/src/mage/cards/c/CorpseConnoisseur.java b/Mage.Sets/src/mage/cards/c/CorpseConnoisseur.java index 8e4e423ba3eb..a08b099ba3c8 100644 --- a/Mage.Sets/src/mage/cards/c/CorpseConnoisseur.java +++ b/Mage.Sets/src/mage/cards/c/CorpseConnoisseur.java @@ -53,7 +53,7 @@ class SearchLibraryPutInGraveyard extends SearchEffect { public SearchLibraryPutInGraveyard() { super(new TargetCardInLibrary(StaticFilters.FILTER_CARD_CREATURE), Outcome.Neutral); - staticText = "search your library for a card and put that card into your graveyard. Then shuffle your library"; + staticText = "search your library for a card and put that card into your graveyard. Then shuffle"; } public SearchLibraryPutInGraveyard(final SearchLibraryPutInGraveyard effect) { diff --git a/Mage.Sets/src/mage/cards/c/CorpseKnight.java b/Mage.Sets/src/mage/cards/c/CorpseKnight.java index d5a68d8c190f..aa12a8357eb8 100644 --- a/Mage.Sets/src/mage/cards/c/CorpseKnight.java +++ b/Mage.Sets/src/mage/cards/c/CorpseKnight.java @@ -9,7 +9,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/c/Corrosion.java b/Mage.Sets/src/mage/cards/c/Corrosion.java index ca3d0d2e17d5..309a29fadd2f 100644 --- a/Mage.Sets/src/mage/cards/c/Corrosion.java +++ b/Mage.Sets/src/mage/cards/c/Corrosion.java @@ -57,7 +57,7 @@ class CorrosionUpkeepEffect extends OneShotEffect { CorrosionUpkeepEffect() { super(Outcome.DestroyPermanent); - this.staticText = "put a rust counter on each artifact target opponent controls. Then destroy each artifact with converted mana cost less than or equal to the number of rust counters on it. Artifacts destroyed this way can't be regenerated"; + this.staticText = "put a rust counter on each artifact target opponent controls. Then destroy each artifact with mana value less than or equal to the number of rust counters on it. Artifacts destroyed this way can't be regenerated"; } CorrosionUpkeepEffect(final CorrosionUpkeepEffect effect) { @@ -83,7 +83,7 @@ public boolean apply(Game game, Ability source) { } // destroy each artifact with converted mana cost less than or equal to the number of rust counters on it for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - if (permanent.getConvertedManaCost() <= permanent.getCounters(game).getCount(CounterType.RUST)) { + if (permanent.getManaValue() <= permanent.getCounters(game).getCount(CounterType.RUST)) { permanent.destroy(source, game, true); } } diff --git a/Mage.Sets/src/mage/cards/c/CosmicIntervention.java b/Mage.Sets/src/mage/cards/c/CosmicIntervention.java index 44e8a59c6628..63555979611b 100644 --- a/Mage.Sets/src/mage/cards/c/CosmicIntervention.java +++ b/Mage.Sets/src/mage/cards/c/CosmicIntervention.java @@ -99,8 +99,8 @@ public boolean checksEventType(GameEvent event, Game game) { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD - && (((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD)) { + ZoneChangeEvent zoneChangeEvent = (ZoneChangeEvent) event; + if (zoneChangeEvent.isDiesEvent()) { Player controller = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); if (permanent == null diff --git a/Mage.Sets/src/mage/cards/c/CouncilGuardian.java b/Mage.Sets/src/mage/cards/c/CouncilGuardian.java index 86693351df0e..8d7a384f0f07 100644 --- a/Mage.Sets/src/mage/cards/c/CouncilGuardian.java +++ b/Mage.Sets/src/mage/cards/c/CouncilGuardian.java @@ -1,7 +1,6 @@ package mage.cards.c; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; @@ -10,6 +9,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.choices.ChoiceColor; +import mage.choices.VoteHandler; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; @@ -17,12 +17,13 @@ import mage.game.Game; import mage.players.Player; -import java.util.HashMap; -import java.util.Map; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.Set; import java.util.UUID; /** - * @author Styxo + * @author Styxo, TheElk801 */ public final class CouncilGuardian extends CardImpl { @@ -35,8 +36,9 @@ public CouncilGuardian(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(5); // Will of the council - When Council Guardian enters the battlefield, starting with you, each player votes for blue, black, red, or green. Council Guardian gains protection from each color with the most votes or tied for most votes. - this.addAbility(new EntersBattlefieldTriggeredAbility(new CouncilsGuardianEffect(), false, "Will of the council — ")); - + this.addAbility(new EntersBattlefieldTriggeredAbility( + new CouncilsGuardianEffect(), false, "Will of the council — " + )); } private CouncilGuardian(final CouncilGuardian card) { @@ -51,12 +53,13 @@ public CouncilGuardian copy() { class CouncilsGuardianEffect extends OneShotEffect { - public CouncilsGuardianEffect() { + CouncilsGuardianEffect() { super(Outcome.Benefit); - this.staticText = "starting with you, each player votes for blue, black, red, or green. {this} gains protection from each color with the most votes or tied for most votes"; + this.staticText = "starting with you, each player votes for blue, black, red, or green. " + + "{this} gains protection from each color with the most votes or tied for most votes"; } - public CouncilsGuardianEffect(final CouncilsGuardianEffect effect) { + private CouncilsGuardianEffect(final CouncilsGuardianEffect effect) { super(effect); } @@ -67,45 +70,39 @@ public CouncilsGuardianEffect copy() { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - ChoiceColor choice = new ChoiceColor(); - choice.getChoices().remove("White"); - if (controller != null) { - Map chosenColors = new HashMap<>(2); - int maxCount = 0; - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - choice.clearChoice(); - if (player.choose(Outcome.Detriment, choice, game)) { - ObjectColor color = choice.getColor(); - if (color != null) { - if (chosenColors.containsKey(color)) { - int count = chosenColors.get(color) + 1; - if (count > maxCount) { - maxCount = count; - } - chosenColors.put(color, count); - } else { - if (maxCount == 0) { - maxCount = 1; - } - chosenColors.put(color, 1); - } - game.informPlayers(player.getLogName() + " has chosen " + color.getDescription() + '.'); - } - } - } - } + CouncilGuardianVote vote = new CouncilGuardianVote(); + vote.doVotes(source, game); - for (Map.Entry entry : chosenColors.entrySet()) { - if (entry.getValue() == maxCount) { - ObjectColor color = entry.getKey(); - game.addEffect(new GainAbilitySourceEffect(ProtectionAbility.from(color), Duration.Custom), source); - } + for (String color : vote.getMostVoted()) { + if (color == null) { + continue; } - return true; + game.addEffect(new GainAbilitySourceEffect( + ProtectionAbility.from(ChoiceColor.getColorFromString(color)), Duration.Custom + ), source); } - return false; + return true; + } +} + +class CouncilGuardianVote extends VoteHandler { + + @Override + protected Set getPossibleVotes(Ability source, Game game) { + return new LinkedHashSet<>(Arrays.asList("Blue", "Black", "Red", "Green")); + } + + @Override + public String playerChoose(String voteInfo, Player player, Player decidingPlayer, Ability source, Game game) { + ChoiceColor choice = new ChoiceColor(); + choice.getChoices().remove("White"); + choice.setSubMessage(voteInfo); + decidingPlayer.choose(Outcome.AIDontUseIt, choice, game); // TODO: add AI hint logic in the choice method, see Tyrant's Choice as example + return choice.getChoice(); + } + + @Override + protected String voteName(String vote) { + return vote; } } diff --git a/Mage.Sets/src/mage/cards/c/CouncilsJudgment.java b/Mage.Sets/src/mage/cards/c/CouncilsJudgment.java index f6c77748e275..0c233f6d818a 100644 --- a/Mage.Sets/src/mage/cards/c/CouncilsJudgment.java +++ b/Mage.Sets/src/mage/cards/c/CouncilsJudgment.java @@ -1,16 +1,16 @@ package mage.cards.c; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.choices.VoteHandler; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; +import mage.filter.FilterPermanent; import mage.filter.common.FilterNonlandPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.ControllerIdPredicate; @@ -18,11 +18,14 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; -import mage.target.common.TargetNonlandPermanent; +import mage.target.TargetPermanent; + +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.UUID; /** - * - * @author emerald000 + * @author emerald000, TheElk801 */ public final class CouncilsJudgment extends CardImpl { @@ -47,10 +50,11 @@ class CouncilsJudgmentEffect extends OneShotEffect { CouncilsJudgmentEffect() { super(Outcome.Exile); - this.staticText = "Will of the council — Starting with you, each player votes for a nonland permanent you don't control. Exile each permanent with the most votes or tied for most votes"; + this.staticText = "Will of the council — Starting with you, each player votes for a " + + "nonland permanent you don't control. Exile each permanent with the most votes or tied for most votes"; } - CouncilsJudgmentEffect(final CouncilsJudgmentEffect effect) { + private CouncilsJudgmentEffect(final CouncilsJudgmentEffect effect) { super(effect); } @@ -61,48 +65,48 @@ public CouncilsJudgmentEffect copy() { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Map chosenCards = new HashMap<>(2); - int maxCount = 0; - FilterNonlandPermanent filter = new FilterNonlandPermanent("a nonland permanent " + controller.getLogName() + " doesn't control"); - filter.add(Predicates.not(new ControllerIdPredicate(controller.getId()))); - //Players each choose a legal permanent - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - Target target = new TargetNonlandPermanent(filter); - target.setNotTarget(true); - if (player.choose(Outcome.Exile, target, source.getSourceId(), game)) { - Permanent permanent = game.getPermanent(target.getFirstTarget()); - if (permanent != null) { - if (chosenCards.containsKey(permanent)) { - int count = chosenCards.get(permanent) + 1; - if (count > maxCount) { - maxCount = count; - } - chosenCards.put(permanent, count); - } else { - if (maxCount == 0) { - maxCount = 1; - } - chosenCards.put(permanent, 1); - } - game.informPlayers(player.getLogName() + " has chosen: " + permanent.getLogName()); - } - } - } - } - - //Exile the card(s) with the most votes. - for (Entry entry : chosenCards.entrySet()) { - if (entry.getValue() == maxCount) { - Permanent permanent = entry.getKey(); - controller.moveCardToExileWithInfo(permanent, null, "", source, game, Zone.BATTLEFIELD, true); - } - } - return true; + Player player = game.getPlayer(source.getSourceId()); + if (player == null) { + return false; } - return false; + CouncilsJudgmentVote vote = new CouncilsJudgmentVote(player); + vote.doVotes(source, game); + + Cards cards = new CardsImpl(); + vote.getMostVoted().stream().forEach(cards::add); + return player.moveCards(cards, Zone.EXILED, source, game); + } +} + +class CouncilsJudgmentVote extends VoteHandler { + + private final FilterPermanent filter; + + CouncilsJudgmentVote(Player controller) { + this.filter = new FilterNonlandPermanent("nonland permanent not controlled by " + controller.getName()); + this.filter.add(Predicates.not(new ControllerIdPredicate(controller.getId()))); + } + + @Override + protected Set getPossibleVotes(Ability source, Game game) { + // too much permanentns on battlefield, so no need to show full list here + return new LinkedHashSet<>(); + } + + @Override + public Permanent playerChoose(String voteInfo, Player player, Player decidingPlayer, Ability source, Game game) { + if (game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game) < 1) { + return null; + } + TargetPermanent target = new TargetPermanent(1, filter); + target.withChooseHint(voteInfo + " (to exile)"); + target.setNotTarget(true); + decidingPlayer.choose(Outcome.Exile, target, source.getSourceId(), game); + return game.getPermanent(target.getFirstTarget()); + } + + @Override + protected String voteName(Permanent vote) { + return vote.getIdName(); } } diff --git a/Mage.Sets/src/mage/cards/c/Counterbalance.java b/Mage.Sets/src/mage/cards/c/Counterbalance.java index 682b5e1bef25..11a567aae9c1 100644 --- a/Mage.Sets/src/mage/cards/c/Counterbalance.java +++ b/Mage.Sets/src/mage/cards/c/Counterbalance.java @@ -47,7 +47,7 @@ class CounterbalanceEffect extends OneShotEffect { public CounterbalanceEffect() { super(Outcome.Neutral); - this.staticText = "you may reveal the top card of your library. If you do, counter that spell if it has the same converted mana cost as the revealed card"; + this.staticText = "you may reveal the top card of your library. If you do, counter that spell if it has the same mana value as the revealed card"; } public CounterbalanceEffect(final CounterbalanceEffect effect) { @@ -71,7 +71,7 @@ public boolean apply(Game game, Ability source) { CardsImpl cards = new CardsImpl(); cards.add(topcard); controller.revealCards(sourcePermanent.getName(), cards, game); - if (topcard.getConvertedManaCost() == spell.getConvertedManaCost()) { + if (topcard.getManaValue() == spell.getManaValue()) { return game.getStack().counter(spell.getId(), source, game); } } diff --git a/Mage.Sets/src/mage/cards/c/CourserOfKruphix.java b/Mage.Sets/src/mage/cards/c/CourserOfKruphix.java index 2dcbcb047f0f..3dd49b34446a 100644 --- a/Mage.Sets/src/mage/cards/c/CourserOfKruphix.java +++ b/Mage.Sets/src/mage/cards/c/CourserOfKruphix.java @@ -21,7 +21,7 @@ */ public final class CourserOfKruphix extends CardImpl { - private static final FilterCard filter = new FilterLandCard("play land cards"); + private static final FilterCard filter = new FilterLandCard("play lands"); public CourserOfKruphix(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{1}{G}{G}"); @@ -33,8 +33,8 @@ public CourserOfKruphix(UUID ownerId, CardSetInfo setInfo) { // Play with the top card of your library revealed. this.addAbility(new SimpleStaticAbility(new PlayWithTheTopCardRevealedEffect())); - // You may play the top card of your library if it's a land card. - this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter))); + // You may play lands from the top of your library. + this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false))); // Whenever a land enters the battlefield under your control, you gain 1 life. this.addAbility(new EntersBattlefieldControlledTriggeredAbility(new GainLifeEffect(1), StaticFilters.FILTER_LAND_A)); diff --git a/Mage.Sets/src/mage/cards/c/CourtStreetDenizen.java b/Mage.Sets/src/mage/cards/c/CourtStreetDenizen.java index 910479e4be20..7c728e6e2fea 100644 --- a/Mage.Sets/src/mage/cards/c/CourtStreetDenizen.java +++ b/Mage.Sets/src/mage/cards/c/CourtStreetDenizen.java @@ -16,7 +16,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/c/CoverOfWinter.java b/Mage.Sets/src/mage/cards/c/CoverOfWinter.java index f09ab94cb193..30998303d890 100644 --- a/Mage.Sets/src/mage/cards/c/CoverOfWinter.java +++ b/Mage.Sets/src/mage/cards/c/CoverOfWinter.java @@ -65,7 +65,7 @@ public CoverOfWinterEffect(CoverOfWinterEffect effect) { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_PLAYER || event.getType() == GameEvent.EventType.DAMAGE_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGE_PLAYER || event.getType() == GameEvent.EventType.DAMAGE_PERMANENT; } @Override @@ -86,9 +86,9 @@ public boolean applies(GameEvent event, Ability source, Game game) { return super.applies(event, source, game); } - if (event.getType() == GameEvent.EventType.DAMAGE_CREATURE) { + if (event.getType() == GameEvent.EventType.DAMAGE_PERMANENT) { Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.isControlledBy(source.getControllerId())) { + if (permanent != null && permanent.isCreature() && permanent.isControlledBy(source.getControllerId())) { return super.applies(event, source, game); } } diff --git a/Mage.Sets/src/mage/cards/c/CovetedPrize.java b/Mage.Sets/src/mage/cards/c/CovetedPrize.java index 94e369e3d25a..5fd841994c97 100644 --- a/Mage.Sets/src/mage/cards/c/CovetedPrize.java +++ b/Mage.Sets/src/mage/cards/c/CovetedPrize.java @@ -34,7 +34,7 @@ Zone.ALL, new SpellCostReductionForEachSourceEffect(1, PartyCount.instance) this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new CastWithoutPayingManaCostEffect(4), FullPartyCondition.instance, "If you have a full party, " + - "you may cast a spell with converted mana cost 4 or less from your hand without paying its mana cost." + "you may cast a spell with mana value 4 or less from your hand without paying its mana cost." )); } diff --git a/Mage.Sets/src/mage/cards/c/CovetousUrge.java b/Mage.Sets/src/mage/cards/c/CovetousUrge.java index 90921c0970c3..f75edb60c0a3 100644 --- a/Mage.Sets/src/mage/cards/c/CovetousUrge.java +++ b/Mage.Sets/src/mage/cards/c/CovetousUrge.java @@ -91,7 +91,7 @@ public boolean apply(Game game, Ability source) { if (card.getSpellAbility() == null) { return true; } - CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.Custom); + CardUtil.makeCardPlayable(game, source, card, Duration.Custom, true); return true; } } diff --git a/Mage.Sets/src/mage/cards/c/CrabappleCohort.java b/Mage.Sets/src/mage/cards/c/CrabappleCohort.java index ba13e018cb99..c105f943f2c8 100644 --- a/Mage.Sets/src/mage/cards/c/CrabappleCohort.java +++ b/Mage.Sets/src/mage/cards/c/CrabappleCohort.java @@ -17,7 +17,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/c/CrackleWithPower.java b/Mage.Sets/src/mage/cards/c/CrackleWithPower.java new file mode 100644 index 000000000000..74dfe1ea9823 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CrackleWithPower.java @@ -0,0 +1,52 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.MultipliedValue; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.Game; +import mage.target.common.TargetAnyTarget; +import mage.target.targetadjustment.TargetAdjuster; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CrackleWithPower extends CardImpl { + + private static final DynamicValue value = new MultipliedValue(ManacostVariableValue.instance, 5); + + public CrackleWithPower(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{X}{X}{R}{R}"); + + // Crackle with Power deals five times X damage to up to X targets. + this.getSpellAbility().addEffect( + new DamageTargetEffect(value).setText("{this} deals five times X damage to each of up to X targets") + ); + this.getSpellAbility().setTargetAdjuster(CrackleWithPowerAdjuster.instance); + } + + private CrackleWithPower(final CrackleWithPower card) { + super(card); + } + + @Override + public CrackleWithPower copy() { + return new CrackleWithPower(this); + } +} + +enum CrackleWithPowerAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + ability.getTargets().clear(); + ability.addTarget(new TargetAnyTarget(0, ability.getManaCostsToPay().getX())); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CracklingDrake.java b/Mage.Sets/src/mage/cards/c/CracklingDrake.java index a80aeeddbfbe..80268a3cf70a 100644 --- a/Mage.Sets/src/mage/cards/c/CracklingDrake.java +++ b/Mage.Sets/src/mage/cards/c/CracklingDrake.java @@ -6,6 +6,8 @@ import mage.abilities.dynamicvalue.common.InstantSorceryExileGraveyardCount; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.SetPowerSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -21,6 +23,10 @@ */ public final class CracklingDrake extends CardImpl { + private static final Hint hint = new ValueHint( + "Instant and sorcery cards in your exile and graveyard", InstantSorceryExileGraveyardCount.instance + ); + public CracklingDrake(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}{U}{R}{R}"); @@ -39,7 +45,7 @@ public CracklingDrake(UUID ownerId, CardSetInfo setInfo) { ).setText("{this}'s power is equal to the total number " + "of instant and sorcery cards you own " + "in exile and in your graveyard.") - )); + ).addHint(hint)); // When Crackling Drake enters the battlefield, draw a card. this.addAbility(new EntersBattlefieldTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/c/CragSaurian.java b/Mage.Sets/src/mage/cards/c/CragSaurian.java index 9d19434dfd24..ff8cb76395e4 100644 --- a/Mage.Sets/src/mage/cards/c/CragSaurian.java +++ b/Mage.Sets/src/mage/cards/c/CragSaurian.java @@ -94,7 +94,7 @@ public CragSaurianTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/c/CramSession.java b/Mage.Sets/src/mage/cards/c/CramSession.java new file mode 100644 index 000000000000..fd6f3574255c --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CramSession.java @@ -0,0 +1,34 @@ +package mage.cards.c; + +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LearnEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CramSession extends CardImpl { + + public CramSession(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B/G}"); + + // You gain 4 life. + this.getSpellAbility().addEffect(new GainLifeEffect(4)); + + // Learn. + this.getSpellAbility().addEffect(new LearnEffect().concatBy("
")); + } + + private CramSession(final CramSession card) { + super(card); + } + + @Override + public CramSession copy() { + return new CramSession(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CranialExtraction.java b/Mage.Sets/src/mage/cards/c/CranialExtraction.java index b6cdb7d0706e..283ce176ddc9 100644 --- a/Mage.Sets/src/mage/cards/c/CranialExtraction.java +++ b/Mage.Sets/src/mage/cards/c/CranialExtraction.java @@ -17,6 +17,7 @@ import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; +import mage.util.CardUtil; /** * @@ -85,6 +86,6 @@ public CranialExtractionEffect copy() { @Override public String getText(Mode mode) { - return "Name a nonland card. " + super.getText(mode); + return "Choose a nonland card name. " + CardUtil.getTextWithFirstCharUpperCase(super.getText(mode)); } } diff --git a/Mage.Sets/src/mage/cards/c/CraterElemental.java b/Mage.Sets/src/mage/cards/c/CraterElemental.java index 4c65d9284331..5263bc538f8c 100644 --- a/Mage.Sets/src/mage/cards/c/CraterElemental.java +++ b/Mage.Sets/src/mage/cards/c/CraterElemental.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.ActivateIfConditionActivatedAbility; @@ -15,16 +13,12 @@ import mage.abilities.effects.common.continuous.SetPowerSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AbilityWord; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.SubLayer; -import mage.constants.Zone; +import mage.constants.*; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class CraterElemental extends CardImpl { @@ -36,7 +30,9 @@ public CraterElemental(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(6); // {R}, {T}, Sacrifice Crater Elemental: Crater Elemental deals 4 damage to target creature. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(4), new ManaCostsImpl("{R}")); + Ability ability = new SimpleActivatedAbility( + new DamageTargetEffect(4, "it"), new ManaCostsImpl<>("{R}") + ); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); ability.addTarget(new TargetCreaturePermanent()); @@ -45,9 +41,11 @@ public CraterElemental(UUID ownerId, CardSetInfo setInfo) { // Formidable — {2}{R}: Crater Elemental has base power 8 until end of turn. Activate this ability only if creatures you control have total power 8 or greater. ability = new ActivateIfConditionActivatedAbility( Zone.BATTLEFIELD, - new SetPowerSourceEffect(StaticValue.get(8), Duration.EndOfTurn, SubLayer.SetPT_7b), - new ManaCostsImpl("{2}{R}"), - FormidableCondition.instance); + new SetPowerSourceEffect(StaticValue.get(8), Duration.EndOfTurn, SubLayer.SetPT_7b) + .setText("{this} has base power 8 until end of turn"), + new ManaCostsImpl<>("{2}{R}"), + FormidableCondition.instance + ); ability.setAbilityWord(AbilityWord.FORMIDABLE); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/CrazedFirecat.java b/Mage.Sets/src/mage/cards/c/CrazedFirecat.java index 940b77f0c3d1..a3bf67ae97c1 100644 --- a/Mage.Sets/src/mage/cards/c/CrazedFirecat.java +++ b/Mage.Sets/src/mage/cards/c/CrazedFirecat.java @@ -69,7 +69,7 @@ public boolean apply(Game game, Ability source) { flipsWon++; // AI workaround to stop on good condition - if (!controller.isHuman() && !controller.isTestMode() && flipsWon >= 2) { + if (controller.isComputer() && flipsWon >= 2) { break; } } diff --git a/Mage.Sets/src/mage/cards/c/CreativeOutburst.java b/Mage.Sets/src/mage/cards/c/CreativeOutburst.java new file mode 100644 index 000000000000..2c39e9e4dfc7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CreativeOutburst.java @@ -0,0 +1,54 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.permanent.token.TreasureToken; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CreativeOutburst extends CardImpl { + + public CreativeOutburst(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}{U}{R}{R}"); + + // Creative Outburst deals 5 damage to any target. Look at the top five cards of your library. Put one of them into your hand and the rest on the bottom of your library in a random order. + this.getSpellAbility().addEffect(new DamageTargetEffect(5)); + this.getSpellAbility().addEffect(new LookLibraryAndPickControllerEffect( + StaticValue.get(5), false, StaticValue.get(1), + StaticFilters.FILTER_CARD, Zone.LIBRARY, false, false + ).setBackInRandomOrder(true).setText("Look at the top five cards of your library. " + + "Put one of them into your hand and the rest on the bottom of your library in a random order")); + this.getSpellAbility().addTarget(new TargetAnyTarget()); + + // {U/R}{U/R}, Discard Creative Outburst: Create a Treasure token. + Ability ability = new SimpleActivatedAbility( + Zone.HAND, new CreateTokenEffect(new TreasureToken()), new ManaCostsImpl("{U/R}{U/R}") + ); + ability.addCost(new DiscardSourceCost()); + this.addAbility(ability); + } + + private CreativeOutburst(final CreativeOutburst card) { + super(card); + } + + @Override + public CreativeOutburst copy() { + return new CreativeOutburst(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CreativeTechnique.java b/Mage.Sets/src/mage/cards/c/CreativeTechnique.java new file mode 100644 index 000000000000..602bd986f046 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CreativeTechnique.java @@ -0,0 +1,95 @@ +package mage.cards.c; + +import mage.ApprovingObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.DemonstrateAbility; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CreativeTechnique extends CardImpl { + + public CreativeTechnique(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{R}"); + + // Demonstrate + this.addAbility(new DemonstrateAbility()); + + // Shuffle your library, then reveal cards from the top of it until you reveal a nonland card. Exile that card and put the rest on the bottom of your library in a random order. You may cast the exiled card without paying its mana cost. + this.getSpellAbility().addEffect(new CreativeTechniqueEffect()); + } + + private CreativeTechnique(final CreativeTechnique card) { + super(card); + } + + @Override + public CreativeTechnique copy() { + return new CreativeTechnique(this); + } +} + +class CreativeTechniqueEffect extends OneShotEffect { + + CreativeTechniqueEffect() { + super(Outcome.Benefit); + staticText = "shuffle your library, then reveal cards from the top of it until you reveal a nonland card. " + + "Exile that card and put the rest on the bottom of your library in a random order. " + + "You may cast the exiled card without paying its mana cost"; + } + + private CreativeTechniqueEffect(final CreativeTechniqueEffect effect) { + super(effect); + } + + @Override + public CreativeTechniqueEffect copy() { + return new CreativeTechniqueEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + player.shuffleLibrary(source, game); + Cards cards = new CardsImpl(); + Card toCast = null; + for (Card card : player.getLibrary().getCards(game)) { + cards.add(card); + if (!card.isLand()) { + toCast = card; + break; + } + } + player.revealCards(source, cards, game); + cards.remove(toCast); + if (toCast != null) { + player.moveCards(toCast, Zone.EXILED, source, game); + } + player.putCardsOnBottomOfLibrary(cards, game, source, false); + if (toCast == null || !player.chooseUse( + Outcome.PlayForFree, "Cast " + toCast.getIdName() + + " without paying its mana cost?", source, game + )) { + return true; + } + game.getState().setValue("PlayFromNotOwnHandZone" + toCast.getId(), Boolean.TRUE); + player.cast( + player.chooseAbilityForCast(toCast, game, true), + game, true, new ApprovingObject(source, game) + ); + game.getState().setValue("PlayFromNotOwnHandZone" + toCast.getId(), null); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CreepyDoll.java b/Mage.Sets/src/mage/cards/c/CreepyDoll.java index 4022f1b25b07..ca21b756e4bb 100644 --- a/Mage.Sets/src/mage/cards/c/CreepyDoll.java +++ b/Mage.Sets/src/mage/cards/c/CreepyDoll.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; @@ -10,24 +8,25 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamagedCreatureEvent; +import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** * @author nantuko */ public final class CreepyDoll extends CardImpl { public CreepyDoll(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{5}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}"); this.subtype.add(SubType.CONSTRUCT); this.power = new MageInt(1); @@ -67,13 +66,17 @@ public CreepyDollTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (((DamagedCreatureEvent) event).isCombatDamage() && event.getSourceId().equals(sourceId)) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getTargetId())); + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null + && permanent.isCreature() + && ((DamagedEvent) event).isCombatDamage() + && event.getSourceId().equals(sourceId)) { + getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game)); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/c/CrestedSunmare.java b/Mage.Sets/src/mage/cards/c/CrestedSunmare.java index 5020a37bdd22..6d1e56f09e03 100644 --- a/Mage.Sets/src/mage/cards/c/CrestedSunmare.java +++ b/Mage.Sets/src/mage/cards/c/CrestedSunmare.java @@ -1,29 +1,24 @@ - package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.YouGainedLifeCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.dynamicvalue.common.ControllerGotLifeCount; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.abilities.keyword.IndestructibleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.ComparisonType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.common.FilterControlledPermanent; import mage.game.permanent.token.CrestedSunmareToken; import mage.watchers.common.PlayerGainedLifeWatcher; +import java.util.UUID; + /** - * * @author emerald000 */ public final class CrestedSunmare extends CardImpl { @@ -42,15 +37,19 @@ public CrestedSunmare(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(5); // Other Horses you control have indestructible. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield, filter, true))); + this.addAbility(new SimpleStaticAbility(new GainAbilityAllEffect( + IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield, filter, true + ))); // At the beginning of each end step, if you gained life this turn, create a 5/5 white Horse creature token. - this.addAbility( - new ConditionalInterveningIfTriggeredAbility( - new BeginningOfEndStepTriggeredAbility(new CreateTokenEffect(new CrestedSunmareToken()), TargetController.ANY, false), - new YouGainedLifeCondition(ComparisonType.MORE_THAN, 0), - "At the beginning of each end step, if you gained life this turn, create a 5/5 white Horse creature token."), - new PlayerGainedLifeWatcher()); + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new BeginningOfEndStepTriggeredAbility( + new CreateTokenEffect(new CrestedSunmareToken()), + TargetController.ANY, false + ), new YouGainedLifeCondition(ComparisonType.MORE_THAN, 0), + "At the beginning of each end step, if you gained life this turn, " + + "create a 5/5 white Horse creature token." + ).addHint(ControllerGotLifeCount.getHint()), new PlayerGainedLifeWatcher()); } private CrestedSunmare(final CrestedSunmare card) { diff --git a/Mage.Sets/src/mage/cards/c/CrimePunishment.java b/Mage.Sets/src/mage/cards/c/CrimePunishment.java index 1d8e5d4e3be7..e73445287b96 100644 --- a/Mage.Sets/src/mage/cards/c/CrimePunishment.java +++ b/Mage.Sets/src/mage/cards/c/CrimePunishment.java @@ -53,7 +53,7 @@ class PunishmentEffect extends OneShotEffect { PunishmentEffect() { super(Outcome.DestroyPermanent); - this.staticText = "Destroy each artifact, creature, and enchantment with converted mana cost X"; + this.staticText = "Destroy each artifact, creature, and enchantment with mana value X"; } PunishmentEffect(final PunishmentEffect effect) { @@ -69,7 +69,7 @@ public PunishmentEffect copy() { public boolean apply(Game game, Ability source) { for (Permanent permanent : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) { if (permanent != null - && permanent.getConvertedManaCost() == source.getManaCostsToPay().getX() + && permanent.getManaValue() == source.getManaCostsToPay().getX() && (permanent.isArtifact() || permanent.isCreature() || permanent.isEnchantment())) { diff --git a/Mage.Sets/src/mage/cards/c/CrimsonHonorGuard.java b/Mage.Sets/src/mage/cards/c/CrimsonHonorGuard.java index 7623a6161bf1..9ce3a8a9ca27 100644 --- a/Mage.Sets/src/mage/cards/c/CrimsonHonorGuard.java +++ b/Mage.Sets/src/mage/cards/c/CrimsonHonorGuard.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.CommanderPredicate; +import mage.filter.predicate.mageobject.CommanderPredicate; import mage.game.Game; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/c/CropSigil.java b/Mage.Sets/src/mage/cards/c/CropSigil.java index afce57106d8a..ec6b258ce88e 100644 --- a/Mage.Sets/src/mage/cards/c/CropSigil.java +++ b/Mage.Sets/src/mage/cards/c/CropSigil.java @@ -43,7 +43,7 @@ public CropSigil(UUID ownerId, CardSetInfo setInfo) { Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new ReturnToHandTargetEffect(true), new ManaCostsImpl<>("{2}{G}"), DeliriumCondition.instance, "Delirium — {2}{G}, Sacrifice {this}: Return up to one target creature card and up to one target land card from your graveyard to your hand. " - + "Activate this ability only if there are four or more card types among cards in your graveyard"); + + "Activate only if there are four or more card types among cards in your graveyard"); ability.addCost(new SacrificeSourceCost()); ability.addTarget(new TargetCardInYourGraveyard(0, 1, filterCreature)); ability.addTarget(new TargetCardInYourGraveyard(0, 1, filterLand)); diff --git a/Mage.Sets/src/mage/cards/c/CrownOfTheAges.java b/Mage.Sets/src/mage/cards/c/CrownOfTheAges.java index 12cf10ae33c0..2dfd51581aac 100644 --- a/Mage.Sets/src/mage/cards/c/CrownOfTheAges.java +++ b/Mage.Sets/src/mage/cards/c/CrownOfTheAges.java @@ -15,7 +15,7 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterEnchantmentPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.AttachmentAttachedToCardTypePredicate; +import mage.filter.predicate.permanent.AttachmentAttachedToCardTypePredicate; import mage.filter.predicate.permanent.PermanentIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/c/CrucibleOfWorlds.java b/Mage.Sets/src/mage/cards/c/CrucibleOfWorlds.java index f140d47247fc..e084861b33ce 100644 --- a/Mage.Sets/src/mage/cards/c/CrucibleOfWorlds.java +++ b/Mage.Sets/src/mage/cards/c/CrucibleOfWorlds.java @@ -17,7 +17,7 @@ public final class CrucibleOfWorlds extends CardImpl { public CrucibleOfWorlds(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); - // You may play land cards from your graveyard. + // You may play lands from your graveyard. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayLandsFromGraveyardControllerEffect())); } diff --git a/Mage.Sets/src/mage/cards/c/CruelFate.java b/Mage.Sets/src/mage/cards/c/CruelFate.java index 7ef7e8644809..b76e2edb42e8 100644 --- a/Mage.Sets/src/mage/cards/c/CruelFate.java +++ b/Mage.Sets/src/mage/cards/c/CruelFate.java @@ -6,10 +6,10 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.FilterCard; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; +import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetOpponent; import java.util.UUID; @@ -25,7 +25,6 @@ public CruelFate(UUID ownerId, CardSetInfo setInfo) { // Look at the top five cards of target opponent's library. Put one of those cards into that player's graveyard and the rest on top of their library in any order. this.getSpellAbility().addEffect(new CruelFateEffect()); this.getSpellAbility().addTarget(new TargetOpponent()); - } private CruelFate(final CruelFate card) { @@ -40,12 +39,13 @@ public CruelFate copy() { class CruelFateEffect extends OneShotEffect { - public CruelFateEffect() { + CruelFateEffect() { super(Outcome.DrawCard); - this.staticText = "Look at the top five cards of target opponent's library. Put one of those cards into that player's graveyard and the rest on top of their library in any order"; + this.staticText = "Look at the top five cards of target opponent's library. " + + "Put one of those cards into that player's graveyard and the rest on top of their library in any order"; } - public CruelFateEffect(final CruelFateEffect effect) { + private CruelFateEffect(final CruelFateEffect effect) { super(effect); } @@ -56,35 +56,25 @@ public CruelFateEffect copy() { @Override public boolean apply(Game game, Ability source) { - Card sourceCard = game.getCard(source.getSourceId()); - if (sourceCard != null) { - Player controller = game.getPlayer(source.getControllerId()); - Player targetOpponent = game.getPlayer(getTargetPointer().getFirst(game, source)); - if (targetOpponent != null && controller != null) { - Cards cards = new CardsImpl(targetOpponent.getLibrary().getTopCards(game, 5)); - controller.lookAtCards(source, null, cards, game); - - // card to put into opponent's graveyard - TargetCard target = new TargetCard(Zone.LIBRARY, new FilterCard("card to put into target opponent's graveyard")); - if (targetOpponent.canRespond()) { - if (cards.size() > 1) { - controller.choose(Outcome.Detriment, cards, target, game); - Card card = cards.get(target.getFirstTarget(), game); - if (card != null) { - cards.remove(card); - controller.moveCards(card, Zone.GRAVEYARD, source, game); - } - } else if (cards.size() == 1) { - Card card = cards.get(cards.iterator().next(), game); - controller.moveCards(card, Zone.GRAVEYARD, source, game); - card.moveToZone(Zone.GRAVEYARD, source, game, true); - cards.clear(); - } - } - controller.putCardsOnTopOfLibrary(cards, game, source, true); - return true; - } + Player controller = game.getPlayer(source.getControllerId()); + Player opponent = game.getPlayer(source.getFirstTarget()); + if (controller == null || opponent == null) { + return false; + } + Cards cards = new CardsImpl(opponent.getLibrary().getTopCards(game, 5)); + if (cards.isEmpty()) { + return false; + } + if (cards.size() == 1) { + return controller.moveCards(cards, Zone.GRAVEYARD, source, game); + } + TargetCard targetCard = new TargetCardInLibrary(); + controller.choose(outcome, cards, targetCard, game); + Card card = game.getCard(targetCard.getFirstTarget()); + if (card != null) { + controller.moveCards(card, Zone.GRAVEYARD, source, game); + cards.remove(card); } - return false; + return controller.putCardsOnTopOfLibrary(card, game, source, true); } } diff --git a/Mage.Sets/src/mage/cards/c/CruelReality.java b/Mage.Sets/src/mage/cards/c/CruelReality.java index 856f688834fe..7f3542764331 100644 --- a/Mage.Sets/src/mage/cards/c/CruelReality.java +++ b/Mage.Sets/src/mage/cards/c/CruelReality.java @@ -1,8 +1,7 @@ - package mage.cards.c; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BeginningOfUpkeepAttachedTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.keyword.EnchantAbility; @@ -11,22 +10,18 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; +import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; import mage.target.TargetPlayer; -import mage.target.targetpointer.FixedTarget; import java.util.UUID; /** - * * @author jeffwadsworth */ public final class CruelReality extends CardImpl { @@ -43,8 +38,7 @@ public CruelReality(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new EnchantAbility(auraTarget.getTargetName())); //At the beginning of enchanted player's upkeep, that player sacrifices a creature or planeswalker. If the player can't, they lose 5 life. - this.addAbility(new CruelRealityTriggeredAbiilty()); - + this.addAbility(new BeginningOfUpkeepAttachedTriggeredAbility(new CruelRealityEffect())); } private CruelReality(final CruelReality card) { @@ -57,55 +51,23 @@ public CruelReality copy() { } } -class CruelRealityTriggeredAbiilty extends TriggeredAbilityImpl { - - public CruelRealityTriggeredAbiilty() { - super(Zone.BATTLEFIELD, new CruelRealityEffect()); - } - - public CruelRealityTriggeredAbiilty(final CruelRealityTriggeredAbiilty ability) { - super(ability); - } - - @Override - public CruelRealityTriggeredAbiilty copy() { - return new CruelRealityTriggeredAbiilty(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.UPKEEP_STEP_PRE; - } +class CruelRealityEffect extends OneShotEffect { - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent enchantment = game.getPermanent(this.sourceId); - if (enchantment != null - && enchantment.getAttachedTo() != null) { - Player cursedPlayer = game.getPlayer(enchantment.getAttachedTo()); - if (cursedPlayer != null - && game.isActivePlayer(cursedPlayer.getId())) { - this.getEffects().get(0).setTargetPointer(new FixedTarget(cursedPlayer.getId())); - return true; - } - } - return false; - } + private static final FilterPermanent filter = new FilterControlledPermanent("creature or planeswalker"); - @Override - public String getRule() { - return "At the beginning of enchanted player's upkeep, " + super.getRule(); + static { + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + CardType.PLANESWALKER.getPredicate() + )); } -} - -class CruelRealityEffect extends OneShotEffect { - public CruelRealityEffect() { + CruelRealityEffect() { super(Outcome.LoseLife); staticText = "that player sacrifices a creature or planeswalker. If the player can't, they lose 5 life"; } - public CruelRealityEffect(final CruelRealityEffect effect) { + private CruelRealityEffect(final CruelRealityEffect effect) { super(effect); } @@ -118,24 +80,21 @@ public CruelRealityEffect copy() { public boolean apply(Game game, Ability source) { Player cursedPlayer = game.getPlayer(targetPointer.getFirst(game, source)); Player controller = game.getPlayer(source.getControllerId()); - if (cursedPlayer != null - && controller != null) { - FilterControlledPermanent filter = new FilterControlledPermanent("creature or planeswalker"); - filter.add(Predicates.or( - CardType.CREATURE.getPredicate(), - CardType.PLANESWALKER.getPredicate())); - TargetPermanent target = new TargetPermanent(filter); - if (cursedPlayer.choose(Outcome.Sacrifice, target, source.getId(), game)) { - Permanent objectToBeSacrificed = game.getPermanent(target.getFirstTarget()); - if (objectToBeSacrificed != null) { - if (objectToBeSacrificed.sacrifice(source, game)) { - return true; - } - } + if (cursedPlayer == null || controller == null) { + return false; + } + TargetPermanent target = new TargetPermanent(filter); + target.setNotTarget(true); + if (target.canChoose(source.getSourceId(), cursedPlayer.getId(), game) + && cursedPlayer.choose(Outcome.Sacrifice, target, source.getId(), game)) { + Permanent objectToBeSacrificed = game.getPermanent(target.getFirstTarget()); + if (objectToBeSacrificed != null) { + if (objectToBeSacrificed.sacrifice(source, game)) { + return true; } - cursedPlayer.loseLife(5, game, source, false); - return true; + } } - return false; + cursedPlayer.loseLife(5, game, source, false); + return true; } } diff --git a/Mage.Sets/src/mage/cards/c/CruelRevival.java b/Mage.Sets/src/mage/cards/c/CruelRevival.java index 7c53c94a4967..31f10a867558 100644 --- a/Mage.Sets/src/mage/cards/c/CruelRevival.java +++ b/Mage.Sets/src/mage/cards/c/CruelRevival.java @@ -14,6 +14,7 @@ import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCardInYourGraveyard; @@ -36,7 +37,6 @@ public final class CruelRevival extends CardImpl { public CruelRevival(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{B}"); - // Destroy target non-Zombie creature. It can't be regenerated. Return up to one target Zombie card from your graveyard to your hand. this.getSpellAbility().addEffect(new CruelRevivalEffect()); this.getSpellAbility().addTarget(new TargetPermanent(filter)); @@ -55,12 +55,13 @@ public CruelRevival copy() { class CruelRevivalEffect extends OneShotEffect { - public CruelRevivalEffect() { + CruelRevivalEffect() { super(Outcome.DestroyPermanent); - staticText = "Destroy target non-Zombie creature. It can't be regenerated. Return up to one target Zombie card from your graveyard to your hand"; + staticText = "Destroy target non-Zombie creature. It can't be regenerated. " + + "Return up to one target Zombie card from your graveyard to your hand"; } - public CruelRevivalEffect(final CruelRevivalEffect effect) { + private CruelRevivalEffect(final CruelRevivalEffect effect) { super(effect); } @@ -71,9 +72,10 @@ public boolean apply(Game game, Ability source) { targetDestroy.destroy(source, game, true); } + Player player = game.getPlayer(source.getControllerId()); Card targetRetrieve = game.getCard(source.getTargets().get(1).getFirstTarget()); - if (targetRetrieve != null) { - targetRetrieve.moveToZone(Zone.HAND, source, game, true); + if (player != null && targetRetrieve != null) { + player.moveCards(targetRetrieve, Zone.HAND, source, game); } return true; } @@ -82,4 +84,4 @@ public boolean apply(Game game, Ability source) { public CruelRevivalEffect copy() { return new CruelRevivalEffect(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/c/Crumble.java b/Mage.Sets/src/mage/cards/c/Crumble.java index 5f79f01b3fbe..629024c55489 100644 --- a/Mage.Sets/src/mage/cards/c/Crumble.java +++ b/Mage.Sets/src/mage/cards/c/Crumble.java @@ -43,7 +43,7 @@ class CrumbleEffect extends OneShotEffect { public CrumbleEffect() { super(Outcome.GainLife); - staticText = "That artifact's controller gains life equal to its converted mana cost"; + staticText = "That artifact's controller gains life equal to its mana value"; } public CrumbleEffect(final CrumbleEffect effect) { @@ -59,7 +59,7 @@ public CrumbleEffect copy() { public boolean apply(Game game, Ability source) { Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); // must use LKI if (permanent != null) { - int cost = permanent.getConvertedManaCost(); + int cost = permanent.getManaValue(); Player player = game.getPlayer(permanent.getControllerId()); if (player != null) { player.gainLife(cost, game, source); diff --git a/Mage.Sets/src/mage/cards/c/CrushingDisappointment.java b/Mage.Sets/src/mage/cards/c/CrushingDisappointment.java new file mode 100644 index 000000000000..b217ea0d8d68 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CrushingDisappointment.java @@ -0,0 +1,32 @@ +package mage.cards.c; + +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.LoseLifeAllPlayersEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CrushingDisappointment extends CardImpl { + + public CrushingDisappointment(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{B}"); + + // Each player loses 2 life. You draw two cards. + this.getSpellAbility().addEffect(new LoseLifeAllPlayersEffect(2)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2).concatBy("You")); + } + + private CrushingDisappointment(final CrushingDisappointment card) { + super(card); + } + + @Override + public CrushingDisappointment copy() { + return new CrushingDisappointment(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CryOfTheCarnarium.java b/Mage.Sets/src/mage/cards/c/CryOfTheCarnarium.java index f8a451c4f221..f6880f696e93 100644 --- a/Mage.Sets/src/mage/cards/c/CryOfTheCarnarium.java +++ b/Mage.Sets/src/mage/cards/c/CryOfTheCarnarium.java @@ -1,11 +1,13 @@ package mage.cards.c; -import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.continuous.BoostAllEffect; -import mage.cards.*; +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; @@ -67,15 +69,8 @@ public boolean apply(Game game, Ability source) { if (player == null || watcher == null) { return false; } - Cards cards = new CardsImpl(); - for (MageObjectReference mor : watcher.getCardsPutToGraveyardFromBattlefield()) { - if (game.getState().getZoneChangeCounter(mor.getSourceId()) == mor.getZoneChangeCounter()) { - Card card = mor.getCard(game); - if (card != null && card.isCreature()) { - cards.add(card); - } - } - } + Cards cards = new CardsImpl(watcher.getCardsPutToGraveyardFromBattlefield(game)); + cards.removeIf(uuid -> !game.getCard(uuid).isCreature()); player.moveCards(cards, Zone.EXILED, source, game); return true; } diff --git a/Mage.Sets/src/mage/cards/c/CryptChampion.java b/Mage.Sets/src/mage/cards/c/CryptChampion.java index 22d838e6ed1a..c3cb29d99ef0 100644 --- a/Mage.Sets/src/mage/cards/c/CryptChampion.java +++ b/Mage.Sets/src/mage/cards/c/CryptChampion.java @@ -21,8 +21,8 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.players.Player; import mage.target.Target; @@ -66,7 +66,7 @@ class CryptChampionEffect extends OneShotEffect { CryptChampionEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "each player puts a creature card with converted mana cost 3 or less from their graveyard onto the battlefield"; + this.staticText = "each player puts a creature card with mana value 3 or less from their graveyard onto the battlefield"; } CryptChampionEffect(final CryptChampionEffect effect) { @@ -86,9 +86,9 @@ public boolean apply(Game game, Ability source) { for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { Player player = game.getPlayer(playerId); if (player != null) { - FilterCard filter = new FilterCreatureCard("creature card with converted mana cost 3 or less from your graveyard"); + FilterCard filter = new FilterCreatureCard("creature card with mana value 3 or less from your graveyard"); filter.add(new OwnerIdPredicate(playerId)); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); Target target = new TargetCardInGraveyard(filter); if (target.canChoose(source.getSourceId(), playerId, game) && player.chooseTarget(outcome, target, source, game)) { diff --git a/Mage.Sets/src/mage/cards/c/CryptGhast.java b/Mage.Sets/src/mage/cards/c/CryptGhast.java index b94ffd5bb1bd..8bb21bda4e17 100644 --- a/Mage.Sets/src/mage/cards/c/CryptGhast.java +++ b/Mage.Sets/src/mage/cards/c/CryptGhast.java @@ -72,7 +72,7 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { Permanent land = game.getPermanentOrLKIBattlefield(event.getTargetId()); - return land != null && filter.match(land, this.getSourceId(), this.getControllerId(), game); + return filter.match(land, this.getSourceId(), this.getControllerId(), game); } @Override diff --git a/Mage.Sets/src/mage/cards/c/CryptIncursion.java b/Mage.Sets/src/mage/cards/c/CryptIncursion.java index bdc0f68ff4e9..68e573fa56c6 100644 --- a/Mage.Sets/src/mage/cards/c/CryptIncursion.java +++ b/Mage.Sets/src/mage/cards/c/CryptIncursion.java @@ -1,20 +1,22 @@ package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class CryptIncursion extends CardImpl { @@ -23,9 +25,8 @@ public CryptIncursion(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}"); // Exile all creature cards from target player's graveyard. You gain 3 life for each card exiled this way. - this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addEffect(new CryptIncursionEffect()); - + this.getSpellAbility().addTarget(new TargetPlayer()); } private CryptIncursion(final CryptIncursion card) { @@ -36,17 +37,17 @@ private CryptIncursion(final CryptIncursion card) { public CryptIncursion copy() { return new CryptIncursion(this); } - } class CryptIncursionEffect extends OneShotEffect { - public CryptIncursionEffect() { + CryptIncursionEffect() { super(Outcome.Detriment); - staticText = "Exile all creature cards from target player's graveyard. You gain 3 life for each card exiled this way"; + staticText = "Exile all creature cards from target player's graveyard. " + + "You gain 3 life for each card exiled this way"; } - public CryptIncursionEffect(final CryptIncursionEffect effect) { + private CryptIncursionEffect(final CryptIncursionEffect effect) { super(effect); } @@ -54,24 +55,20 @@ public CryptIncursionEffect(final CryptIncursionEffect effect) { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); Player targetPlayer = game.getPlayer(source.getFirstTarget()); - if (player != null && targetPlayer != null) { - int exiledCards = 0; - for (Card card : targetPlayer.getGraveyard().getCards(game)) { - if (StaticFilters.FILTER_CARD_CREATURE.match(card, game)) { - if (card.moveToExile(null, "", source, game)) { - exiledCards++; - } - } - } - player.gainLife(exiledCards * 3, game, source); - return true; - } - return false; + Cards cards = new CardsImpl(targetPlayer.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURE, game)); + player.moveCards(cards, Zone.EXILED, source, game); + int count = cards + .stream() + .map(game.getState()::getZone) + .map(Zone.EXILED::equals) + .mapToInt(x -> x ? 1 : 0) + .sum(); + player.gainLife(3 * count, game, source); + return true; } @Override public CryptIncursionEffect copy() { return new CryptIncursionEffect(this); } - } diff --git a/Mage.Sets/src/mage/cards/c/CrypticAnnelid.java b/Mage.Sets/src/mage/cards/c/CrypticAnnelid.java index 565a2a03dd55..28978c197c80 100644 --- a/Mage.Sets/src/mage/cards/c/CrypticAnnelid.java +++ b/Mage.Sets/src/mage/cards/c/CrypticAnnelid.java @@ -26,7 +26,7 @@ public CrypticAnnelid(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(4); // When Cryptic Annelid enters the battlefield, scry 1, then scry 2, then scry 3. - Ability ability = new EntersBattlefieldTriggeredAbility(new ScryEffect(1)); + Ability ability = new EntersBattlefieldTriggeredAbility(new ScryEffect(1).setText("scry 1")); Effect effect = new ScryEffect(2); effect.setText(", then scry 2"); ability.addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/c/Cryptoplasm.java b/Mage.Sets/src/mage/cards/c/Cryptoplasm.java index 2fd5135608fa..c9659f99e2f7 100644 --- a/Mage.Sets/src/mage/cards/c/Cryptoplasm.java +++ b/Mage.Sets/src/mage/cards/c/Cryptoplasm.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/c/Cryptwailing.java b/Mage.Sets/src/mage/cards/c/Cryptwailing.java index 047814b46e41..875605718946 100644 --- a/Mage.Sets/src/mage/cards/c/Cryptwailing.java +++ b/Mage.Sets/src/mage/cards/c/Cryptwailing.java @@ -10,7 +10,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.target.TargetPlayer; import mage.target.common.TargetCardInYourGraveyard; @@ -25,7 +25,7 @@ public Cryptwailing(UUID ownerId, CardSetInfo setInfo) { // {1}, Exile two creature cards from your graveyard: Target player discards a card. Activate this ability only any time you could cast a sorcery. ActivateAsSorceryActivatedAbility ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new DiscardTargetEffect(1), new GenericManaCost(1)); - ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(2,2, new FilterCreatureCard("two creature cards from your graveyard")))); + ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(2, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD))); ability.addTarget(new TargetPlayer()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/CrystalShard.java b/Mage.Sets/src/mage/cards/c/CrystalShard.java index 1884a1ec2ab5..27c9083702eb 100644 --- a/Mage.Sets/src/mage/cards/c/CrystalShard.java +++ b/Mage.Sets/src/mage/cards/c/CrystalShard.java @@ -1,10 +1,10 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.CompositeCost; import mage.abilities.costs.Cost; +import mage.abilities.costs.OrCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCostsImpl; @@ -19,22 +19,25 @@ import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class CrystalShard extends CardImpl { public CrystalShard(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); // {3}, {tap} or {U}, {tap}: Return target creature to its owner's hand unless its controller pays {1}. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CrystalShardEffect(new GenericManaCost(1)), new ManaCostsImpl("{3}")); - ability.addCost(new TapSourceCost()); - ability.addTarget(new TargetCreaturePermanent()); - this.addAbility(ability); - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CrystalShardEffect(new GenericManaCost(1)), new ManaCostsImpl("{U}")); - ability.addCost(new TapSourceCost()); + Ability ability = new SimpleActivatedAbility( + new CrystalShardEffect(), + new OrCost( + new CompositeCost(new GenericManaCost(3), new TapSourceCost(), ""), + new CompositeCost(new ManaCostsImpl("{U}"), new TapSourceCost(), ""), + "{3}, {T} or {U}, {T}" + ) + ); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } @@ -51,17 +54,13 @@ public CrystalShard copy() { class CrystalShardEffect extends OneShotEffect { - protected Cost cost; - - public CrystalShardEffect(Cost cost) { + CrystalShardEffect() { super(Outcome.Detriment); - this.staticText = "Return target creature to its owner's hand unless its controller pays {1}"; - this.cost = cost; + this.staticText = "return target creature to its owner's hand unless its controller pays {1}"; } - public CrystalShardEffect(final CrystalShardEffect effect) { + private CrystalShardEffect(final CrystalShardEffect effect) { super(effect); - this.cost = effect.cost.copy(); } @Override @@ -72,23 +71,19 @@ public CrystalShardEffect copy() { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (targetCreature != null) { - Player player = game.getPlayer(targetCreature.getControllerId()); - if (player != null) { - cost.clearPaid(); - final StringBuilder sb = new StringBuilder("Pay {1}? (Otherwise ").append(targetCreature.getName()).append(" will be returned to its owner's hand)"); - if (player.chooseUse(Outcome.Benefit, sb.toString(), source, game)) { - cost.pay(source, game, source, targetCreature.getControllerId(), false, null); - } - if (!cost.isPaid()) { - controller.moveCards(targetCreature, Zone.HAND, source, game); - } - } - } + Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (controller == null || targetCreature == null) { return true; } - return false; + Player player = game.getPlayer(targetCreature.getControllerId()); + if (player == null) { + return true; + } + Cost cost = new GenericManaCost(1); + String message = "Pay {1}? (Otherwise " + targetCreature.getName() + " will be returned to its owner's hand)"; + if (player.chooseUse(Outcome.Benefit, message, source, game)) { + cost.pay(source, game, source, targetCreature.getControllerId(), false, null); + } + return cost.isPaid() || controller.moveCards(targetCreature, Zone.HAND, source, game); } } diff --git a/Mage.Sets/src/mage/cards/c/CrystallineResonance.java b/Mage.Sets/src/mage/cards/c/CrystallineResonance.java index 828f18ae8e29..8167b56cd064 100644 --- a/Mage.Sets/src/mage/cards/c/CrystallineResonance.java +++ b/Mage.Sets/src/mage/cards/c/CrystallineResonance.java @@ -10,7 +10,7 @@ import mage.constants.Duration; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.target.TargetPermanent; import mage.util.functions.CopyApplier; diff --git a/Mage.Sets/src/mage/cards/c/CullingRitual.java b/Mage.Sets/src/mage/cards/c/CullingRitual.java new file mode 100644 index 000000000000..4d1f414ecfbb --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CullingRitual.java @@ -0,0 +1,92 @@ +package mage.cards.c; + +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.ManaType; +import mage.constants.Outcome; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterNonlandPermanent; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CullingRitual extends CardImpl { + + public CullingRitual(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}{G}"); + + // Destroy each nonland permanent with mana value 2 or less. Add {B} or {G} for each permanent destroyed this way. + this.getSpellAbility().addEffect(new CullingRitualEffect()); + } + + private CullingRitual(final CullingRitual card) { + super(card); + } + + @Override + public CullingRitual copy() { + return new CullingRitual(this); + } +} + +class CullingRitualEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterNonlandPermanent(); + + static { + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); + } + + CullingRitualEffect() { + super(Outcome.Benefit); + staticText = "destroy each nonland permanent with mana value 2 or less. " + + "Add {B} or {G} for each permanent destroyed this way"; + } + + private CullingRitualEffect(final CullingRitualEffect effect) { + super(effect); + } + + @Override + public CullingRitualEffect copy() { + return new CullingRitualEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + int counter = 0; + for (Permanent permanent : game.getBattlefield().getActivePermanents( + filter, source.getControllerId(), source.getSourceId(), game + )) { + counter += permanent.destroy(source, game, false) ? 1 : 0; + } + if (counter == 0) { + return false; + } + int black = player.getAmount( + 0, counter, counter + " permanents were destroyed, " + + "choose the amount of black mana to produce (the rest will be green)", game + ); + Mana mana = new Mana(ManaType.BLACK, black); + if (black < counter) { + mana.add(new Mana(ManaType.GREEN, counter - black)); + } + player.getManaPool().addMana(mana, game, source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CullingScales.java b/Mage.Sets/src/mage/cards/c/CullingScales.java index 942c3a0c5382..0d3922d4a016 100644 --- a/Mage.Sets/src/mage/cards/c/CullingScales.java +++ b/Mage.Sets/src/mage/cards/c/CullingScales.java @@ -12,7 +12,7 @@ import mage.filter.common.FilterNonlandPermanent; import mage.filter.predicate.ObjectSourcePlayer; import mage.filter.predicate.ObjectSourcePlayerPredicate; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; @@ -25,7 +25,7 @@ public final class CullingScales extends CardImpl { private static final FilterPermanent filterNonlandPermanentWithLowestCmc = new FilterNonlandPermanent( - "nonland permanent with the lowest converted mana cost (If two or more permanents are tied for lowest cost, target any one of them.)" + "nonland permanent with the lowest mana value (If two or more permanents are tied for lowest cost, target any one of them.)" ); static { @@ -57,7 +57,7 @@ class HasLowestCMCAmongstNonlandPermanentsPredicate implements ObjectSourcePlaye @Override public boolean apply(ObjectSourcePlayer input, Game game) { FilterPermanent filter = new FilterNonlandPermanent(); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, input.getObject().getConvertedManaCost())); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, input.getObject().getManaValue())); return !game.getBattlefield().contains(filter, input.getSourceId(), input.getPlayerId(), game, 1); } diff --git a/Mage.Sets/src/mage/cards/c/CullingSun.java b/Mage.Sets/src/mage/cards/c/CullingSun.java index 9472e71afbd4..7ccf609304a9 100644 --- a/Mage.Sets/src/mage/cards/c/CullingSun.java +++ b/Mage.Sets/src/mage/cards/c/CullingSun.java @@ -8,17 +8,17 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; /** * * @author Loki */ public final class CullingSun extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with converted mana cost 3 or less"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with mana value 3 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public CullingSun(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/c/CulminationOfStudies.java b/Mage.Sets/src/mage/cards/c/CulminationOfStudies.java new file mode 100644 index 000000000000..2e75841878b1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CulminationOfStudies.java @@ -0,0 +1,95 @@ +package mage.cards.c; + +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.game.permanent.token.TreasureToken; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CulminationOfStudies extends CardImpl { + + public CulminationOfStudies(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{U}{R}"); + + // Exile the top X cards of your library. For each land card exiled this way, create a Treasure token. For each blue card exiled this way, draw a card. For each red card exiled this way, Culmination of Studies deals 1 damage to each opponent. + this.getSpellAbility().addEffect(new CulminationOfStudiesEffect()); + } + + private CulminationOfStudies(final CulminationOfStudies card) { + super(card); + } + + @Override + public CulminationOfStudies copy() { + return new CulminationOfStudies(this); + } +} + +class CulminationOfStudiesEffect extends OneShotEffect { + + private static final FilterCard filterBlue = new FilterCard(); + private static final FilterCard filterRed = new FilterCard(); + + static { + filterBlue.add(new ColorPredicate(ObjectColor.BLUE)); + filterRed.add(new ColorPredicate(ObjectColor.RED)); + } + + CulminationOfStudiesEffect() { + super(Outcome.Benefit); + staticText = "exile the top X cards of your library. For each land card exiled this way, " + + "create a Treasure token. For each blue card exiled this way, draw a card. " + + "For each red card exiled this way, {this} deals 1 damage to each opponent"; + } + + private CulminationOfStudiesEffect(final CulminationOfStudiesEffect effect) { + super(effect); + } + + @Override + public CulminationOfStudiesEffect copy() { + return new CulminationOfStudiesEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, source.getManaCostsToPay().getX())); + player.moveCards(cards, Zone.EXILED, source, game); + cards.removeIf(uuid -> game.getState().getZone(uuid) != Zone.EXILED); + int landCards = cards.count(StaticFilters.FILTER_CARD_LAND, game); + int blueCards = cards.count(filterBlue, game); + int redCards = cards.count(filterRed, game); + if (landCards > 0) { + new TreasureToken().putOntoBattlefield(landCards, game, source, source.getControllerId()); + } + if (blueCards > 0) { + player.drawCards(blueCards, source, game); + } + if (redCards > 0) { + new DamagePlayersEffect(redCards, TargetController.OPPONENT).apply(game, source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CultOfTheWaxingMoon.java b/Mage.Sets/src/mage/cards/c/CultOfTheWaxingMoon.java index 4673d42bba50..a0282606bdd8 100644 --- a/Mage.Sets/src/mage/cards/c/CultOfTheWaxingMoon.java +++ b/Mage.Sets/src/mage/cards/c/CultOfTheWaxingMoon.java @@ -73,7 +73,7 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { Permanent permanent = game.getPermanent(event.getTargetId()); - return permanent != null && filter.match(permanent, getSourceId(), getControllerId(), game); + return filter.match(permanent, getSourceId(), getControllerId(), game); } @Override diff --git a/Mage.Sets/src/mage/cards/c/Cultivate.java b/Mage.Sets/src/mage/cards/c/Cultivate.java index f970b20af49f..579ebda3c3d6 100644 --- a/Mage.Sets/src/mage/cards/c/Cultivate.java +++ b/Mage.Sets/src/mage/cards/c/Cultivate.java @@ -45,12 +45,13 @@ class CultivateEffect extends OneShotEffect { protected static final FilterCard filter = new FilterCard("card to put on the battlefield tapped"); - public CultivateEffect() { + CultivateEffect() { super(Outcome.PutLandInPlay); - staticText = "Search your library for up to two basic land cards, reveal those cards, and put one onto the battlefield tapped and the other into your hand. Then shuffle your library"; + staticText = "search your library for up to two basic land cards, reveal those cards, " + + "put one onto the battlefield tapped and the other into your hand, then shuffle"; } - public CultivateEffect(final CultivateEffect effect) { + private CultivateEffect(final CultivateEffect effect) { super(effect); } diff --git a/Mage.Sets/src/mage/cards/c/CunningGeysermage.java b/Mage.Sets/src/mage/cards/c/CunningGeysermage.java index 523d21e587e3..c7ce4e0003a2 100644 --- a/Mage.Sets/src/mage/cards/c/CunningGeysermage.java +++ b/Mage.Sets/src/mage/cards/c/CunningGeysermage.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/c/CunningRhetoric.java b/Mage.Sets/src/mage/cards/c/CunningRhetoric.java new file mode 100644 index 000000000000..e373cdbce56b --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CunningRhetoric.java @@ -0,0 +1,117 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.OneShotEffect; +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.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CunningRhetoric extends CardImpl { + + public CunningRhetoric(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); + + // Whenever an opponent attacks you and/or one or more planeswalkers you control, exile the top card of that player's library. You may play that card for as long as it remains exiled, and you may spend mana as though it were mana of any color to cast it. + this.addAbility(new CunningRhetoricTriggeredAbility()); + } + + private CunningRhetoric(final CunningRhetoric card) { + super(card); + } + + @Override + public CunningRhetoric copy() { + return new CunningRhetoric(this); + } +} + +class CunningRhetoricTriggeredAbility extends TriggeredAbilityImpl { + + public CunningRhetoricTriggeredAbility() { + super(Zone.BATTLEFIELD, new CunningRhetoricEffect(), false); + } + + public CunningRhetoricTriggeredAbility(final CunningRhetoricTriggeredAbility ability) { + super(ability); + } + + @Override + public CunningRhetoricTriggeredAbility copy() { + return new CunningRhetoricTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + UUID playerId = game.getCombat() + .getAttackers() + .stream() + .filter(attacker -> isControlledBy(game.getCombat().getDefendingPlayerId(attacker, game))) + .map(game::getControllerId) + .filter(game.getOpponents(getControllerId())::contains) + .findFirst() + .orElse(null); + if (playerId == null) { + return false; + } + getEffects().setTargetPointer(new FixedTarget(playerId)); + return true; + } + + @Override + public String getRule() { + return "Whenever an opponent attacks you and/or one or more planeswalkers you control, " + + "exile the top card of that player's library. You may play that card for as long " + + "as it remains exiled, and you may spend mana as though it were mana of any color to cast it."; + } +} + +class CunningRhetoricEffect extends OneShotEffect { + + CunningRhetoricEffect() { + super(Outcome.Benefit); + } + + private CunningRhetoricEffect(final CunningRhetoricEffect effect) { + super(effect); + } + + @Override + public CunningRhetoricEffect copy() { + return new CunningRhetoricEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player opponent = game.getPlayer(getTargetPointer().getFirst(game, source)); + if (opponent == null) { + return false; + } + Card card = opponent.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + opponent.moveCards(card, Zone.EXILED, source, game); + CardUtil.makeCardPlayable(game, source, card, Duration.Custom, true); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CunningWish.java b/Mage.Sets/src/mage/cards/c/CunningWish.java index cae07012f3ff..cc61a71f38af 100644 --- a/Mage.Sets/src/mage/cards/c/CunningWish.java +++ b/Mage.Sets/src/mage/cards/c/CunningWish.java @@ -28,7 +28,7 @@ public CunningWish(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new WishEffect(filter)); // Exile Cunning Wish. - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private CunningWish(final CunningWish card) { diff --git a/Mage.Sets/src/mage/cards/c/Curate.java b/Mage.Sets/src/mage/cards/c/Curate.java new file mode 100644 index 000000000000..0f171aa3bc04 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/Curate.java @@ -0,0 +1,77 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Curate extends CardImpl { + + public Curate(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); + + // Look at the top two cards of your library. Put any number of them into your graveyard and the rest back on top of your library in any order. + // Draw a card. + this.getSpellAbility().addEffect(new CurateEffect()); + } + + private Curate(final Curate card) { + super(card); + } + + @Override + public Curate copy() { + return new Curate(this); + } +} + +class CurateEffect extends OneShotEffect { + + CurateEffect() { + super(Outcome.Benefit); + staticText = "look at the top two cards of your library. Put any number of them into your graveyard " + + "and the rest back on top of your library in any order.
Draw a card"; + } + + private CurateEffect(final CurateEffect effect) { + super(effect); + } + + @Override + public CurateEffect copy() { + return new CurateEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 2)); + if (!cards.isEmpty()) { + TargetCard target = new TargetCard(0, cards.size(), Zone.LIBRARY, StaticFilters.FILTER_CARD); + target.withChooseHint("to graveyard"); + player.chooseTarget(Outcome.Benefit, cards, target, source, game); + player.moveCards(new CardsImpl(target.getTargets()), Zone.GRAVEYARD, source, game); + cards.removeAll(target.getTargets()); + player.putCardsOnTopOfLibrary(cards, game, source, true); + } + player.drawCards(1, source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CuriosityCrafter.java b/Mage.Sets/src/mage/cards/c/CuriosityCrafter.java new file mode 100644 index 000000000000..965825568df4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CuriosityCrafter.java @@ -0,0 +1,65 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SetTargetPointer; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.TokenPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CuriosityCrafter extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("a creature token you control"); + + static { + filter.add(TokenPredicate.instance); + } + + public CuriosityCrafter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.BIRD); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // You have no maximum hand size. + this.addAbility(new SimpleStaticAbility(new MaximumHandSizeControllerEffect( + Integer.MAX_VALUE, Duration.WhileOnBattlefield, + MaximumHandSizeControllerEffect.HandSizeModification.SET + ))); + + // Whenever a creature token you control deals combat damage to a player, draw a card. + this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility( + new DrawCardSourceControllerEffect(1), filter, + false, SetTargetPointer.NONE, true + )); + } + + private CuriosityCrafter(final CuriosityCrafter card) { + super(card); + } + + @Override + public CuriosityCrafter copy() { + return new CuriosityCrafter(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CurseOfEchoes.java b/Mage.Sets/src/mage/cards/c/CurseOfEchoes.java index 77516a6b5d34..9675b80039f4 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfEchoes.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfEchoes.java @@ -85,7 +85,7 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && (spell.isInstant() || spell.isSorcery())) { + if (spell != null && spell.isInstantOrSorcery()) { Permanent enchantment = game.getPermanent(sourceId); if (enchantment != null && enchantment.getAttachedTo() != null) { Player player = game.getPlayer(enchantment.getAttachedTo()); diff --git a/Mage.Sets/src/mage/cards/c/CurseOfMisfortunes.java b/Mage.Sets/src/mage/cards/c/CurseOfMisfortunes.java index 671ddd4a0330..4a5d4dede13c 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfMisfortunes.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfMisfortunes.java @@ -61,7 +61,7 @@ class CurseOfMisfortunesEffect extends OneShotEffect { public CurseOfMisfortunesEffect() { super(Outcome.Detriment); - staticText = "you may search your library for a Curse card that doesn't have the same name as a Curse attached to enchanted player, put it onto the battlefield attached to that player, then shuffle your library"; + staticText = "you may search your library for a Curse card that doesn't have the same name as a Curse attached to enchanted player, put it onto the battlefield attached to that player, then shuffle"; } public CurseOfMisfortunesEffect(final CurseOfMisfortunesEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/CurseOfOblivion.java b/Mage.Sets/src/mage/cards/c/CurseOfOblivion.java index b593c88d80ef..62eec2c2e5c1 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfOblivion.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfOblivion.java @@ -1,8 +1,7 @@ - package mage.cards.c; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BeginningOfUpkeepAttachedTriggeredAbility; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.ExileFromZoneTargetEffect; import mage.abilities.keyword.EnchantAbility; @@ -12,19 +11,12 @@ import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.filter.StaticFilters; import mage.target.TargetPlayer; -import mage.target.targetpointer.FixedTarget; import java.util.UUID; /** - * * @author BetaSteward */ public final class CurseOfOblivion extends CardImpl { @@ -41,7 +33,9 @@ public CurseOfOblivion(UUID ownerId, CardSetInfo setInfo) { this.addAbility(ability); // At the beginning of enchanted player's upkeep, that player exiles two cards from their graveyard. - this.addAbility(new CurseOfOblivionAbility()); + this.addAbility(new BeginningOfUpkeepAttachedTriggeredAbility(new ExileFromZoneTargetEffect( + Zone.GRAVEYARD, StaticFilters.FILTER_CARD_CARDS, 2, false + ).setText("that player exiles two cards from their graveyard"))); } private CurseOfOblivion(final CurseOfOblivion card) { @@ -53,43 +47,3 @@ public CurseOfOblivion copy() { return new CurseOfOblivion(this); } } - -class CurseOfOblivionAbility extends TriggeredAbilityImpl { - - public CurseOfOblivionAbility() { - super(Zone.BATTLEFIELD, new ExileFromZoneTargetEffect(Zone.GRAVEYARD, null, "", new FilterCard(), 2)); - } - - public CurseOfOblivionAbility(final CurseOfOblivionAbility ability) { - super(ability); - } - - @Override - public CurseOfOblivionAbility copy() { - return new CurseOfOblivionAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.UPKEEP_STEP_PRE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent enchantment = game.getPermanent(this.sourceId); - if (enchantment != null && enchantment.getAttachedTo() != null) { - Player player = game.getPlayer(enchantment.getAttachedTo()); - if (player != null && game.isActivePlayer(player.getId())) { - this.getEffects().get(0).setTargetPointer(new FixedTarget(player.getId())); - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "At the beginning of enchanted player's upkeep, that player exiles two cards from their graveyard."; - } - -} diff --git a/Mage.Sets/src/mage/cards/c/CurseOfTheBloodyTome.java b/Mage.Sets/src/mage/cards/c/CurseOfTheBloodyTome.java index c79b826847fe..9803b34953ec 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfTheBloodyTome.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfTheBloodyTome.java @@ -1,7 +1,7 @@ package mage.cards.c; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BeginningOfUpkeepAttachedTriggeredAbility; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.PutLibraryIntoGraveTargetEffect; import mage.abilities.keyword.EnchantAbility; @@ -10,19 +10,11 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.TargetPlayer; -import mage.target.targetpointer.FixedTarget; import java.util.UUID; /** - * * @author Alvin */ public final class CurseOfTheBloodyTome extends CardImpl { @@ -39,8 +31,9 @@ public CurseOfTheBloodyTome(UUID ownerId, CardSetInfo setInfo) { this.addAbility(ability); // At the beginning of enchanted player's upkeep, that player puts the top two cards of their library into their graveyard. - this.addAbility(new CurseOfTheBloodyTomeAbility()); - + this.addAbility(new BeginningOfUpkeepAttachedTriggeredAbility( + new PutLibraryIntoGraveTargetEffect(2).setText("that player mills two cards") + )); } private CurseOfTheBloodyTome(final CurseOfTheBloodyTome card) { @@ -52,42 +45,3 @@ public CurseOfTheBloodyTome copy() { return new CurseOfTheBloodyTome(this); } } - -class CurseOfTheBloodyTomeAbility extends TriggeredAbilityImpl { - - public CurseOfTheBloodyTomeAbility() { - super(Zone.BATTLEFIELD, new PutLibraryIntoGraveTargetEffect(2)); - } - - public CurseOfTheBloodyTomeAbility(final CurseOfTheBloodyTomeAbility ability) { - super(ability); - } - - @Override - public CurseOfTheBloodyTomeAbility copy() { - return new CurseOfTheBloodyTomeAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.UPKEEP_STEP_PRE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent enchantment = game.getPermanent(this.sourceId); - if (enchantment != null && enchantment.getAttachedTo() != null) { - Player player = game.getPlayer(enchantment.getAttachedTo()); - if (player != null && game.isActivePlayer(player.getId())) { - this.getEffects().get(0).setTargetPointer(new FixedTarget(player.getId())); - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "At the beginning of enchanted player's upkeep, that player mills two cards."; - } -} diff --git a/Mage.Sets/src/mage/cards/c/CurseOfThePiercedHeart.java b/Mage.Sets/src/mage/cards/c/CurseOfThePiercedHeart.java index 615ff1880b8e..d12b08f91aee 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfThePiercedHeart.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfThePiercedHeart.java @@ -1,7 +1,7 @@ package mage.cards.c; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BeginningOfUpkeepAttachedTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.keyword.EnchantAbility; @@ -10,17 +10,15 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterPlaneswalkerPermanent; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; import mage.target.TargetPlayer; -import mage.target.targetpointer.FixedTarget; import java.util.UUID; @@ -41,7 +39,7 @@ public CurseOfThePiercedHeart(UUID ownerId, CardSetInfo setInfo) { this.addAbility(ability); // At the beginning of enchanted player's upkeep, Curse of the Pierced Heart deals 1 damage to that player. - this.addAbility(new CurseOfThePiercedHeartAbility()); + this.addAbility(new BeginningOfUpkeepAttachedTriggeredAbility(new CurseOfThePiercedHeartEffect())); } private CurseOfThePiercedHeart(final CurseOfThePiercedHeart card) { @@ -54,55 +52,14 @@ public CurseOfThePiercedHeart copy() { } } -class CurseOfThePiercedHeartAbility extends TriggeredAbilityImpl { - - public CurseOfThePiercedHeartAbility() { - super(Zone.BATTLEFIELD, new CurseOfThePiercedHeartEffect()); - } - - public CurseOfThePiercedHeartAbility(final CurseOfThePiercedHeartAbility ability) { - super(ability); - } - - @Override - public CurseOfThePiercedHeartAbility copy() { - return new CurseOfThePiercedHeartAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.UPKEEP_STEP_PRE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent enchantment = game.getPermanent(this.sourceId); - if (enchantment != null && enchantment.getAttachedTo() != null) { - Player player = game.getPlayer(enchantment.getAttachedTo()); - if (player != null && game.isActivePlayer(player.getId())) { - this.getEffects().get(0).setTargetPointer(new FixedTarget(player.getId())); - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "At the beginning of enchanted player's upkeep, " - + "{this} deals 1 damage to that player or a planeswalker that player controls."; - } - -} - class CurseOfThePiercedHeartEffect extends OneShotEffect { - public CurseOfThePiercedHeartEffect() { + CurseOfThePiercedHeartEffect() { super(Outcome.Damage); this.staticText = "{this} deals 1 damage to that player or a planeswalker that player controls"; } - public CurseOfThePiercedHeartEffect(final CurseOfThePiercedHeartEffect effect) { + private CurseOfThePiercedHeartEffect(final CurseOfThePiercedHeartEffect effect) { super(effect); } @@ -114,29 +71,23 @@ public CurseOfThePiercedHeartEffect copy() { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent enchantment = game.getPermanent(source.getSourceId()); - if (controller == null || enchantment == null) { + Player opponent = game.getPlayer(targetPointer.getFirst(game, source)); + if (controller == null || opponent == null) { return false; } - UUID opponentId = enchantment.getAttachedTo(); - Player opponent = game.getPlayer(opponentId); - if (opponent == null) { - return false; + if (game.getBattlefield().count(StaticFilters.FILTER_CONTROLLED_PERMANENT_PLANESWALKER, source.getSourceId(), opponent.getId(), game) < 1 + || !controller.chooseUse(Outcome.Damage, "Redirect to a planeswalker controlled by " + opponent.getLogName() + "?", source, game)) { + return opponent.damage(1, source.getSourceId(), source, game) > 0; } - if (!game.getBattlefield().getAllActivePermanents(new FilterPlaneswalkerPermanent(), opponentId, game).isEmpty()) { - if (controller.chooseUse(Outcome.Damage, "Redirect to a planeswalker controlled by " + opponent.getLogName() + "?", source, game)) { - FilterPlaneswalkerPermanent filter = new FilterPlaneswalkerPermanent("a planeswalker controlled by " + opponent.getLogName()); - filter.add(new ControllerIdPredicate(opponentId)); - TargetPermanent target = new TargetPermanent(1, 1, filter, false); - if (target.choose(Outcome.Damage, controller.getId(), source.getSourceId(), game)) { - Permanent permanent = game.getPermanent(target.getFirstTarget()); - if (permanent != null) { - return permanent.damage(1, source.getSourceId(), source, game, false, true) > 0; - } - } - } + FilterPermanent filter = new FilterPlaneswalkerPermanent("a planeswalker controlled by " + opponent.getLogName()); + filter.add(new ControllerIdPredicate(opponent.getId())); + TargetPermanent target = new TargetPermanent(filter); + target.setNotTarget(true); + controller.choose(outcome, target, source.getSourceId(), game); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent != null) { + return permanent.damage(1, source.getSourceId(), source, game, false, true) > 0; } - opponent.damage(1, source.getSourceId(), source, game); - return true; + return opponent.damage(1, source.getSourceId(), source, game) > 0; } } diff --git a/Mage.Sets/src/mage/cards/c/CurseOfTheSwine.java b/Mage.Sets/src/mage/cards/c/CurseOfTheSwine.java index d9d389367ec0..47a4b15b4150 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfTheSwine.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfTheSwine.java @@ -17,6 +17,9 @@ import java.util.HashMap; import java.util.Map; import java.util.UUID; +import mage.cards.Card; +import mage.cards.Cards; +import mage.cards.CardsImpl; /** * @author LevelX2 @@ -74,7 +77,41 @@ public CurseOfTheSwineEffect copy() { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); + Cards creaturesToExile = new CardsImpl(); if (controller != null) { + Map playersWithTargets = new HashMap<>(); + for (UUID targetId : this.getTargetPointer().getTargets(game, source)) { + Permanent creature = game.getPermanent(targetId); + if (creature != null) { + creaturesToExile.add(creature); + } + } + + // move creatures to exile all at once + controller.moveCards(creaturesToExile, Zone.EXILED, source, game); + + // Count all creatures actually exiled and add them to the player's count + for (Card card : creaturesToExile.getCards(game)) { + Permanent lkiP = game.getPermanentOrLKIBattlefield(card.getId()); + if (lkiP != null + && game.getState().getZone(lkiP.getId()) == Zone.EXILED) { + playersWithTargets.put(lkiP.getControllerId(), + playersWithTargets.getOrDefault(lkiP.getControllerId(), 0) + 1); + } + } + Boar2Token swineToken = new Boar2Token(); + for (Map.Entry exiledByController : playersWithTargets.entrySet()) { + swineToken.putOntoBattlefield(exiledByController.getValue(), game, source, exiledByController.getKey()); + } + return true; + } + return false; + } +} + + +/* +if (controller != null) { Map playersWithTargets = new HashMap<>(); for (UUID targetId : this.getTargetPointer().getTargets(game, source)) { Permanent creature = game.getPermanent(targetId); @@ -91,7 +128,8 @@ public boolean apply(Game game, Ability source) { } return true; } - return false; - } -} + + + +*/ diff --git a/Mage.Sets/src/mage/cards/c/CurseOfThirst.java b/Mage.Sets/src/mage/cards/c/CurseOfThirst.java index d4e436e4309c..bffb0a1ea664 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfThirst.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfThirst.java @@ -1,7 +1,7 @@ package mage.cards.c; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BeginningOfUpkeepAttachedTriggeredAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; @@ -12,19 +12,14 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPlayer; -import mage.target.targetpointer.FixedTarget; +import java.util.Objects; import java.util.UUID; /** - * * @author BetaSteward */ public final class CurseOfThirst extends CardImpl { @@ -40,8 +35,10 @@ public CurseOfThirst(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new EnchantAbility(auraTarget.getTargetName())); // At the beginning of enchanted player's upkeep, Curse of Thirst deals damage to that player equal to the number of Curses attached to them. - this.addAbility(new CurseOfThirstAbility()); - + this.addAbility(new BeginningOfUpkeepAttachedTriggeredAbility( + new DamageTargetEffect(CursesAttachedCount.instance) + .setText("{this} deals damage to that player equal to the number of Curses attached to them") + )); } private CurseOfThirst(final CurseOfThirst card) { @@ -54,73 +51,28 @@ public CurseOfThirst copy() { } } -class CurseOfThirstAbility extends TriggeredAbilityImpl { - - public CurseOfThirstAbility() { - super(Zone.BATTLEFIELD, new DamageTargetEffect(new CursesAttachedCount())); - } - - public CurseOfThirstAbility(final CurseOfThirstAbility ability) { - super(ability); - } - - @Override - public CurseOfThirstAbility copy() { - return new CurseOfThirstAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.UPKEEP_STEP_PRE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent enchantment = game.getPermanent(this.sourceId); - if (enchantment != null && enchantment.getAttachedTo() != null) { - Player player = game.getPlayer(enchantment.getAttachedTo()); - if (player != null && game.isActivePlayer(player.getId())) { - this.getEffects().get(0).setTargetPointer(new FixedTarget(player.getId())); - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "At the beginning of enchanted player's upkeep, Curse of Thirst " - + "deals damage to that player equal to the number of Curses attached to them."; - } - -} - -class CursesAttachedCount implements DynamicValue { - - public CursesAttachedCount() { - } +enum CursesAttachedCount implements DynamicValue { + instance; @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { - int count = 0; - Permanent enchantment = game.getPermanent(sourceAbility.getSourceId()); - if (enchantment != null && enchantment.getAttachedTo() != null) { - Player player = game.getPlayer(enchantment.getAttachedTo()); - if (player != null) { - for (UUID attachmentId : player.getAttachments()) { - Permanent attachment = game.getPermanent(attachmentId); - if (attachment != null && attachment.hasSubtype(SubType.CURSE, game)) { - count++; - } - } - } + Player player = game.getPlayer(effect.getTargetPointer().getFirst(game, sourceAbility)); + if (player == null) { + return 0; } - return count; + return player + .getAttachments() + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .map(p -> p.hasSubtype(SubType.CURSE, game)) + .mapToInt(x -> x ? 1 : 0) + .sum(); } @Override - public DynamicValue copy() { - return new CursesAttachedCount(); + public CursesAttachedCount copy() { + return instance; } @Override diff --git a/Mage.Sets/src/mage/cards/c/CursedMirror.java b/Mage.Sets/src/mage/cards/c/CursedMirror.java new file mode 100644 index 000000000000..58618bc25ea0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CursedMirror.java @@ -0,0 +1,54 @@ +package mage.cards.c; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.effects.common.CopyPermanentEffect; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.mana.RedManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.game.Game; +import mage.util.functions.CopyApplier; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CursedMirror extends CardImpl { + + public CursedMirror(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{R}"); + + // {T}: Add {R}. + this.addAbility(new RedManaAbility()); + + // As Cursed Mirror enters the battlefield, you may have it become a copy of any creature on the battlefield until end of turn, except it has haste. + this.addAbility(new EntersBattlefieldAbility( + new CopyPermanentEffect(new CursedMirrorCopyApplier()).setDuration(Duration.EndOfTurn), + true, null, "As {this} enters the battlefield, you may have it " + + "become a copy of any creature on the battlefield until end of turn, except it has haste.", "" + )); + } + + private CursedMirror(final CursedMirror card) { + super(card); + } + + @Override + public CursedMirror copy() { + return new CursedMirror(this); + } +} + +class CursedMirrorCopyApplier extends CopyApplier { + + @Override + public boolean apply(Game game, MageObject blueprint, Ability source, UUID copyToObjectId) { + blueprint.getAbilities().add(HasteAbility.getInstance()); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CustodiSoulbinders.java b/Mage.Sets/src/mage/cards/c/CustodiSoulbinders.java index b73d347359ac..4e6393782471 100644 --- a/Mage.Sets/src/mage/cards/c/CustodiSoulbinders.java +++ b/Mage.Sets/src/mage/cards/c/CustodiSoulbinders.java @@ -16,7 +16,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.SpiritWhiteToken; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/c/CustodiSoulcaller.java b/Mage.Sets/src/mage/cards/c/CustodiSoulcaller.java index 7d6acc55677c..96697093d0d2 100644 --- a/Mage.Sets/src/mage/cards/c/CustodiSoulcaller.java +++ b/Mage.Sets/src/mage/cards/c/CustodiSoulcaller.java @@ -15,10 +15,9 @@ import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetadjustment.TargetAdjuster; @@ -42,9 +41,8 @@ public CustodiSoulcaller(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new MeleeAbility()); // Whenever Custodi Soulcaller attacks, return target creature card with converted mana cost X or less from your graveyard to the battlefield, where X is the number of players you attacked with a creature this combat. - Ability ability = new AttacksTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(), false); + Ability ability = new AttacksTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect().setText("return target creature card with mana value X or less from your graveyard to the battlefield, where X is the number of players you attacked this combat"), false); ability.addWatcher(new CustodiSoulcallerWatcher()); - ability.addTarget(new TargetCardInYourGraveyard(new FilterCreatureCard("creature card with converted mana cost X or less from your graveyard, where X is the number of players you attacked with a creature this combat"))); ability.setTargetAdjuster(CustodiSoulcallerAdjuster.instance); this.addAbility(ability); } @@ -69,9 +67,9 @@ public void adjustTargets(Ability ability, Game game) { Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(ability.getSourceId()); if (watcher != null) { int xValue = watcher.getNumberOfAttackedPlayers(sourcePermanent.getControllerId()); - FilterCard filter = new FilterCard("creature card with converted mana cost " + xValue + " or less"); + FilterCard filter = new FilterCard("creature card with mana value " + xValue + " or less"); filter.add(CardType.CREATURE.getPredicate()); - filter.add(Predicates.or(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, xValue), new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, xValue))); + filter.add(Predicates.or(new ManaValuePredicate(ComparisonType.EQUAL_TO, xValue), new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue))); ability.getTargets().add(new TargetCardInYourGraveyard(filter)); } } diff --git a/Mage.Sets/src/mage/cards/c/CustodiSquire.java b/Mage.Sets/src/mage/cards/c/CustodiSquire.java index 191fc07d50f4..f167b4ac0954 100644 --- a/Mage.Sets/src/mage/cards/c/CustodiSquire.java +++ b/Mage.Sets/src/mage/cards/c/CustodiSquire.java @@ -1,4 +1,3 @@ - package mage.cards.c; import mage.MageInt; @@ -6,29 +5,31 @@ import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; -import mage.cards.*; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.choices.VoteHandler; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; import mage.game.Game; import mage.players.Player; -import mage.target.TargetCard; +import mage.target.common.TargetCardInGraveyard; -import java.util.HashMap; -import java.util.Map; +import java.util.LinkedHashSet; +import java.util.Set; import java.util.UUID; /** - * - * @author LevelX2 + * @author LevelX2, TheElk801 */ public final class CustodiSquire extends CardImpl { public CustodiSquire(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}"); this.subtype.add(SubType.SPIRIT); this.subtype.add(SubType.CLERIC); this.power = new MageInt(3); @@ -37,7 +38,9 @@ public CustodiSquire(UUID ownerId, CardSetInfo setInfo) { // Flying this.addAbility(FlyingAbility.getInstance()); // Will of the council - When Custodi Squire enters the battlefield, starting with you, each player votes for an artifact, creature, or enchantment card in your graveyard. Return each card with the most votes or tied for most votes to your hand. - this.addAbility(new EntersBattlefieldTriggeredAbility(new CustodiSquireVoteEffect(), false, true)); + this.addAbility(new EntersBattlefieldTriggeredAbility( + new CustodiSquireVoteEffect(), false, true + )); } private CustodiSquire(final CustodiSquire card) { @@ -52,20 +55,14 @@ public CustodiSquire copy() { class CustodiSquireVoteEffect extends OneShotEffect { - private static final FilterCard filter = new FilterCard("artifact, creature, or enchantment card from your graveyard"); - - static { - filter.add(Predicates.or(CardType.ARTIFACT.getPredicate(), - CardType.CREATURE.getPredicate(), - CardType.ENCHANTMENT.getPredicate())); - } - CustodiSquireVoteEffect() { super(Outcome.Benefit); - this.staticText = "Will of the council — When {this} enters the battlefield, starting with you, each player votes for an artifact, creature, or enchantment card in your graveyard. Return each card with the most votes or tied for most votes to your hand"; + this.staticText = "Will of the council — When {this} enters the battlefield, " + + "starting with you, each player votes for an artifact, creature, or enchantment card in your graveyard. " + + "Return each card with the most votes or tied for most votes to your hand"; } - CustodiSquireVoteEffect(final CustodiSquireVoteEffect effect) { + private CustodiSquireVoteEffect(final CustodiSquireVoteEffect effect) { super(effect); } @@ -76,47 +73,52 @@ public CustodiSquireVoteEffect copy() { @Override public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + CustodiSquireVote vote = new CustodiSquireVote(); + vote.doVotes(source, game); + + return player.moveCards(vote.getMostVoted(), Zone.HAND, source, game); + } +} + +class CustodiSquireVote extends VoteHandler { + + private static final FilterCard filter = new FilterCard("artifact, creature, or enchantment card"); + + static { + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.CREATURE.getPredicate(), + CardType.ENCHANTMENT.getPredicate() + )); + } + + @Override + protected Set getPossibleVotes(Ability source, Game game) { + // too much permanentns on battlefield, so no need to show full list here + return new LinkedHashSet<>(); + } + + @Override + public Card playerChoose(String voteInfo, Player player, Player decidingPlayer, Ability source, Game game) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Cards possibleCards = new CardsImpl(); - possibleCards.addAll(controller.getGraveyard().getCards(filter, game)); - if (!possibleCards.isEmpty()) { - Map cardCounter = new HashMap<>(); - TargetCard target = new TargetCard(1, 1, Zone.GRAVEYARD, filter); - int maxCount = 1; - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - target.clearChosen(); - player.chooseTarget(outcome, possibleCards, target, source, game); - Card card = game.getCard(target.getFirstTarget()); - if (card != null) { - game.informPlayers(player.getLogName() + " voted for " + card.getLogName()); - if (!cardCounter.containsKey(target.getFirstTarget())) { - cardCounter.put(target.getFirstTarget(), 1); - } else { - int count = cardCounter.get(target.getFirstTarget()) + 1; - if (count > maxCount) { - maxCount = count; - } - cardCounter.put(target.getFirstTarget(), count); - } - } - } - } - Cards cardsToMove = new CardsImpl(); - for (UUID uuid : possibleCards) { - if (cardCounter.containsKey(uuid)) { - if (cardCounter.get(uuid) == maxCount) { - cardsToMove.add(uuid); - } - } - } - controller.moveCards(cardsToMove, Zone.HAND, source, game); - } - return true; + if (controller == null || controller.getGraveyard().count( + filter, source.getSourceId(), source.getControllerId(), game + ) < 1) { + return null; } - return false; + TargetCardInGraveyard target = new TargetCardInGraveyard(filter); + target.withChooseHint(voteInfo + " (from graveyard to hand)"); + target.setNotTarget(true); + decidingPlayer.choose(Outcome.ReturnToHand, controller.getGraveyard(), target, game); + return controller.getGraveyard().get(target.getFirstTarget(), game); + } + @Override + protected String voteName(Card vote) { + return vote.getIdName(); } } diff --git a/Mage.Sets/src/mage/cards/c/CutTheTethers.java b/Mage.Sets/src/mage/cards/c/CutTheTethers.java index 2e544c42dbd2..ff6c7f510c4e 100644 --- a/Mage.Sets/src/mage/cards/c/CutTheTethers.java +++ b/Mage.Sets/src/mage/cards/c/CutTheTethers.java @@ -5,11 +5,13 @@ import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -25,7 +27,6 @@ public final class CutTheTethers extends CardImpl { public CutTheTethers(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}{U}"); - // For each Spirit, return it to its owner's hand unless that player pays {3}. this.getSpellAbility().addEffect(new CutTheTethersEffect()); } @@ -42,18 +43,14 @@ public CutTheTethers copy() { class CutTheTethersEffect extends OneShotEffect { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Spirit creatures"); - - static { - filter.add(SubType.SPIRIT.getPredicate()); - } + private static final FilterPermanent filter = new FilterPermanent(SubType.SPIRIT, ""); - public CutTheTethersEffect() { + CutTheTethersEffect() { super(Outcome.ReturnToHand); this.staticText = "For each Spirit, return it to its owner's hand unless that player pays {3}"; } - public CutTheTethersEffect(final CutTheTethersEffect effect) { + private CutTheTethersEffect(final CutTheTethersEffect effect) { super(effect); } @@ -64,19 +61,28 @@ public CutTheTethersEffect copy() { @Override public boolean apply(Game game, Ability source) { - for (Permanent creature : game.getState().getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - Player player = game.getPlayer(creature.getControllerId()); - if (player != null) { - boolean paid = false; - if (player.chooseUse(Outcome.Benefit, "Pay {3} to keep " + creature.getName() + " on the battlefield?", source, game)) { - Cost cost = ManaUtil.createManaCost(3, false); - paid = cost.pay(source, game, source, creature.getControllerId(), false, null); - } - if (!paid) { - creature.moveToZone(Zone.HAND, source, game, true); - } + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Cards toHand = new CardsImpl(); + for (Permanent permanent : game.getBattlefield().getActivePermanents( + filter, source.getControllerId(), source.getSourceId(), game + )) { + Player player = game.getPlayer(permanent.getOwnerId()); + if (player == null) { + continue; + } + boolean paid = false; + if (player.chooseUse(Outcome.Benefit, "Pay {3} to keep " + permanent.getIdName() + " on the battlefield?", source, game)) { + Cost cost = ManaUtil.createManaCost(3, false); + paid = cost.pay(source, game, source, permanent.getControllerId(), false, null); + } + if (!paid) { + toHand.add(permanent); } } + controller.moveCards(toHand, Zone.HAND, source, game); return true; } } diff --git a/Mage.Sets/src/mage/cards/c/CyclicalEvolution.java b/Mage.Sets/src/mage/cards/c/CyclicalEvolution.java index 081fbb74a808..b6d3c453d1e2 100644 --- a/Mage.Sets/src/mage/cards/c/CyclicalEvolution.java +++ b/Mage.Sets/src/mage/cards/c/CyclicalEvolution.java @@ -28,7 +28,7 @@ public CyclicalEvolution(UUID ownerId, CardSetInfo setInfo) { // Target creature gets +3/+3 until end of turn. Exile Cyclical Evolution with three time counters on it. getSpellAbility().addEffect(new BoostTargetEffect(3, 3, Duration.EndOfTurn)); getSpellAbility().addTarget(new TargetCreaturePermanent()); - getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + getSpellAbility().addEffect(new ExileSpellEffect()); Effect effect = new AddCountersSourceEffect(CounterType.TIME.createInstance(), StaticValue.get(3), true, true); effect.setText("with 3 time counters on it"); getSpellAbility().addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/c/CyclopeanTomb.java b/Mage.Sets/src/mage/cards/c/CyclopeanTomb.java index 670884c26a54..56ed5e0ec753 100644 --- a/Mage.Sets/src/mage/cards/c/CyclopeanTomb.java +++ b/Mage.Sets/src/mage/cards/c/CyclopeanTomb.java @@ -46,10 +46,10 @@ public CyclopeanTomb(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); // {2}, {tap}: Put a mire counter on target non-Swamp land. That land is a Swamp for as long as it has a mire counter on it. Activate this ability only during your upkeep. - Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new AddCountersTargetEffect(CounterType.MIRE.createInstance()), new GenericManaCost(2), new IsStepCondition(PhaseStep.UPKEEP), "{2}, {T}: Put a mire counter on target non-Swamp land. That land is a Swamp for as long as it has a mire counter on it. Activate this ability only during your upkeep."); + Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new AddCountersTargetEffect(CounterType.MIRE.createInstance()), new GenericManaCost(2), new IsStepCondition(PhaseStep.UPKEEP), "{2}, {T}: Put a mire counter on target non-Swamp land. That land is a Swamp for as long as it has a mire counter on it. Activate only during your upkeep."); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetLandPermanent(filter)); - ability.addEffect(new BecomeSwampEffect(Duration.Custom, false, true, SubType.SWAMP)); + ability.addEffect(new BecomeSwampEffect()); this.addAbility(ability, new CyclopeanTombCounterWatcher()); // When Cyclopean Tomb is put into a graveyard from the battlefield, at the beginning of each of your upkeeps for the rest of the game, remove all mire counters from a land that a mire counter was put onto with Cyclopean Tomb but that a mire counter has not been removed from with Cyclopean Tomb. @@ -68,8 +68,8 @@ public CyclopeanTomb copy() { class BecomeSwampEffect extends BecomesBasicLandTargetEffect { - BecomeSwampEffect(Duration duration, boolean chooseLandType, boolean loseOther, SubType... landNames) { - super(duration, chooseLandType, loseOther, landNames); + BecomeSwampEffect() { + super(Duration.Custom, false, true, SubType.SWAMP); staticText = "That land is a Swamp for as long as it has a mire counter on it"; } @@ -80,14 +80,11 @@ private BecomeSwampEffect(final BecomeSwampEffect effect) { @Override public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { Permanent land = game.getPermanent(this.targetPointer.getFirst(game, source)); - if (land == null) { - // if permanent left battlefield the effect can be removed because it was only valid for that object + if (land == null || land.getCounters(game).getCount(CounterType.MIRE) < 1) { this.discard(); - } else if (land.getCounters(game).getCount(CounterType.MIRE) > 0) { - // only if Mire counter is on the object it becomes a Swamp. - super.apply(layer, sublayer, source, game); + return false; } - return true; + return super.apply(layer, sublayer, source, game); } @Override diff --git a/Mage.Sets/src/mage/cards/c/CytoplastRootKin.java b/Mage.Sets/src/mage/cards/c/CytoplastRootKin.java index 13a656ff7f54..5fe91f4613f2 100644 --- a/Mage.Sets/src/mage/cards/c/CytoplastRootKin.java +++ b/Mage.Sets/src/mage/cards/c/CytoplastRootKin.java @@ -18,7 +18,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/d/DaemogothTitan.java b/Mage.Sets/src/mage/cards/d/DaemogothTitan.java new file mode 100644 index 000000000000..58f89fddac0e --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DaemogothTitan.java @@ -0,0 +1,40 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.common.AttacksOrBlocksTriggeredAbility; +import mage.abilities.effects.common.SacrificeControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DaemogothTitan extends CardImpl { + + public DaemogothTitan(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B/G}{B/G}{B/G}{B/G}"); + + this.subtype.add(SubType.DEMON); + this.power = new MageInt(11); + this.toughness = new MageInt(10); + + // Whenever Daemogoth Titan attacks or blocks, sacrifice a creature. + this.addAbility(new AttacksOrBlocksTriggeredAbility(new SacrificeControllerEffect( + StaticFilters.FILTER_PERMANENT_CREATURE, 1, null + ), false)); + } + + private DaemogothTitan(final DaemogothTitan card) { + super(card); + } + + @Override + public DaemogothTitan copy() { + return new DaemogothTitan(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DaemogothWoeEater.java b/Mage.Sets/src/mage/cards/d/DaemogothWoeEater.java new file mode 100644 index 000000000000..dc88639deed7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DaemogothWoeEater.java @@ -0,0 +1,54 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SacrificeSourceTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.SacrificeControllerEffect; +import mage.abilities.effects.common.discard.DiscardEachPlayerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DaemogothWoeEater extends CardImpl { + + public DaemogothWoeEater(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B/G}{G}"); + + this.subtype.add(SubType.DEMON); + this.power = new MageInt(7); + this.toughness = new MageInt(6); + + // At the beginning of your upkeep, sacrifice a creature. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SacrificeControllerEffect( + StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT, 1, "" + ), TargetController.YOU, false)); + + // When you sacrifice Daemogoth Woe-Eater, each opponent discards a card, you draw a card, and you gain 2 life. + Ability ability = new SacrificeSourceTriggeredAbility( + new DiscardEachPlayerEffect(TargetController.OPPONENT), false + ); + ability.addEffect(new DrawCardSourceControllerEffect(1).concatBy(", you")); + ability.addEffect(new GainLifeEffect(2).concatBy(", and")); + this.addAbility(ability); + } + + private DaemogothWoeEater(final DaemogothWoeEater card) { + super(card); + } + + @Override + public DaemogothWoeEater copy() { + return new DaemogothWoeEater(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DampingEngine.java b/Mage.Sets/src/mage/cards/d/DampingEngine.java index 261731383d8b..b75491e722b2 100644 --- a/Mage.Sets/src/mage/cards/d/DampingEngine.java +++ b/Mage.Sets/src/mage/cards/d/DampingEngine.java @@ -1,55 +1,32 @@ -/* - * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of BetaSteward_at_googlemail.com. - */ package mage.cards.d; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.SpecialAction; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.Condition; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.FilterPermanent; +import mage.game.Controllable; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetControlledPermanent; +import java.util.Map; +import java.util.Objects; import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Collectors; /** - * @author jeffwadsworth + * @author TheElk801 */ public class DampingEngine extends CardImpl { @@ -58,9 +35,11 @@ public DampingEngine(UUID ownerId, CardSetInfo setInfo) { // A player who controls more permanents than each other player can't play lands or cast artifact, creature, or enchantment spells. // That player may sacrifice a permanent for that player to ignore this effect until end of turn. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DampingEngineEffect())); - this.addAbility(new DampingEngineSpecialAction()); - + Ability ability = new SimpleStaticAbility(new DampingEngineEffect()); + ability.addEffect(new GainAbilitySourceEffect(new DampingEngineSpecialAction()).setText( + "That player may sacrifice a permanent for that player to ignore this effect until end of turn" + )); + this.addAbility(ability); } private DampingEngine(final DampingEngine card) { @@ -71,17 +50,53 @@ private DampingEngine(final DampingEngine card) { public DampingEngine copy() { return new DampingEngine(this); } + + static boolean checkPlayer(Player player, Game game) { + Map map = game + .getBattlefield() + .getActivePermanents(player.getId(), game) + .stream() + .filter(Objects::nonNull) + .map(Controllable::getControllerId) + .collect(Collectors.toMap( + Function.identity(), + x -> 1, + Integer::sum + )); + int playerPerms = map.getOrDefault(player.getId(), 0); + int otherPerms = map + .entrySet() + .stream() + .filter(e -> !player.getId().equals(e.getKey())) + .mapToInt(e -> e.getValue()) + .max() + .orElse(0); + return playerPerms > otherPerms; + } + + static String makeKey(UUID playerId, Ability source, Game game) { + return "dampingEngine_" + + playerId + "_" + + source.getSourceId() + "_" + + source.getSourceObjectZoneChangeCounter() + "_" + + game.getTurnNum(); + } + + static boolean checkValue(UUID playerId, Ability source, Game game) { + Object object = game.getState().getValue(makeKey(playerId, source, game)); + return object instanceof Boolean && ((Boolean) object); + } } class DampingEngineEffect extends ContinuousRuleModifyingEffectImpl { - public DampingEngineEffect() { + DampingEngineEffect() { super(Duration.WhileOnBattlefield, Outcome.AIDontUseIt); - staticText = "A player who controls more permanents than each other player can't play lands or cast artifact, creature, or enchantment spells" - + "That player may sacrifice a permanent for that player to ignore this effect until end of turn.

"; + staticText = "A player who controls more permanents than each other player " + + "can't play lands or cast artifact, creature, or enchantment spells."; } - public DampingEngineEffect(final DampingEngineEffect effect) { + private DampingEngineEffect(final DampingEngineEffect effect) { super(effect); } @@ -97,7 +112,7 @@ public boolean apply(Game game, Ability source) { @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { - MageObject mageObject = game.getObject(source.getSourceId()); + MageObject mageObject = source.getSourceObject(game); if (mageObject != null) { return "You can't play the land or cast the spell (" + mageObject.getName() + " in play)."; } @@ -114,38 +129,33 @@ public boolean checksEventType(GameEvent event, Game game) { @Override public boolean applies(GameEvent event, Ability source, Game game) { Player player = game.getPlayer(event.getPlayerId()); - Permanent dampingEngine = game.getPermanent(source.getSourceId()); - final Card card = game.getCard(event.getSourceId()); - if (player != null || card != null) { - // check type of spell - if (card.isCreature() - || card.isArtifact() - || card.isEnchantment() - || card.isLand()) { - // check to see if the player has more permanents - if (new ControlsMorePermanentsThanEachOtherPlayer(player).apply(game, source)) { - // check to see if the player choose to ignore the effect - return game.getState().getValue("ignoreEffect") == null - || dampingEngine == null - || !game.getState().getValue("ignoreEffect").equals - (dampingEngine.getId() + "ignoreEffect" + game.getState().getPriorityPlayerId() + game.getState().getTurnNum()); - } - } + Permanent dampingEngine = source.getSourcePermanentIfItStillExists(game); + Card card = game.getCard(event.getSourceId()); + if (player == null || dampingEngine == null || card == null) { + return false; } - return false; + if (!card.isCreature() + && !card.isArtifact() + && !card.isEnchantment() + && !card.isLand()) { + return false; + } + return DampingEngine.checkPlayer(player, game) + && !DampingEngine.checkValue(player.getId(), source, game); } } class DampingEngineSpecialAction extends SpecialAction { - public DampingEngineSpecialAction() { + DampingEngineSpecialAction() { super(Zone.BATTLEFIELD); this.addCost(new SacrificeTargetCost(new TargetControlledPermanent(), true)); this.addEffect(new DampingEngineIgnoreEffect()); this.setMayActivate(TargetController.ANY); + this.setRuleVisible(false); } - public DampingEngineSpecialAction(final DampingEngineSpecialAction ability) { + private DampingEngineSpecialAction(final DampingEngineSpecialAction ability) { super(ability); } @@ -153,16 +163,31 @@ public DampingEngineSpecialAction(final DampingEngineSpecialAction ability) { public DampingEngineSpecialAction copy() { return new DampingEngineSpecialAction(this); } + + @Override + public ActivationStatus canActivate(UUID playerId, Game game) { + Player player = game.getPlayer(playerId); + if (player != null + && DampingEngine.checkPlayer(player, game) + && !DampingEngine.checkValue(player.getId(), this, game)) { + return super.canActivate(playerId, game); + } + return ActivationStatus.getFalse(); + } + + @Override + public String getRule() { + return ""; + } } class DampingEngineIgnoreEffect extends OneShotEffect { - public DampingEngineIgnoreEffect() { + DampingEngineIgnoreEffect() { super(Outcome.AIDontUseIt); - this.staticText = "That player may sacrifice a permanent for that player to ignore this effect until end of turn"; } - public DampingEngineIgnoreEffect(final DampingEngineIgnoreEffect effect) { + private DampingEngineIgnoreEffect(final DampingEngineIgnoreEffect effect) { super(effect); } @@ -173,36 +198,7 @@ public DampingEngineIgnoreEffect copy() { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - String key = permanent.getId() + "ignoreEffect" + game.getState().getPriorityPlayerId() + game.getState().getTurnNum(); - if (key != null) { - game.getState().setValue("ignoreEffect", key); - } + game.getState().setValue(DampingEngine.makeKey(source.getControllerId(), source, game), Boolean.TRUE); return true; } } - -class ControlsMorePermanentsThanEachOtherPlayer implements Condition { - - Player player; - - public ControlsMorePermanentsThanEachOtherPlayer(Player player) { - this.player = player; - } - - @Override - public boolean apply(Game game, Ability source) { - int numPermanents = game.getBattlefield().countAll(new FilterPermanent(), player.getId(), game); - for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { - if (numPermanents > game.getBattlefield().countAll(new FilterPermanent(), playerId, game)) { - return true; - } - } - return false; - } - - @Override - public String toString() { - return "a player controls less permanents than you"; - } -} diff --git a/Mage.Sets/src/mage/cards/d/DanceOfTheManse.java b/Mage.Sets/src/mage/cards/d/DanceOfTheManse.java index 75dcf91da670..4d0b4fcaeda6 100644 --- a/Mage.Sets/src/mage/cards/d/DanceOfTheManse.java +++ b/Mage.Sets/src/mage/cards/d/DanceOfTheManse.java @@ -12,7 +12,7 @@ import mage.constants.*; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.Target; @@ -55,7 +55,7 @@ enum DanceOfTheManseAdjuster implements TargetAdjuster { public void adjustTargets(Ability ability, Game game) { int xValue = ability.getManaCostsToPay().getX(); FilterCard filter = new FilterCard("artifact and/or non-Aura enchantment cards " + - "each with converted mana cost " + xValue + " or less from your graveyard"); + "each with mana value " + xValue + " or less from your graveyard"); filter.add(Predicates.or( CardType.ARTIFACT.getPredicate(), Predicates.and( @@ -63,7 +63,7 @@ public void adjustTargets(Ability ability, Game game) { Predicates.not(SubType.AURA.getPredicate()) ) )); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, xValue + 1)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); ability.getTargets().clear(); ability.addTarget(new TargetCardInYourGraveyard(0, xValue, filter)); } @@ -74,7 +74,7 @@ class DanceOfTheManseEffect extends OneShotEffect { DanceOfTheManseEffect() { super(Outcome.Benefit); staticText = "Return up to X target artifact and/or non-Aura enchantment cards " + - "each with converted mana cost X or less from your graveyard to the battlefield. " + + "each with mana value X or less from your graveyard to the battlefield. " + "If X is 6 or more, those permanents are 4/4 creatures in addition to their other types."; } diff --git a/Mage.Sets/src/mage/cards/d/DarettiScrapSavant.java b/Mage.Sets/src/mage/cards/d/DarettiScrapSavant.java index b20d29bb8790..0a3f91aa63be 100644 --- a/Mage.Sets/src/mage/cards/d/DarettiScrapSavant.java +++ b/Mage.Sets/src/mage/cards/d/DarettiScrapSavant.java @@ -4,17 +4,13 @@ import mage.abilities.LoyaltyAbility; import mage.abilities.common.CanBeYourCommanderAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; -import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.GetEmblemEffect; -import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.abilities.effects.common.discard.DiscardAndDrawThatManyEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.CardsImpl; import mage.constants.*; -import mage.filter.FilterCard; import mage.filter.common.FilterArtifactCard; import mage.filter.common.FilterControlledArtifactPermanent; import mage.game.Game; @@ -24,8 +20,6 @@ import mage.target.Target; import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetControlledPermanent; -import mage.target.common.TargetDiscard; -import mage.target.targetpointer.FixedTarget; import java.util.UUID; @@ -42,7 +36,7 @@ public DarettiScrapSavant(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); // +2: Discard up to two cards, then draw that many cards. - this.addAbility(new LoyaltyAbility(new DarettiDiscardDrawEffect(), 2)); + this.addAbility(new LoyaltyAbility(new DiscardAndDrawThatManyEffect(2), 2)); // -2: Sacrifice an artifact. If you do, return target artifact card from your graveyard to the battlefield. LoyaltyAbility loyaltyAbility = new LoyaltyAbility(new DarettiSacrificeEffect(), -2); @@ -66,44 +60,14 @@ public DarettiScrapSavant copy() { } } -class DarettiDiscardDrawEffect extends OneShotEffect { - - public DarettiDiscardDrawEffect() { - super(Outcome.Detriment); - this.staticText = "Discard up to two cards, then draw that many cards"; - } - - public DarettiDiscardDrawEffect(final DarettiDiscardDrawEffect effect) { - super(effect); - } - - @Override - public DarettiDiscardDrawEffect copy() { - return new DarettiDiscardDrawEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - TargetDiscard target = new TargetDiscard(0, 2, new FilterCard(), controller.getId()); - target.choose(outcome, controller.getId(), source.getSourceId(), game); - int count = controller.discard(new CardsImpl(target.getTargets()), false, source, game).size(); - controller.drawCards(count, source, game); - return true; - } - return false; - } -} - class DarettiSacrificeEffect extends OneShotEffect { - public DarettiSacrificeEffect() { + DarettiSacrificeEffect() { super(Outcome.PutCardInPlay); this.staticText = "Sacrifice an artifact. If you do, return target artifact card from your graveyard to the battlefield"; } - public DarettiSacrificeEffect(final DarettiSacrificeEffect effect) { + private DarettiSacrificeEffect(final DarettiSacrificeEffect effect) { super(effect); } @@ -115,50 +79,19 @@ public DarettiSacrificeEffect copy() { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Target target = new TargetControlledPermanent(1, 1, new FilterControlledArtifactPermanent(), true); - if (target.canChoose(source.getSourceId(), controller.getId(), game) - && controller.chooseTarget(outcome, target, source, game)) { - Permanent artifact = game.getPermanent(target.getFirstTarget()); - if (artifact != null && artifact.sacrifice(source, game)) { - Card card = game.getCard(getTargetPointer().getFirst(game, source)); - if (card != null) { - return controller.moveCards(card, Zone.BATTLEFIELD, source, game); - } - } - } + if (controller == null) { + return false; + } + Target target = new TargetControlledPermanent(1, 1, new FilterControlledArtifactPermanent(), true); + if (!target.canChoose(source.getSourceId(), controller.getId(), game) + || !controller.chooseTarget(outcome, target, source, game)) { return true; } - return false; - } -} - -class DarettiScrapSavantEffect extends OneShotEffect { - - DarettiScrapSavantEffect() { - super(Outcome.PutCardInPlay); - this.staticText = "return that card to the battlefield at the beginning of the next end step"; - } - - DarettiScrapSavantEffect(final DarettiScrapSavantEffect effect) { - super(effect); - } - - @Override - public DarettiScrapSavantEffect copy() { - return new DarettiScrapSavantEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Card card = game.getCard(getTargetPointer().getFirst(game, source)); - if (card != null && game.getState().getZone(card.getId()) == Zone.GRAVEYARD) { - Effect effect = new ReturnFromGraveyardToBattlefieldTargetEffect(); - effect.setTargetPointer(new FixedTarget(card, game)); - effect.setText("return that card to the battlefield at the beginning of the next end step"); - game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(Zone.COMMAND, effect, TargetController.ANY), source); + Permanent artifact = game.getPermanent(target.getFirstTarget()); + if (artifact == null || !artifact.sacrifice(source, game)) { return true; } - return false; + Card card = game.getCard(getTargetPointer().getFirst(game, source)); + return card == null || controller.moveCards(card, Zone.BATTLEFIELD, source, game); } } diff --git a/Mage.Sets/src/mage/cards/d/DarkConfidant.java b/Mage.Sets/src/mage/cards/d/DarkConfidant.java index a74dc25b8403..8dcbc416c0f9 100644 --- a/Mage.Sets/src/mage/cards/d/DarkConfidant.java +++ b/Mage.Sets/src/mage/cards/d/DarkConfidant.java @@ -48,7 +48,7 @@ class DarkConfidantEffect extends OneShotEffect { DarkConfidantEffect() { super(Outcome.DrawCard); - this.staticText = "reveal the top card of your library and put that card into your hand. You lose life equal to its converted mana cost"; + this.staticText = "reveal the top card of your library and put that card into your hand. You lose life equal to its mana value"; } DarkConfidantEffect(final DarkConfidantEffect effect) { @@ -66,7 +66,7 @@ public boolean apply(Game game, Ability source) { Cards cards = new CardsImpl(card); controller.revealCards(sourcePermanent.getIdName(), cards, game); controller.moveCards(card, Zone.HAND, source, game); - controller.loseLife(card.getConvertedManaCost(), game, source, false); + controller.loseLife(card.getManaValue(), game, source, false); } return true; diff --git a/Mage.Sets/src/mage/cards/d/DarkDecision.java b/Mage.Sets/src/mage/cards/d/DarkDecision.java index 2898d27c4a1d..137a60d5be03 100644 --- a/Mage.Sets/src/mage/cards/d/DarkDecision.java +++ b/Mage.Sets/src/mage/cards/d/DarkDecision.java @@ -50,7 +50,7 @@ class DarkDecisionEffect extends OneShotEffect { public DarkDecisionEffect() { super(Outcome.DrawCard); - this.staticText = "Search the top 10 cards of your library for a nonland card, exile it, then shuffle your library. Until end of turn, you may cast that card"; + this.staticText = "Search the top 10 cards of your library for a nonland card, exile it, then shuffle. Until end of turn, you may cast that card"; } public DarkDecisionEffect(final DarkDecisionEffect effect) { diff --git a/Mage.Sets/src/mage/cards/d/DarkImpostor.java b/Mage.Sets/src/mage/cards/d/DarkImpostor.java index 6f37ec593c60..b3b21f423d00 100644 --- a/Mage.Sets/src/mage/cards/d/DarkImpostor.java +++ b/Mage.Sets/src/mage/cards/d/DarkImpostor.java @@ -1,11 +1,8 @@ package mage.cards.d; -import java.util.UUID; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.ActivatedAbility; -import mage.abilities.Mode; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -17,14 +14,18 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.ExileZone; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author noxx - * */ public final class DarkImpostor extends CardImpl { @@ -37,12 +38,16 @@ public DarkImpostor(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(2); // {4}{B}{B}: Exile target creature and put a +1/+1 counter on Dark Impostor. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DarkImpostorExileTargetEffect(), new ManaCostsImpl("{4}{B}{B}")); + Ability ability = new SimpleActivatedAbility( + new DarkImpostorExileTargetEffect(), new ManaCostsImpl("{4}{B}{B}") + ); + ability.addEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance()) + .setText("and put a +1/+1 counter on {this}")); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); // Dark Impostor has all activated abilities of all creature cards exiled with it. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DarkImpostorContinuousEffect())); + this.addAbility(new SimpleStaticAbility(new DarkImpostorContinuousEffect())); } private DarkImpostor(final DarkImpostor card) { @@ -57,11 +62,12 @@ public DarkImpostor copy() { class DarkImpostorExileTargetEffect extends OneShotEffect { - public DarkImpostorExileTargetEffect() { + DarkImpostorExileTargetEffect() { super(Outcome.Exile); + staticText = "exile target creature"; } - public DarkImpostorExileTargetEffect(final DarkImpostorExileTargetEffect effect) { + private DarkImpostorExileTargetEffect(final DarkImpostorExileTargetEffect effect) { super(effect); } @@ -72,46 +78,43 @@ public DarkImpostorExileTargetEffect copy() { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); - MageObject sourceObject = source.getSourceObject(game); - if (permanent != null) { - permanent.moveToExile(null, null, source, game); - if (sourceObject instanceof Permanent) { - ((Permanent) sourceObject).imprint(permanent.getId(), game); - } + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (player == null || permanent == null) { + return false; } - return new AddCountersSourceEffect(CounterType.P1P1.createInstance()).apply(game, source); - } - - @Override - public String getText(Mode mode) { - return "exile target creature and put a +1/+1 counter on {this}"; + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); + if (sourcePermanent == null) { + return player.moveCards(permanent, Zone.EXILED, source, game); + } + return player.moveCardsToExile( + permanent, source, game, true, CardUtil.getExileZoneId(game, source), sourcePermanent.getIdName() + ); } } class DarkImpostorContinuousEffect extends ContinuousEffectImpl { - public DarkImpostorContinuousEffect() { + DarkImpostorContinuousEffect() { super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); staticText = "{this} has all activated abilities of all creature cards exiled with it"; } - public DarkImpostorContinuousEffect(final DarkImpostorContinuousEffect effect) { + private DarkImpostorContinuousEffect(final DarkImpostorContinuousEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { - Permanent perm = game.getPermanent(source.getSourceId()); - if (perm != null) { - for (UUID imprintedId : perm.getImprinted()) { - Card card = game.getCard(imprintedId); - if (card != null) { - for (Ability ability : card.getAbilities(game)) { - if (ability instanceof ActivatedAbility) { - perm.addAbility(ability.copy(), source.getSourceId(), game); - } - } + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + if (permanent == null || exileZone == null || exileZone.isEmpty()) { + return false; + } + for (Card card : exileZone.getCards(StaticFilters.FILTER_CARD_CREATURE, game)) { + for (Ability ability : card.getAbilities(game)) { + if (ability instanceof ActivatedAbility) { + permanent.addAbility(ability, source.getSourceId(), game); } } } @@ -122,5 +125,4 @@ public boolean apply(Game game, Ability source) { public DarkImpostorContinuousEffect copy() { return new DarkImpostorContinuousEffect(this); } - } diff --git a/Mage.Sets/src/mage/cards/d/DarkSupplicant.java b/Mage.Sets/src/mage/cards/d/DarkSupplicant.java index 5122c2d3aed8..779092d0184b 100644 --- a/Mage.Sets/src/mage/cards/d/DarkSupplicant.java +++ b/Mage.Sets/src/mage/cards/d/DarkSupplicant.java @@ -65,7 +65,7 @@ class DarkSupplicantEffect extends OneShotEffect { public DarkSupplicantEffect() { super(Outcome.PutCardInPlay); - this.staticText = "Search your graveyard, hand, and/or library for a card named Scion of Darkness and put it onto the battlefield. If you search your library this way, shuffle it"; + this.staticText = "Search your graveyard, hand, and/or library for a card named Scion of Darkness and put it onto the battlefield. If you search your library this way, shuffle"; } public DarkSupplicantEffect(final DarkSupplicantEffect effect) { diff --git a/Mage.Sets/src/mage/cards/d/DarkTutelage.java b/Mage.Sets/src/mage/cards/d/DarkTutelage.java index 26056b063b8f..8125fe718599 100644 --- a/Mage.Sets/src/mage/cards/d/DarkTutelage.java +++ b/Mage.Sets/src/mage/cards/d/DarkTutelage.java @@ -1,27 +1,32 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.OnEventTriggeredAbility; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +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.TargetController; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.GameEvent.EventType; import mage.players.Player; +import java.util.UUID; + /** - * * @author BetaSteward_at_googlemail.com */ public final class DarkTutelage extends CardImpl { public DarkTutelage(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); - this.addAbility(new OnEventTriggeredAbility(EventType.UPKEEP_STEP_PRE, "beginning of your upkeep", new DarkTutelageEffect(), false)); + + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new DarkTutelageEffect(), TargetController.YOU, false + )); } private DarkTutelage(final DarkTutelage card) { @@ -32,38 +37,39 @@ private DarkTutelage(final DarkTutelage card) { public DarkTutelage copy() { return new DarkTutelage(this); } - } class DarkTutelageEffect extends OneShotEffect { - public DarkTutelageEffect() { + DarkTutelageEffect() { super(Outcome.DrawCard); - staticText = "reveal the top card of your library and put that card into your hand. You lose life equal to its converted mana cost"; + staticText = "reveal the top card of your library and put that card into your hand. You lose life equal to its mana value"; } - public DarkTutelageEffect(final DarkTutelageEffect effect) { + private DarkTutelageEffect(final DarkTutelageEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Card card = controller.getLibrary().getFromTop(game); - if (card != null) { - controller.revealCards(source, new CardsImpl(card), game); - card.moveToZone(Zone.HAND, source, game, false); - controller.loseLife(card.getConvertedManaCost(), game, source, false); - return true; - } + Player player = game.getPlayer(source.getControllerId()); + if (player == null || !player.getLibrary().hasCards()) { + return false; + } + Card card = player.getLibrary().getFromTop(game); + if (card == null) { + return false; } - return false; + player.revealCards(source, new CardsImpl(card), game); + player.moveCards(card, Zone.HAND, source, game); + if (card.getManaValue() > 0) { + player.loseLife(card.getManaValue(), game, source, false); + } + return true; } @Override public DarkTutelageEffect copy() { return new DarkTutelageEffect(this); } - } diff --git a/Mage.Sets/src/mage/cards/d/DarkslickShores.java b/Mage.Sets/src/mage/cards/d/DarkslickShores.java index ce14e9ee57ce..0c828b10faaf 100644 --- a/Mage.Sets/src/mage/cards/d/DarkslickShores.java +++ b/Mage.Sets/src/mage/cards/d/DarkslickShores.java @@ -15,7 +15,7 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.filter.common.FilterLandPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/d/DauntingDefender.java b/Mage.Sets/src/mage/cards/d/DauntingDefender.java index b792b5ac1980..2f82af5c1eef 100644 --- a/Mage.Sets/src/mage/cards/d/DauntingDefender.java +++ b/Mage.Sets/src/mage/cards/d/DauntingDefender.java @@ -63,9 +63,9 @@ public boolean apply(Game game, Ability source) { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.DAMAGE_CREATURE) { + if (event.getType() == GameEvent.EventType.DAMAGE_PERMANENT) { Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.isControlledBy(source.getControllerId()) && permanent.hasSubtype(SubType.CLERIC, game)) { + if (permanent != null && permanent.isControlledBy(source.getControllerId()) && permanent.isCreature() && permanent.hasSubtype(SubType.CLERIC, game)) { return super.applies(event, source, game); } } diff --git a/Mage.Sets/src/mage/cards/d/DauntlessBodyguard.java b/Mage.Sets/src/mage/cards/d/DauntlessBodyguard.java index 1f7adf8d7c6f..535c7ab7ac0f 100644 --- a/Mage.Sets/src/mage/cards/d/DauntlessBodyguard.java +++ b/Mage.Sets/src/mage/cards/d/DauntlessBodyguard.java @@ -20,7 +20,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/d/DauntlessDourbark.java b/Mage.Sets/src/mage/cards/d/DauntlessDourbark.java index add62ac399f3..1ca398929b21 100644 --- a/Mage.Sets/src/mage/cards/d/DauntlessDourbark.java +++ b/Mage.Sets/src/mage/cards/d/DauntlessDourbark.java @@ -19,7 +19,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/d/DawnEvangel.java b/Mage.Sets/src/mage/cards/d/DawnEvangel.java index 2b17d77d6185..c8848aa074d2 100644 --- a/Mage.Sets/src/mage/cards/d/DawnEvangel.java +++ b/Mage.Sets/src/mage/cards/d/DawnEvangel.java @@ -10,7 +10,7 @@ import mage.constants.SubType; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; @@ -50,10 +50,10 @@ public DawnEvangel copy() { class DawnEvangelAbility extends DiesCreatureTriggeredAbility { private static final FilterCard cardFilter - = new FilterCreatureCard("creature card with converted mana cost 2 or less from your graveyard"); + = new FilterCreatureCard("creature card with mana value 2 or less from your graveyard"); static { - cardFilter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); + cardFilter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); } DawnEvangelAbility() { @@ -85,7 +85,7 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { return "Whenever a creature dies, if an Aura you controlled was attached to it, " + - "return target creature card with converted mana cost 2 or less from your graveyard to your hand."; + "return target creature card with mana value 2 or less from your graveyard to your hand."; } @Override diff --git a/Mage.Sets/src/mage/cards/d/DawnbreakReclaimer.java b/Mage.Sets/src/mage/cards/d/DawnbreakReclaimer.java index f850e2b78d9d..cb95219643be 100644 --- a/Mage.Sets/src/mage/cards/d/DawnbreakReclaimer.java +++ b/Mage.Sets/src/mage/cards/d/DawnbreakReclaimer.java @@ -18,7 +18,7 @@ import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; diff --git a/Mage.Sets/src/mage/cards/d/DaxosOfMeletis.java b/Mage.Sets/src/mage/cards/d/DaxosOfMeletis.java index 48046ff32767..21cb9ee126a1 100644 --- a/Mage.Sets/src/mage/cards/d/DaxosOfMeletis.java +++ b/Mage.Sets/src/mage/cards/d/DaxosOfMeletis.java @@ -60,7 +60,7 @@ class DaxosOfMeletisEffect extends OneShotEffect { public DaxosOfMeletisEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "exile the top card of that player's library. You gain life equal to that card's converted mana cost. Until end of turn, you may cast that card and you may spend mana as though it were mana of any color to cast it"; + this.staticText = "exile the top card of that player's library. You gain life equal to that card's mana value. Until end of turn, you may cast that card and you may spend mana as though it were mana of any color to cast it"; } public DaxosOfMeletisEffect(final DaxosOfMeletisEffect effect) { @@ -85,12 +85,12 @@ public boolean apply(Game game, Ability source) { // move card to exile controller.moveCardsToExile(card, source, game, true, exileId, sourceObject.getIdName()); // player gains life - controller.gainLife(card.getConvertedManaCost(), game, source); + controller.gainLife(card.getManaValue(), game, source); // Add effects only if the card has a spellAbility (e.g. not for lands). if (card.getSpellAbility() != null) { // allow to cast the card // and you may spend mana as though it were mana of any color to cast it - CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.EndOfTurn); + CardUtil.makeCardPlayable(game, source, card, Duration.EndOfTurn, true); } } return true; diff --git a/Mage.Sets/src/mage/cards/d/DaybreakRanger.java b/Mage.Sets/src/mage/cards/d/DaybreakRanger.java index 15569cbc0c22..976c5ce728ce 100644 --- a/Mage.Sets/src/mage/cards/d/DaybreakRanger.java +++ b/Mage.Sets/src/mage/cards/d/DaybreakRanger.java @@ -1,30 +1,24 @@ - package mage.cards.d; -import java.util.UUID; - import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; -import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.AbilityPredicate; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** * @author North */ @@ -54,8 +48,7 @@ public DaybreakRanger(UUID ownerId, CardSetInfo setInfo) { this.addAbility(activatedAbility); // At the beginning of each upkeep, if no spells were cast last turn, transform Daybreak Ranger. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private DaybreakRanger(final DaybreakRanger card) { diff --git a/Mage.Sets/src/mage/cards/d/DazzlingSphinx.java b/Mage.Sets/src/mage/cards/d/DazzlingSphinx.java new file mode 100644 index 000000000000..adf8dafbaea1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DazzlingSphinx.java @@ -0,0 +1,98 @@ +package mage.cards.d; + +import mage.ApprovingObject; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +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.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DazzlingSphinx extends CardImpl { + + public DazzlingSphinx(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); + + this.subtype.add(SubType.SPHINX); + this.power = new MageInt(4); + this.toughness = new MageInt(5); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever Dazzling Sphinx deals combat damage to a player, that player exiles cards from the top of their library until they exile an instant or sorcery card. You may cast that card without paying its mana cost. Then that player puts the exiled cards that weren't cast this way on the bottom of their library in a random order. + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( + new DazzlingSphinxEffect(), false, true + )); + } + + private DazzlingSphinx(final DazzlingSphinx card) { + super(card); + } + + @Override + public DazzlingSphinx copy() { + return new DazzlingSphinx(this); + } +} + +class DazzlingSphinxEffect extends OneShotEffect { + + DazzlingSphinxEffect() { + super(Outcome.Benefit); + staticText = "that player exiles cards from the top of their library until they exile an " + + "instant or sorcery card. You may cast that card without paying its mana cost. Then that player " + + "puts the exiled cards that weren't cast this way on the bottom of their library in a random order"; + } + + private DazzlingSphinxEffect(final DazzlingSphinxEffect effect) { + super(effect); + } + + @Override + public DazzlingSphinxEffect copy() { + return new DazzlingSphinxEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player opponent = game.getPlayer(targetPointer.getFirst(game, source)); + if (controller == null || opponent == null) { + return false; + } + Cards cards = new CardsImpl(); + Card toCast = null; + for (Card card : opponent.getLibrary().getCards(game)) { + cards.add(card); + if (card.isInstantOrSorcery()) { + toCast = card; + } + } + opponent.moveCards(cards, Zone.EXILED, source, game); + if (toCast != null && controller.chooseUse( + outcome, "Cast " + toCast.getName() + " without paying its mana cost?", source, game + )) { + game.getState().setValue("PlayFromNotOwnHandZone" + toCast.getId(), Boolean.TRUE); + controller.cast( + controller.chooseAbilityForCast(toCast, game, true), + game, true, new ApprovingObject(source, game) + ); + game.getState().setValue("PlayFromNotOwnHandZone" + toCast.getId(), null); + } + cards.retainZone(Zone.EXILED, game); + opponent.putCardsOnBottomOfLibrary(cards, game, source, false); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/d/DeadlyBrew.java b/Mage.Sets/src/mage/cards/d/DeadlyBrew.java new file mode 100644 index 000000000000..75dc47eb45da --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DeadlyBrew.java @@ -0,0 +1,119 @@ +package mage.cards.d; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DeadlyBrew extends CardImpl { + + public DeadlyBrew(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{B}{G}"); + + // Each player sacrifices a creature or planeswalker. If you sacrificed a permanent this way, you may return another permanent card from your graveyard to your hand. + this.getSpellAbility().addEffect(new DeadlyBrewEffect()); + } + + private DeadlyBrew(final DeadlyBrew card) { + super(card); + } + + @Override + public DeadlyBrew copy() { + return new DeadlyBrew(this); + } +} + +class DeadlyBrewEffect extends OneShotEffect { + + private static final FilterPermanent filter + = new FilterCreatureOrPlaneswalkerPermanent("creature or planeswalker you control"); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + } + + DeadlyBrewEffect() { + super(Outcome.Benefit); + staticText = "each player sacrifices a creature or planeswalker. If you sacrificed a permanent this way, " + + "you may return another permanent card from your graveyard to your hand"; + } + + private DeadlyBrewEffect(final DeadlyBrewEffect effect) { + super(effect); + } + + @Override + public DeadlyBrewEffect copy() { + return new DeadlyBrewEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + List toSacrifice = new ArrayList<>(); + Permanent yours = null; + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + TargetPermanent target = new TargetPermanent(filter); + target.setNotTarget(true); + if (!target.canChoose(source.getSourceId(), playerId, game)) { + continue; + } + player.choose(outcome, target, source.getSourceId(), game); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent == null) { + continue; + } + if (permanent.isControlledBy(source.getControllerId())) { + yours = permanent; + } else { + toSacrifice.add(permanent); + } + } + for (Permanent permanent : toSacrifice) { + if (permanent == null) { + continue; + } + permanent.sacrifice(source, game); + } + Cards yourGrave = new CardsImpl(controller.getGraveyard()); + yourGrave.removeIf(uuid -> !game.getCard(uuid).isPermanent()); + if (yours == null || !yours.sacrifice(source, game)) { + return true; + } + TargetCardInYourGraveyard target = new TargetCardInYourGraveyard( + 0, 1, StaticFilters.FILTER_CARD_PERMANENT, true + ); + controller.choose(outcome, yourGrave, target, game); + Card card = controller.getGraveyard().get(target.getFirstTarget(), game); + if (card != null) { + controller.moveCards(card, Zone.HAND, source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/d/DeadlyGrub.java b/Mage.Sets/src/mage/cards/d/DeadlyGrub.java index 071cae86c42c..f4b53025735c 100644 --- a/Mage.Sets/src/mage/cards/d/DeadlyGrub.java +++ b/Mage.Sets/src/mage/cards/d/DeadlyGrub.java @@ -17,7 +17,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; -import mage.game.permanent.token.DeadlyGrubToken; +import mage.game.permanent.token.DeadlyGrubInsectToken; /** * @@ -37,8 +37,9 @@ public DeadlyGrub(UUID ownerId, CardSetInfo setInfo) { this.addAbility(ability); this.addAbility(new VanishingUpkeepAbility(3)); this.addAbility(new VanishingSacrificeAbility()); + // When Deadly Grub dies, if it had no time counters on it, create a 6/1 green Insect creature token with shroud. - this.addAbility(new ConditionalInterveningIfTriggeredAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new DeadlyGrubToken(), 1)), + this.addAbility(new ConditionalInterveningIfTriggeredAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new DeadlyGrubInsectToken(), 1)), LastTimeCounterRemovedCondition.instance, "When {this} dies, if it had no time counters on it, create a 6/1 green Insect creature token with shroud.")); } diff --git a/Mage.Sets/src/mage/cards/d/Deadshot.java b/Mage.Sets/src/mage/cards/d/Deadshot.java index b35a2d87958e..c0f482e7b1ca 100644 --- a/Mage.Sets/src/mage/cards/d/Deadshot.java +++ b/Mage.Sets/src/mage/cards/d/Deadshot.java @@ -10,7 +10,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/d/DeathMutation.java b/Mage.Sets/src/mage/cards/d/DeathMutation.java index bdd092a0cee0..2fdbea7ba758 100644 --- a/Mage.Sets/src/mage/cards/d/DeathMutation.java +++ b/Mage.Sets/src/mage/cards/d/DeathMutation.java @@ -3,7 +3,7 @@ import java.util.UUID; import mage.ObjectColor; -import mage.abilities.dynamicvalue.common.TargetConvertedManaCost; +import mage.abilities.dynamicvalue.common.TargetManaValue; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; @@ -35,7 +35,7 @@ public DeathMutation(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new DestroyTargetEffect(true)); this.getSpellAbility().addTarget(new TargetPermanent(filter)); // create X 1/1 green Saproling creature tokens, where X is that creature's converted mana cost. - this.getSpellAbility().addEffect(new CreateTokenEffect(new SaprolingToken(), TargetConvertedManaCost.instance)); + this.getSpellAbility().addEffect(new CreateTokenEffect(new SaprolingToken(), TargetManaValue.instance)); } private DeathMutation(final DeathMutation card) { diff --git a/Mage.Sets/src/mage/cards/d/DeathPitsOfRath.java b/Mage.Sets/src/mage/cards/d/DeathPitsOfRath.java index f7a3a13f5dcb..c89e27d75b8f 100644 --- a/Mage.Sets/src/mage/cards/d/DeathPitsOfRath.java +++ b/Mage.Sets/src/mage/cards/d/DeathPitsOfRath.java @@ -1,9 +1,7 @@ package mage.cards.d; -import java.util.UUID; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -11,16 +9,18 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author Plopman */ public final class DeathPitsOfRath extends CardImpl { public DeathPitsOfRath(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{B}{B}"); // Whenever a creature is dealt damage, destroy it. It can't be regenerated. this.addAbility(new DeathPitsOfRathTriggeredAbility()); @@ -53,14 +53,16 @@ public DeathPitsOfRathTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent == null || !permanent.isCreature()) { + return false; } + getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game)); return true; } diff --git a/Mage.Sets/src/mage/cards/d/DeathWish.java b/Mage.Sets/src/mage/cards/d/DeathWish.java index 8f599440d893..6b133b73faec 100644 --- a/Mage.Sets/src/mage/cards/d/DeathWish.java +++ b/Mage.Sets/src/mage/cards/d/DeathWish.java @@ -1,17 +1,16 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.effects.common.ExileSpellEffect; import mage.abilities.effects.common.LoseHalfLifeEffect; import mage.abilities.effects.common.WishEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author Plopman */ public final class DeathWish extends CardImpl { @@ -20,13 +19,13 @@ public DeathWish(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}{B}"); // You may choose a card you own from outside the game and put it into your hand. - this.getSpellAbility().addEffect(new WishEffect(new FilterCard(), false)); + this.getSpellAbility().addEffect(new WishEffect(StaticFilters.FILTER_CARD_A, false)); // You lose half your life, rounded up. this.getSpellAbility().addEffect(new LoseHalfLifeEffect()); // Exile Death Wish. - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private DeathWish(final DeathWish card) { diff --git a/Mage.Sets/src/mage/cards/d/DeathbringerRegent.java b/Mage.Sets/src/mage/cards/d/DeathbringerRegent.java index 968839bb5110..7a627b3c6ce5 100644 --- a/Mage.Sets/src/mage/cards/d/DeathbringerRegent.java +++ b/Mage.Sets/src/mage/cards/d/DeathbringerRegent.java @@ -16,7 +16,7 @@ import mage.constants.SubType; import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.watchers.common.CastFromHandWatcher; diff --git a/Mage.Sets/src/mage/cards/d/DeathlessBehemoth.java b/Mage.Sets/src/mage/cards/d/DeathlessBehemoth.java index 68238ee61964..3042c776802b 100644 --- a/Mage.Sets/src/mage/cards/d/DeathlessBehemoth.java +++ b/Mage.Sets/src/mage/cards/d/DeathlessBehemoth.java @@ -22,7 +22,7 @@ */ public final class DeathlessBehemoth extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("two Eldrazi Scions"); + private static final FilterControlledPermanent filter = new FilterControlledPermanent("Eldrazi Scions"); static { filter.add(Predicates.and( diff --git a/Mage.Sets/src/mage/cards/d/DeathsOasis.java b/Mage.Sets/src/mage/cards/d/DeathsOasis.java index 2748394fead4..4e5d95e5021a 100644 --- a/Mage.Sets/src/mage/cards/d/DeathsOasis.java +++ b/Mage.Sets/src/mage/cards/d/DeathsOasis.java @@ -21,7 +21,7 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.game.events.GameEvent; @@ -47,7 +47,7 @@ public DeathsOasis(UUID ownerId, CardSetInfo setInfo) { // {1}, Sacrifice Death's Oasis: You gain life equal to the greatest converted mana cost among creatures you control. Ability ability = new SimpleActivatedAbility( new GainLifeEffect(DeathsOasisValue.instance) - .setText("you gain life equal to the greatest converted mana cost among creatures you control"), + .setText("you gain life equal to the highest mana value among creatures you control"), new GenericManaCost(1) ); ability.addCost(new SacrificeSourceCost()); @@ -96,14 +96,14 @@ public boolean checkTrigger(GameEvent event, Game game) { } this.getEffects().clear(); this.addEffect(new MillCardsControllerEffect(2)); - this.addEffect(new DeathsOasisEffect(zEvent.getTarget().getConvertedManaCost())); + this.addEffect(new DeathsOasisEffect(zEvent.getTarget().getManaValue())); return true; } @Override public String getRule() { - return "Whenever a nontoken creature you control dies, put the top two cards of your library " + - "into your graveyard. Then return a creature card with lesser converted mana cost " + + return "Whenever a nontoken creature you control dies, mill two cards. " + + "Then return a creature card with lesser mana value " + "than the creature that died from your graveyard to your hand."; } } @@ -114,8 +114,8 @@ class DeathsOasisEffect extends OneShotEffect { DeathsOasisEffect(int cmc) { super(Outcome.Benefit); - this.filter = new FilterCreatureCard("creature card in your graveyard with converted mana cost " + (cmc - 1) + " or less"); - this.filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, cmc)); + this.filter = new FilterCreatureCard("creature card in your graveyard with mana value " + (cmc - 1) + " or less"); + this.filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, cmc)); } private DeathsOasisEffect(final DeathsOasisEffect effect) { @@ -153,7 +153,7 @@ public int calculate(Game game, Ability sourceAbility, Effect effect) { .getAllActivePermanents(sourceAbility.getControllerId()) .stream() .filter(Permanent::isCreature) - .mapToInt(Permanent::getConvertedManaCost) + .mapToInt(Permanent::getManaValue) .max() .orElse(0); } diff --git a/Mage.Sets/src/mage/cards/d/DeathsPresence.java b/Mage.Sets/src/mage/cards/d/DeathsPresence.java index ec9ce99c97da..fa567e26e202 100644 --- a/Mage.Sets/src/mage/cards/d/DeathsPresence.java +++ b/Mage.Sets/src/mage/cards/d/DeathsPresence.java @@ -63,8 +63,8 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD - && ((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD) { + ZoneChangeEvent zoneChangeEvent = (ZoneChangeEvent) event; + if (zoneChangeEvent.isDiesEvent()) { Permanent permanent = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); if (permanent != null && permanent.isControlledBy(this.getControllerId()) && permanent.isCreature()) { this.getTargets().clear(); diff --git a/Mage.Sets/src/mage/cards/d/DecayingSoil.java b/Mage.Sets/src/mage/cards/d/DecayingSoil.java index c570621b9fe6..90f7f8b8fadd 100644 --- a/Mage.Sets/src/mage/cards/d/DecayingSoil.java +++ b/Mage.Sets/src/mage/cards/d/DecayingSoil.java @@ -1,32 +1,25 @@ package mage.cards.d; -import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.CardsInControllerGraveyardCondition; -import mage.abilities.costs.Cost; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.ExileFromZoneTargetEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TokenPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.events.ZoneChangeEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.common.TargetCardInYourGraveyard; -import mage.target.targetpointer.FixedTarget; -import mage.util.ManaUtil; import java.util.UUID; @@ -35,30 +28,30 @@ */ public final class DecayingSoil extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nontoken creature"); + private static final FilterPermanent filter = new FilterCreaturePermanent("nontoken creature"); static { filter.add(TargetController.YOU.getOwnerPredicate()); filter.add(Predicates.not(TokenPredicate.instance)); } + private static final Condition condition = new CardsInControllerGraveyardCondition(7); + public DecayingSoil(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}{B}"); - // At the beginning of your upkeep, exile a card from your graveyard. - Ability ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new ExileTargetEffect(), TargetController.YOU, false); - TargetCardInYourGraveyard target = new TargetCardInYourGraveyard(); - ability.addTarget(target); - this.addAbility(ability); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + Zone.BATTLEFIELD, new ExileFromZoneTargetEffect(Zone.GRAVEYARD, false + ).setText("exile a card from your graveyard"), TargetController.YOU, false, true)); // Threshold - As long as seven or more cards are in your graveyard, Decaying Soil has "Whenever a nontoken creature is put into your graveyard from the battlefield, you may pay {1}. If you do, return that card to your hand." - ability = new SimpleStaticAbility(Zone.BATTLEFIELD, - new ConditionalContinuousEffect(new GainAbilitySourceEffect(new DecayingSoilTriggeredAbility(new DecayingSoilEffect(), filter)), - new CardsInControllerGraveyardCondition(7), - "As long as seven or more cards are in your graveyard, {this} has \"Whenever a nontoken creature is put into your graveyard from the battlefield, you may pay {1}. If you do, return that card to your hand.\"")); - ability.setAbilityWord(AbilityWord.THRESHOLD); - this.addAbility(ability); + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(new DecayingSoilTriggeredAbility()), + condition, "Threshold — As long as seven or more cards are in your graveyard, " + + "{this} has \"Whenever a nontoken creature is put into your graveyard from the battlefield, " + + "you may pay {1}. If you do, return that card to your hand.\"" + ))); } private DecayingSoil(final DecayingSoil card) { @@ -71,18 +64,21 @@ public DecayingSoil copy() { } } -class DecayingSoilTriggeredAbility extends TriggeredAbilityImpl { +class DecayingSoilTriggeredAbility extends DiesCreatureTriggeredAbility { - protected FilterCreaturePermanent filter; + private static final FilterPermanent filter = new FilterCreaturePermanent(); - public DecayingSoilTriggeredAbility(Effect effect, FilterCreaturePermanent filter) { - super(Zone.BATTLEFIELD, effect, false); - this.filter = filter; + static { + filter.add(TargetController.YOU.getOwnerPredicate()); + filter.add(Predicates.not(TokenPredicate.instance)); } - public DecayingSoilTriggeredAbility(DecayingSoilTriggeredAbility ability) { + DecayingSoilTriggeredAbility() { + super(new DoIfCostPaid(new ReturnFromGraveyardToHandTargetEffect(), new GenericManaCost(1)), false, filter, true); + } + + private DecayingSoilTriggeredAbility(DecayingSoilTriggeredAbility ability) { super(ability); - this.filter = ability.filter; } @Override @@ -90,77 +86,9 @@ public DecayingSoilTriggeredAbility copy() { return new DecayingSoilTriggeredAbility(this); } - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ZONE_CHANGE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.isDiesEvent()) { - Permanent permanent = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); - if (permanent != null && filter.match(permanent, this.getSourceId(), this.getControllerId(), game)) { - getEffects().get(0).setTargetPointer(new FixedTarget(permanent, game)); - return true; - } - } - return false; - } - - @Override - public boolean checkInterveningIfClause(Game game) { - Player controller = game.getPlayer(this.getControllerId()); - return controller != null && controller.getGraveyard().contains(this.getSourceId()); - } - - @Override public String getRule() { - return "Whenever a " + filter.getMessage() + " is put into your graveyard from the battlefield, " + super.getRule(); + return "Whenever a nontoken creature is put into your graveyard from the battlefield, " + + "you may pay {1}. If you do, return that card to your hand."; } } - - -class DecayingSoilEffect extends OneShotEffect { - - private final Cost cost = ManaUtil.createManaCost(1, false); - - public DecayingSoilEffect() { - super(Outcome.Benefit); - staticText = "you may pay {1}. If you do, return that card to your hand"; - - } - - public DecayingSoilEffect(final DecayingSoilEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - if (player.chooseUse(Outcome.Benefit, " - Pay " + cost.getText() + '?', source, game)) { - cost.clearPaid(); - if (cost.pay(source, game, source, source.getControllerId(), false, null)) { - UUID target = this.getTargetPointer().getFirst(game, source); - if (target != null) { - Card card = game.getCard(target); - // check if it's still in graveyard - if (card != null && game.getState().getZone(card.getId()) == Zone.GRAVEYARD) { - card.moveToZone(Zone.HAND, source, game, true); - return true; - } - } - } - } - } - return false; - } - - @Override - public DecayingSoilEffect copy() { - return new DecayingSoilEffect(this); - } - -} diff --git a/Mage.Sets/src/mage/cards/d/DecisiveDenial.java b/Mage.Sets/src/mage/cards/d/DecisiveDenial.java new file mode 100644 index 000000000000..c57bed8a33f3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DecisiveDenial.java @@ -0,0 +1,46 @@ +package mage.cards.d; + +import mage.abilities.Mode; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CounterUnlessPaysEffect; +import mage.abilities.effects.common.FightTargetsEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.TargetSpell; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DecisiveDenial extends CardImpl { + + public DecisiveDenial(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}{U}"); + + // Choose one — + // • Target creature you control fights target creature you don't control. + this.getSpellAbility().addEffect(new FightTargetsEffect() + .setText("target creature you control fights target creature you don't control")); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); + + // • Counter target noncreature spell unless its controller pays {3}. + Mode mode = new Mode(new CounterUnlessPaysEffect(new GenericManaCost(3))); + mode.addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_NON_CREATURE)); + this.getSpellAbility().addMode(mode); + } + + private DecisiveDenial(final DecisiveDenial card) { + super(card); + } + + @Override + public DecisiveDenial copy() { + return new DecisiveDenial(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DecoratedChampion.java b/Mage.Sets/src/mage/cards/d/DecoratedChampion.java index 5c3ee664b4c2..c0b14f601f59 100644 --- a/Mage.Sets/src/mage/cards/d/DecoratedChampion.java +++ b/Mage.Sets/src/mage/cards/d/DecoratedChampion.java @@ -11,7 +11,7 @@ import mage.constants.CardType; import mage.counters.CounterType; import mage.filter.common.FilterTeamPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/d/DecoyGambit.java b/Mage.Sets/src/mage/cards/d/DecoyGambit.java index a7751d677065..0736cdccef75 100644 --- a/Mage.Sets/src/mage/cards/d/DecoyGambit.java +++ b/Mage.Sets/src/mage/cards/d/DecoyGambit.java @@ -4,8 +4,11 @@ import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.ControllerIdPredicate; @@ -15,14 +18,12 @@ import mage.target.Target; import mage.target.TargetPermanent; import mage.target.targetadjustment.TargetAdjuster; + import java.util.Collection; -import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.UUID; import java.util.stream.Collectors; -import mage.abilities.condition.Condition; -import mage.constants.Zone; /** * @author TheElk801 @@ -88,7 +89,7 @@ public DecoyGambitEffect copy() { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - HashSet permanentToHand = new HashSet(); + Cards permanentToHand = new CardsImpl(); int numberOfCardsToDraw = 0; if (controller == null) { return false; @@ -102,8 +103,6 @@ public boolean apply(Game game, Ability source) { .filter(Objects::nonNull) .collect(Collectors.toList()); for (Permanent permanent : permanents) { - // If a creature targeted by Decoy Gambit changes controller, it’s no longer a legal target. - new DecoyGambitCondition(permanent).apply(game, source); // save current controller Player player = game.getPlayer(permanent.getControllerId()); if (player == null) { continue; @@ -125,31 +124,7 @@ As the Decoy Gambit resolves, first the next opponent in turn order (or, if it cards. After you’ve drawn, the appropriate creatures are all simultaneously returned to their owners’ hands. */ controller.drawCards(numberOfCardsToDraw, source, game); - for (Permanent creature : permanentToHand) { - if (creature != null - && new DecoyGambitCondition(creature).apply(game, source)) { // same controller required - creature.moveToZone(Zone.HAND, source, game, false); - } - } + controller.moveCards(permanentToHand, Zone.HAND, source, game); return true; } } - -class DecoyGambitCondition implements Condition { - - private UUID controllerId; - private final Permanent permanent; - - DecoyGambitCondition(Permanent permanent) { - this.permanent = permanent; - } - - @Override - public boolean apply(Game game, Ability source) { - if (controllerId == null) { // is the original controller set - controllerId = permanent.getControllerId(); // original controller set - } - return (permanent != null - && Objects.equals(controllerId, permanent.getControllerId())); - } -} diff --git a/Mage.Sets/src/mage/cards/d/DecreeOfAnnihilation.java b/Mage.Sets/src/mage/cards/d/DecreeOfAnnihilation.java index a3428894e660..a52cc656a0dd 100644 --- a/Mage.Sets/src/mage/cards/d/DecreeOfAnnihilation.java +++ b/Mage.Sets/src/mage/cards/d/DecreeOfAnnihilation.java @@ -1,18 +1,18 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.CycleTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyAllEffect; import mage.abilities.keyword.CyclingAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.predicate.Predicates; @@ -20,8 +20,9 @@ import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** - * * @author fireshoes */ public final class DecreeOfAnnihilation extends CardImpl { @@ -36,8 +37,7 @@ public DecreeOfAnnihilation(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new CyclingAbility(new ManaCostsImpl("{5}{R}{R}"))); // When you cycle Decree of Annihilation, destroy all lands. - Ability ability = new CycleTriggeredAbility(new DestroyAllEffect(StaticFilters.FILTER_LANDS), false); - this.addAbility(ability); + this.addAbility(new CycleTriggeredAbility(new DestroyAllEffect(StaticFilters.FILTER_LANDS), false)); } private DecreeOfAnnihilation(final DecreeOfAnnihilation card) { @@ -52,21 +52,23 @@ public DecreeOfAnnihilation copy() { class DecreeOfAnnihilationEffect extends OneShotEffect { - private static final FilterPermanent filter = new FilterPermanent(""); + private static final FilterPermanent filter = new FilterPermanent(); static { filter.add(Predicates.or( CardType.ARTIFACT.getPredicate(), CardType.CREATURE.getPredicate(), - CardType.LAND.getPredicate())); + CardType.LAND.getPredicate() + )); } - public DecreeOfAnnihilationEffect() { + DecreeOfAnnihilationEffect() { super(Outcome.Detriment); - staticText = "Exile all artifacts, creatures, and lands from the battlefield, all cards from all graveyards, and all cards from all hands"; + staticText = "Exile all artifacts, creatures, and lands from the battlefield, " + + "all cards from all graveyards, and all cards from all hands"; } - public DecreeOfAnnihilationEffect(final DecreeOfAnnihilationEffect effect) { + private DecreeOfAnnihilationEffect(final DecreeOfAnnihilationEffect effect) { super(effect); } @@ -77,26 +79,24 @@ public DecreeOfAnnihilationEffect copy() { @Override public boolean apply(Game game, Ability source) { - for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - permanent.moveToExile(null, "", source, game); + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Cards cards = new CardsImpl(); + for (Permanent permanent : game.getBattlefield().getActivePermanents( + filter, source.getControllerId(), source.getSourceId(), game + )) { + cards.add(permanent); } for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { Player player = game.getPlayer(playerId); - if (player != null) { - for (UUID cid : player.getHand().copy()) { - Card c = game.getCard(cid); - if (c != null) { - c.moveToExile(null, null, source, game); - } - } - for (UUID cid : player.getGraveyard().copy()) { - Card c = game.getCard(cid); - if (c != null) { - c.moveToExile(null, null, source, game); - } - } + if (player == null) { + continue; } + cards.addAll(player.getHand()); + cards.addAll(player.getGraveyard()); } - return true; + return controller.moveCards(cards, Zone.EXILED, source, game); } } diff --git a/Mage.Sets/src/mage/cards/d/DecreeOfSilence.java b/Mage.Sets/src/mage/cards/d/DecreeOfSilence.java index 6d4d05bf44c4..01d06dc957da 100644 --- a/Mage.Sets/src/mage/cards/d/DecreeOfSilence.java +++ b/Mage.Sets/src/mage/cards/d/DecreeOfSilence.java @@ -20,6 +20,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import mage.target.TargetSpell; /** @@ -34,7 +35,7 @@ public DecreeOfSilence(UUID ownerId, CardSetInfo setInfo) { // Whenever an opponent casts a spell, counter that spell and put a depletion counter on Decree of Silence. If there are three or more depletion counters on Decree of Silence, sacrifice it. Effect effect = new CounterTargetEffect(); effect.setText("counter that spell"); - Ability ability = new SpellCastOpponentTriggeredAbility(Zone.BATTLEFIELD, effect, new FilterSpell("a spell"), + Ability ability = new SpellCastOpponentTriggeredAbility(Zone.BATTLEFIELD, effect, StaticFilters.FILTER_SPELL_A, false, SetTargetPointer.SPELL); effect = new AddCountersSourceEffect(CounterType.DEPLETION.createInstance()); effect.setText("and put a depletion counter on {this}."); diff --git a/Mage.Sets/src/mage/cards/d/DeekahFractalTheorist.java b/Mage.Sets/src/mage/cards/d/DeekahFractalTheorist.java new file mode 100644 index 000000000000..28642f1e746f --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DeekahFractalTheorist.java @@ -0,0 +1,81 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.combat.CantBeBlockedTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.token.QuandrixToken; +import mage.game.stack.Spell; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DeekahFractalTheorist extends CardImpl { + + public DeekahFractalTheorist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it, where X is that spell's mana value. + this.addAbility(new MagecraftAbility(QuandrixToken.getEffect( + DeekahFractalTheoristValue.instance, "Put X +1/+1 counters on it, " + + "where X is that spell's mana value" + ))); + + // {3}{U}: Target creature token can't be blocked this turn. + Ability ability = new SimpleActivatedAbility( + new CantBeBlockedTargetEffect(Duration.EndOfTurn), new ManaCostsImpl("{3}{U}") + ); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_TOKEN)); + this.addAbility(ability); + } + + private DeekahFractalTheorist(final DeekahFractalTheorist card) { + super(card); + } + + @Override + public DeekahFractalTheorist copy() { + return new DeekahFractalTheorist(this); + } +} + +enum DeekahFractalTheoristValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + Spell spell = (Spell) effect.getValue(MagecraftAbility.SPELL_KEY); + return spell != null ? spell.getManaValue() : 0; + } + + @Override + public DeekahFractalTheoristValue copy() { + return instance; + } + + @Override + public String getMessage() { + return ""; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/d/DeepWood.java b/Mage.Sets/src/mage/cards/d/DeepWood.java index c943aa24b628..46f482cdd0b8 100644 --- a/Mage.Sets/src/mage/cards/d/DeepWood.java +++ b/Mage.Sets/src/mage/cards/d/DeepWood.java @@ -74,7 +74,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { DamagePlayerEvent damageEvent = (DamagePlayerEvent) event; if (event.getTargetId().equals(source.getControllerId())) { Permanent permanent = game.getPermanentOrLKIBattlefield(damageEvent.getSourceId()); - if (permanent != null && filter.match(permanent, game)) { + if (filter.match(permanent, game)) { return true; } } diff --git a/Mage.Sets/src/mage/cards/d/DeepfireElemental.java b/Mage.Sets/src/mage/cards/d/DeepfireElemental.java index 9a542a4c5453..fcf49f719e3e 100644 --- a/Mage.Sets/src/mage/cards/d/DeepfireElemental.java +++ b/Mage.Sets/src/mage/cards/d/DeepfireElemental.java @@ -22,7 +22,7 @@ */ public final class DeepfireElemental extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or creature with converted mana cost X"); + private static final FilterPermanent filter = new FilterPermanent("artifact or creature with mana value X"); static { filter.add(Predicates.or( diff --git a/Mage.Sets/src/mage/cards/d/DeeprootElite.java b/Mage.Sets/src/mage/cards/d/DeeprootElite.java index 6b4ecd04ae3e..e41d034419ec 100644 --- a/Mage.Sets/src/mage/cards/d/DeeprootElite.java +++ b/Mage.Sets/src/mage/cards/d/DeeprootElite.java @@ -14,7 +14,7 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/d/DefendTheCampus.java b/Mage.Sets/src/mage/cards/d/DefendTheCampus.java new file mode 100644 index 000000000000..9c9f2cf8457e --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DefendTheCampus.java @@ -0,0 +1,50 @@ +package mage.cards.d; + +import mage.abilities.Mode; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Duration; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.PowerPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DefendTheCampus extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("creature with power 4 or greater"); + + static { + filter.add(new PowerPredicate(ComparisonType.MORE_THAN, 3)); + } + + public DefendTheCampus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{W}"); + + // Choose one — + // • Creatures you control get +1/+1 until end of turn. + this.getSpellAbility().addEffect(new BoostControlledEffect(1, 1, Duration.EndOfTurn)); + + // • Destroy target creature with power 4 or greater. + Mode mode = new Mode(new DestroyTargetEffect()); + mode.addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addMode(mode); + } + + private DefendTheCampus(final DefendTheCampus card) { + super(card); + } + + @Override + public DefendTheCampus copy() { + return new DefendTheCampus(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DefenseOfTheHeart.java b/Mage.Sets/src/mage/cards/d/DefenseOfTheHeart.java index 3b38dbe1db2f..3b65cf7c7785 100644 --- a/Mage.Sets/src/mage/cards/d/DefenseOfTheHeart.java +++ b/Mage.Sets/src/mage/cards/d/DefenseOfTheHeart.java @@ -32,7 +32,7 @@ public DefenseOfTheHeart(UUID ownerId, CardSetInfo setInfo) { TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new SacrificeSourceEffect(), TargetController.YOU, false); ability.addEffect(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 2, StaticFilters.FILTER_CARD_CREATURE), false, Outcome.PutLandInPlay)); DefenseOfTheHeartCondition contition = new DefenseOfTheHeartCondition(); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, contition, "At the beginning of your upkeep, if an opponent controls three or more creatures, sacrifice {this}, search your library for up to two creature cards, and put those cards onto the battlefield. Then shuffle your library")); + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, contition, "At the beginning of your upkeep, if an opponent controls three or more creatures, sacrifice {this}, search your library for up to two creature cards, put those cards onto the battlefield, then shuffle")); } diff --git a/Mage.Sets/src/mage/cards/d/DefiantFalcon.java b/Mage.Sets/src/mage/cards/d/DefiantFalcon.java index bcafd2094b30..471afb147af3 100644 --- a/Mage.Sets/src/mage/cards/d/DefiantFalcon.java +++ b/Mage.Sets/src/mage/cards/d/DefiantFalcon.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; /** @@ -24,11 +24,11 @@ */ public final class DefiantFalcon extends CardImpl { - private static final FilterPermanentCard filter = new FilterPermanentCard("Rebel permanent card with converted mana cost 3 or less"); + private static final FilterPermanentCard filter = new FilterPermanentCard("Rebel permanent card with mana value 3 or less"); static { filter.add(SubType.REBEL.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public DefiantFalcon(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/d/DefiantGreatmaw.java b/Mage.Sets/src/mage/cards/d/DefiantGreatmaw.java index 43724042f45d..7731c6c34716 100644 --- a/Mage.Sets/src/mage/cards/d/DefiantGreatmaw.java +++ b/Mage.Sets/src/mage/cards/d/DefiantGreatmaw.java @@ -14,7 +14,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.target.common.TargetControlledCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/d/DefiantVanguard.java b/Mage.Sets/src/mage/cards/d/DefiantVanguard.java index b5225a24285f..380b596fb274 100644 --- a/Mage.Sets/src/mage/cards/d/DefiantVanguard.java +++ b/Mage.Sets/src/mage/cards/d/DefiantVanguard.java @@ -22,10 +22,9 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.target.common.TargetCardInLibrary; import mage.target.targetpointer.FixedTarget; @@ -36,11 +35,11 @@ */ public final class DefiantVanguard extends CardImpl { - private static final FilterPermanentCard filter = new FilterPermanentCard("Rebel permanent card with converted mana cost 4 or less"); + private static final FilterPermanentCard filter = new FilterPermanentCard("Rebel permanent card with mana value 4 or less"); static { filter.add(SubType.REBEL.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 5)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 5)); } public DefiantVanguard(UUID ownerId, CardSetInfo setInfo) { @@ -111,7 +110,7 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { - return "When {this} blocks, at end of combat, destroy it and all creatures it blocked this turn"; + return "When {this} blocks, at end of combat, destroy it and all creatures it blocked this turn."; } } diff --git a/Mage.Sets/src/mage/cards/d/Deflection.java b/Mage.Sets/src/mage/cards/d/Deflection.java index c7f4c7587166..29759e661527 100644 --- a/Mage.Sets/src/mage/cards/d/Deflection.java +++ b/Mage.Sets/src/mage/cards/d/Deflection.java @@ -7,7 +7,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.NumberOfTargetsPredicate; +import mage.filter.predicate.other.NumberOfTargetsPredicate; import mage.target.TargetSpell; /** diff --git a/Mage.Sets/src/mage/cards/d/DefyDeath.java b/Mage.Sets/src/mage/cards/d/DefyDeath.java index 9691efd57713..a9e0f111bf06 100644 --- a/Mage.Sets/src/mage/cards/d/DefyDeath.java +++ b/Mage.Sets/src/mage/cards/d/DefyDeath.java @@ -25,7 +25,7 @@ public DefyDeath(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{W}{W}"); // Return target creature card from your graveyard to the battlefield. If it's an Angel, put two +1/+1 counters on it. - this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect(false, false)); + this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); this.getSpellAbility().addEffect(new DefyDeathEffect()); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); } diff --git a/Mage.Sets/src/mage/cards/d/DelifsCone.java b/Mage.Sets/src/mage/cards/d/DelifsCone.java index 209fa888223f..afec01670b9d 100644 --- a/Mage.Sets/src/mage/cards/d/DelifsCone.java +++ b/Mage.Sets/src/mage/cards/d/DelifsCone.java @@ -1,46 +1,44 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility; +import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.GainLifeEffect; -import mage.abilities.effects.common.continuous.AssignNoCombatDamageSourceEffect; -import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; +import mage.game.events.DamageEvent; +import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; +import java.util.UUID; + /** - * * @author MarcoMarin */ public final class DelifsCone extends CardImpl { public DelifsCone(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{0}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{0}"); - Ability ability2 = new AttacksAndIsNotBlockedTriggeredAbility(new DelifsConeEffect(), true); - ability2.addEffect(new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn)); // {tap}, Sacrifice Delif's Cone: This turn, when target creature you control attacks and isn't blocked, you may gain life equal to its power. If you do, it assigns no combat damage this turn. - SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new GainAbilityTargetEffect(ability2, Duration.EndOfTurn), - new TapSourceCost()); + Ability ability = new SimpleActivatedAbility( + new CreateDelayedTriggeredAbilityEffect( + new DelifsConeTriggeredAbility(), false + ), new TapSourceCost() + ); ability.addCost(new SacrificeSourceCost()); ability.addTarget(new TargetControlledCreaturePermanent()); - this.addAbility(ability); - } private DelifsCone(final DelifsCone card) { @@ -52,26 +50,105 @@ public DelifsCone copy() { return new DelifsCone(this); } } -class DelifsConeEffect extends OneShotEffect{ - - public DelifsConeEffect() { - super(Outcome.Damage); - this.setText("you may gain life equal to its power"); - } - - public DelifsConeEffect(final DelifsConeEffect effect) { + +class DelifsConeTriggeredAbility extends DelayedTriggeredAbility { + + DelifsConeTriggeredAbility() { + super(new DelifsConeLifeEffect(), Duration.EndOfTurn, false, true); + this.addEffect(new DelifsConePreventEffect()); + } + + private DelifsConeTriggeredAbility(final DelifsConeTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.UNBLOCKED_ATTACKER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getTargetId().equals(getFirstTarget()); + } + + @Override + public DelifsConeTriggeredAbility copy() { + return new DelifsConeTriggeredAbility(this); + } + + @Override + public String getRule() { + return "This turn, when target creature you control attacks and isn't blocked, " + + "you may gain life equal to its power. If you do, it assigns no combat damage this turn."; + } +} + +class DelifsConeLifeEffect extends OneShotEffect { + + DelifsConeLifeEffect() { + super(Outcome.Benefit); + } + + private DelifsConeLifeEffect(final DelifsConeLifeEffect effect) { super(effect); } - + @Override - public DelifsConeEffect copy() { - return new DelifsConeEffect(this); + public DelifsConeLifeEffect copy() { + return new DelifsConeLifeEffect(this); } @Override public boolean apply(Game game, Ability source) { - Permanent perm = game.getPermanent(source.getSourceId()); - GainLifeEffect lifeEffect = new GainLifeEffect(perm.getPower().getValue()); - return lifeEffect.apply(game, source); + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source)); + if (player != null || permanent != null) { + player.gainLife(permanent.getPower().getValue(), game, source); + return true; + } + return false; } -} \ No newline at end of file +} + +class DelifsConePreventEffect extends ReplacementEffectImpl { + + DelifsConePreventEffect() { + super(Duration.EndOfTurn, Outcome.Neutral); + } + + private DelifsConePreventEffect(final DelifsConePreventEffect effect) { + super(effect); + } + + @Override + public DelifsConePreventEffect copy() { + return new DelifsConePreventEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + switch (event.getType()) { + case DAMAGE_PERMANENT: + case DAMAGE_PLAYER: + return true; + default: + return false; + } + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return ((DamageEvent) event).isCombatDamage() && event.getTargetId().equals(targetPointer.getFirst(game, source)); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DelifsCube.java b/Mage.Sets/src/mage/cards/d/DelifsCube.java index 939115beff79..62a9cf9d683d 100644 --- a/Mage.Sets/src/mage/cards/d/DelifsCube.java +++ b/Mage.Sets/src/mage/cards/d/DelifsCube.java @@ -1,55 +1,49 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility; +import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.RegenerateTargetEffect; -import mage.abilities.effects.common.continuous.AssignNoCombatDamageSourceEffect; -import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.game.events.DamageEvent; +import mage.game.events.GameEvent; import mage.target.common.TargetControlledCreaturePermanent; +import java.util.UUID; + /** - * - * @author MarcoMarin + * @author TheElk801 */ public final class DelifsCube extends CardImpl { public DelifsCube(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{1}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); - Ability ability2 = new AttacksAndIsNotBlockedTriggeredAbility(new DelifsCubeEffect(this.getId()), false); - ability2.addEffect(new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn)); // {2}, {tap}: This turn, when target creature you control attacks and isn't blocked, it assigns no combat damage this turn and you put a cube counter on Delif's Cube. - SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new GainAbilityTargetEffect(ability2, Duration.EndOfTurn), - new ManaCostsImpl("{2}")); + Ability ability = new SimpleActivatedAbility( + new CreateDelayedTriggeredAbilityEffect(new DelifsCubeTriggeredAbility()), new GenericManaCost(2) + ); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetControlledCreaturePermanent()); - this.addAbility(ability); + // {2}, Remove a cube counter from Delif's Cube: Regenerate target creature. - SimpleActivatedAbility ability3 = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new RegenerateTargetEffect(), - new ManaCostsImpl("{2}")); - ability3.addCost(new RemoveCountersSourceCost(CounterType.CUBE.createInstance())); - ability3.addTarget(new TargetControlledCreaturePermanent()); - - this.addAbility(ability3); + ability = new SimpleActivatedAbility(new RegenerateTargetEffect(), new GenericManaCost(2)); + ability.addCost(new RemoveCountersSourceCost(CounterType.CUBE.createInstance())); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); } private DelifsCube(final DelifsCube card) { @@ -62,31 +56,77 @@ public DelifsCube copy() { } } -class DelifsCubeEffect extends OneShotEffect{ - - private UUID cubeId; - - public DelifsCubeEffect(UUID cubeId) { - super(Outcome.Benefit); - this.cubeId = cubeId; - this.setText("This turn, when target creature you control attacks and isn't blocked, it assigns no combat damage this turn and you put a cube counter on Delif's Cube"); - } - - public DelifsCubeEffect(final DelifsCubeEffect effect) { +class DelifsCubeTriggeredAbility extends DelayedTriggeredAbility { + + DelifsCubeTriggeredAbility() { + super(new DelifsCubePreventEffect(), Duration.EndOfTurn, false, false); + this.addEffect(new AddCountersSourceEffect(CounterType.CUBE.createInstance())); + } + + private DelifsCubeTriggeredAbility(final DelifsCubeTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.UNBLOCKED_ATTACKER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getTargetId().equals(getFirstTarget()); + } + + @Override + public DelifsCubeTriggeredAbility copy() { + return new DelifsCubeTriggeredAbility(this); + } + + @Override + public String getRule() { + return "This turn, when target creature you control attacks and isn't blocked, " + + "it assigns no combat damage this turn and you put a cube counter on {this}."; + } +} + +class DelifsCubePreventEffect extends ReplacementEffectImpl { + + DelifsCubePreventEffect() { + super(Duration.EndOfTurn, Outcome.Neutral); + } + + private DelifsCubePreventEffect(final DelifsCubePreventEffect effect) { super(effect); - this.cubeId = effect.cubeId; } - + @Override - public DelifsCubeEffect copy() { - return new DelifsCubeEffect(this); + public DelifsCubePreventEffect copy() { + return new DelifsCubePreventEffect(this); } @Override public boolean apply(Game game, Ability source) { - Permanent perm = game.getPermanent(cubeId); - if (perm == null) return false; - perm.addCounters(CounterType.CUBE.createInstance(), source.getControllerId(), source, game); - return true; + return true; } -} \ No newline at end of file + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + switch (event.getType()) { + case DAMAGE_PERMANENT: + case DAMAGE_PLAYER: + return true; + default: + return false; + } + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return ((DamageEvent) event).isCombatDamage() && event.getTargetId().equals(targetPointer.getFirst(game, source)); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DeliverUntoEvil.java b/Mage.Sets/src/mage/cards/d/DeliverUntoEvil.java index dd8cf7be8bd0..5f1a212885b4 100644 --- a/Mage.Sets/src/mage/cards/d/DeliverUntoEvil.java +++ b/Mage.Sets/src/mage/cards/d/DeliverUntoEvil.java @@ -36,7 +36,7 @@ public DeliverUntoEvil(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 4)); // Exile Deliver Unto Evil. - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private DeliverUntoEvil(final DeliverUntoEvil card) { diff --git a/Mage.Sets/src/mage/cards/d/DelverOfSecrets.java b/Mage.Sets/src/mage/cards/d/DelverOfSecrets.java index f6cd8e72ea5d..b0c282c70dcb 100644 --- a/Mage.Sets/src/mage/cards/d/DelverOfSecrets.java +++ b/Mage.Sets/src/mage/cards/d/DelverOfSecrets.java @@ -82,7 +82,7 @@ public boolean apply(Game game, Ability source) { Cards cards = new CardsImpl(); cards.add(card); player.lookAtCards(sourcePermanent.getName(), cards, game); - if (player.chooseUse(Outcome.DrawCard, "Do you wish to reveal the card at the top of the library?", source, game)) { + if (player.chooseUse(Outcome.DrawCard, "Reveal the top card of your library?", source, game)) { player.revealCards(sourcePermanent.getName(), cards, game); if (filter.match(card, game)) { return new TransformSourceEffect(true, true).apply(game, source); diff --git a/Mage.Sets/src/mage/cards/d/DementiaSliver.java b/Mage.Sets/src/mage/cards/d/DementiaSliver.java index c3783a8d8c6e..85284e15036c 100644 --- a/Mage.Sets/src/mage/cards/d/DementiaSliver.java +++ b/Mage.Sets/src/mage/cards/d/DementiaSliver.java @@ -48,7 +48,7 @@ public DementiaSliver(UUID ownerId, CardSetInfo setInfo) { "All Slivers have \"{T}: Choose a card name. " + "Target opponent reveals a card at random from their hand." + " If that card has the chosen name, that player discards it." - + " Activate this ability only during your turn.\"" + + " Activate only during your turn.\"" ) )); } diff --git a/Mage.Sets/src/mage/cards/d/DemonicTaskmaster.java b/Mage.Sets/src/mage/cards/d/DemonicTaskmaster.java index dd5a6f3811a8..4fea076e2b98 100644 --- a/Mage.Sets/src/mage/cards/d/DemonicTaskmaster.java +++ b/Mage.Sets/src/mage/cards/d/DemonicTaskmaster.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/d/DemonlordBelzenlok.java b/Mage.Sets/src/mage/cards/d/DemonlordBelzenlok.java index 01bc5612a114..6c11b5e926da 100644 --- a/Mage.Sets/src/mage/cards/d/DemonlordBelzenlok.java +++ b/Mage.Sets/src/mage/cards/d/DemonlordBelzenlok.java @@ -55,7 +55,7 @@ class DemonlordBelzenlokEffect extends OneShotEffect { public DemonlordBelzenlokEffect() { super(Outcome.Benefit); staticText = "exile cards from the top of your library until you exile a nonland card, then put that card into your hand. " - + "If the card's converted mana cost is 4 or greater, repeat this process. " + + "If the card's mana value is 4 or greater, repeat this process. " + "{this} deals 1 damage to you for each card put into your hand this way"; } @@ -77,7 +77,7 @@ public boolean apply(Game game, Ability source) { if (card != null) { controller.moveCards(card, Zone.EXILED, source, game); if (!card.isLand()) { - if (card.getConvertedManaCost() < 4) { + if (card.getManaValue() < 4) { cont = false; } controller.moveCards(card, Zone.HAND, source, game); diff --git a/Mage.Sets/src/mage/cards/d/DemonlordOfAshmouth.java b/Mage.Sets/src/mage/cards/d/DemonlordOfAshmouth.java index 3dc6018067ef..b059b1f5fbb9 100644 --- a/Mage.Sets/src/mage/cards/d/DemonlordOfAshmouth.java +++ b/Mage.Sets/src/mage/cards/d/DemonlordOfAshmouth.java @@ -13,7 +13,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledPermanent; /** diff --git a/Mage.Sets/src/mage/cards/d/DemonsHerald.java b/Mage.Sets/src/mage/cards/d/DemonsHerald.java index 29df9abb10fc..bb9166156027 100644 --- a/Mage.Sets/src/mage/cards/d/DemonsHerald.java +++ b/Mage.Sets/src/mage/cards/d/DemonsHerald.java @@ -1,9 +1,6 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeTargetCost; @@ -14,34 +11,26 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.mageobject.ColorPredicate; import mage.filter.predicate.mageobject.NamePredicate; import mage.target.common.TargetCardInLibrary; -import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetControlledCreatureEachColor; + +import java.util.UUID; /** - * * @author North */ public final class DemonsHerald extends CardImpl { private static final FilterCard filter = new FilterCard("card named Prince of Thralls"); - private static final FilterControlledCreaturePermanent filterBlue = new FilterControlledCreaturePermanent("a blue creature"); - private static final FilterControlledCreaturePermanent filterBlack = new FilterControlledCreaturePermanent("a black creature"); - private static final FilterControlledCreaturePermanent filterRed = new FilterControlledCreaturePermanent("a red creature"); static { filter.add(new NamePredicate("Prince of Thralls")); - filterBlue.add(new ColorPredicate(ObjectColor.BLUE)); - filterBlack.add(new ColorPredicate(ObjectColor.BLACK)); - filterRed.add(new ColorPredicate(ObjectColor.RED)); } public DemonsHerald(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WIZARD); @@ -50,14 +39,11 @@ public DemonsHerald(UUID ownerId, CardSetInfo setInfo) { // {2}{B}, {tap}, Sacrifice a blue creature, a black creature, and a red creature: // Search your library for a card named Prince of Thralls and put it onto the battlefield. Then shuffle your library. - TargetCardInLibrary target = new TargetCardInLibrary(1, 1, new FilterCard(filter)); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new SearchLibraryPutInPlayEffect(target), - new ManaCostsImpl("{2}{B}")); + Ability ability = new SimpleActivatedAbility(new SearchLibraryPutInPlayEffect( + new TargetCardInLibrary(filter)), new ManaCostsImpl<>("{2}{B}") + ); ability.addCost(new TapSourceCost()); - ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filterBlue, false))); - ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filterBlack, false))); - ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filterRed, false))); + ability.addCost(new SacrificeTargetCost(new TargetControlledCreatureEachColor("UBR"))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/d/DenizenOfTheDeep.java b/Mage.Sets/src/mage/cards/d/DenizenOfTheDeep.java index c5535182cc2b..a0857acaa647 100644 --- a/Mage.Sets/src/mage/cards/d/DenizenOfTheDeep.java +++ b/Mage.Sets/src/mage/cards/d/DenizenOfTheDeep.java @@ -11,7 +11,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/d/DenyingWind.java b/Mage.Sets/src/mage/cards/d/DenyingWind.java index 5d598abcdfdb..aa69afb4acdb 100644 --- a/Mage.Sets/src/mage/cards/d/DenyingWind.java +++ b/Mage.Sets/src/mage/cards/d/DenyingWind.java @@ -45,7 +45,7 @@ class DenyingWindEffect extends OneShotEffect { public DenyingWindEffect() { super(Outcome.Neutral); - staticText = "search target player's library for up to seven cards and exile them. Then that player shuffles their library"; + staticText = "search target player's library for up to seven cards and exile them. Then that player shuffles"; } public DenyingWindEffect(final DenyingWindEffect effect) { diff --git a/Mage.Sets/src/mage/cards/d/DeputyOfAcquittals.java b/Mage.Sets/src/mage/cards/d/DeputyOfAcquittals.java index cb5c1e2db4dd..44e8443105af 100644 --- a/Mage.Sets/src/mage/cards/d/DeputyOfAcquittals.java +++ b/Mage.Sets/src/mage/cards/d/DeputyOfAcquittals.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/d/DescendantsPath.java b/Mage.Sets/src/mage/cards/d/DescendantsPath.java index 6bf349776a5f..a7ed285fd5ee 100644 --- a/Mage.Sets/src/mage/cards/d/DescendantsPath.java +++ b/Mage.Sets/src/mage/cards/d/DescendantsPath.java @@ -51,8 +51,8 @@ public DescendantsPathEffect() { super(Outcome.Discard); this.staticText = "reveal the top card of your library. If it's a creature " + "card that shares a creature type with a creature you control, " - + "you may cast that card without paying its mana cost. Otherwise, " - + "put that card on the bottom of your library"; + + "you may cast it without paying its mana cost. If you don't cast it, " + + "put it on the bottom of your library"; } public DescendantsPathEffect(final DescendantsPathEffect effect) { diff --git a/Mage.Sets/src/mage/cards/d/Desertion.java b/Mage.Sets/src/mage/cards/d/Desertion.java index a13de6ba8ffc..8fe5b50c07b0 100644 --- a/Mage.Sets/src/mage/cards/d/Desertion.java +++ b/Mage.Sets/src/mage/cards/d/Desertion.java @@ -1,7 +1,5 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; @@ -18,8 +16,10 @@ import mage.game.events.ZoneChangeEvent; import mage.target.TargetSpell; +import java.util.Objects; +import java.util.UUID; + /** - * * @author Quercitron */ public final class Desertion extends CardImpl { @@ -30,7 +30,7 @@ public Desertion(UUID ownerId, CardSetInfo setInfo) { // Counter target spell. this.getSpellAbility().addEffect(new CounterTargetEffect()); this.getSpellAbility().addTarget(new TargetSpell()); - + // If an artifact or creature spell is countered this way, put that card onto the battlefield under your control instead of into its owner's graveyard. this.addAbility(new SimpleStaticAbility(Zone.STACK, new DesertionReplacementEffect())); } @@ -80,13 +80,13 @@ public boolean checksEventType(GameEvent event, Game game) { } @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (!event.getSourceId().equals(source.getSourceId()) + public boolean applies(GameEvent event, Ability source, Game game) { + if (!Objects.equals(event.getSourceId(), source.getSourceId()) || !(((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD)) { return false; } - MageObject mageObject = game.getObject(event.getTargetId()); - return mageObject != null + MageObject mageObject = game.getObject(event.getTargetId()); + return mageObject != null && (mageObject.isArtifact() || mageObject.isCreature()); } } diff --git a/Mage.Sets/src/mage/cards/d/DesolationGiant.java b/Mage.Sets/src/mage/cards/d/DesolationGiant.java index 5ee4f6f2c8c9..2ee454d29aa5 100644 --- a/Mage.Sets/src/mage/cards/d/DesolationGiant.java +++ b/Mage.Sets/src/mage/cards/d/DesolationGiant.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/d/DesolationTwin.java b/Mage.Sets/src/mage/cards/d/DesolationTwin.java index c20d3e38e453..82d145f382cc 100644 --- a/Mage.Sets/src/mage/cards/d/DesolationTwin.java +++ b/Mage.Sets/src/mage/cards/d/DesolationTwin.java @@ -70,6 +70,6 @@ public DesolationTwinOnCastAbility copy() { @Override public String getRule() { - return "When you cast {this}, " + super.getRule(); + return "When you cast this spell, " + super.getRule(); } } diff --git a/Mage.Sets/src/mage/cards/d/Despark.java b/Mage.Sets/src/mage/cards/d/Despark.java index cca3d6bcae59..1cd082bc48e1 100644 --- a/Mage.Sets/src/mage/cards/d/Despark.java +++ b/Mage.Sets/src/mage/cards/d/Despark.java @@ -6,7 +6,7 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetPermanent; import java.util.UUID; @@ -17,10 +17,10 @@ public final class Despark extends CardImpl { private static final FilterPermanent filter - = new FilterPermanent("permanent with converted mana cost 4 or greater"); + = new FilterPermanent("permanent with mana value 4 or greater"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 3)); + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 3)); } public Despark(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/d/DesperateGambit.java b/Mage.Sets/src/mage/cards/d/DesperateGambit.java index c9c97dd9bb0a..2e9d4d1ce221 100644 --- a/Mage.Sets/src/mage/cards/d/DesperateGambit.java +++ b/Mage.Sets/src/mage/cards/d/DesperateGambit.java @@ -80,8 +80,7 @@ public DesperateGambitEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE || - event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER || + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT || event.getType() == GameEvent.EventType.DAMAGE_PLAYER; } diff --git a/Mage.Sets/src/mage/cards/d/DetentionVortex.java b/Mage.Sets/src/mage/cards/d/DetentionVortex.java new file mode 100644 index 000000000000..aca5121045ab --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DetentionVortex.java @@ -0,0 +1,57 @@ +package mage.cards.d; + +import mage.abilities.ActivatedAbility; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DestroySourceEffect; +import mage.abilities.effects.common.combat.CantBlockAttackActivateAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DetentionVortex extends CardImpl { + + public DetentionVortex(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + + this.subtype.add(SubType.AURA); + + // Enchant nonland permanent + TargetPermanent auraTarget = new TargetPermanent(StaticFilters.FILTER_PERMANENT_NON_LAND); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + + // Enchanted permanent can't attack or block, and its activated abilities can't be activated. + this.addAbility(new SimpleStaticAbility(new CantBlockAttackActivateAttachedEffect("permanent"))); + + // {3}: Destroy Detention Vortex. Only your opponents may activate this ability and only as a sorcery. + ActivatedAbility ability = new ActivateAsSorceryActivatedAbility( + new DestroySourceEffect(), new GenericManaCost(3) + ); + ability.setMayActivate(TargetController.OPPONENT); + this.addAbility(ability); + } + + private DetentionVortex(final DetentionVortex card) { + super(card); + } + + @Override + public DetentionVortex copy() { + return new DetentionVortex(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/Detonate.java b/Mage.Sets/src/mage/cards/d/Detonate.java index 7f304d0b8cd3..783fe1d1ffa5 100644 --- a/Mage.Sets/src/mage/cards/d/Detonate.java +++ b/Mage.Sets/src/mage/cards/d/Detonate.java @@ -11,7 +11,7 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.filter.common.FilterArtifactPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.target.common.TargetArtifactPermanent; import mage.target.targetadjustment.TargetAdjuster; @@ -28,7 +28,7 @@ public Detonate(UUID ownerId, CardSetInfo setInfo) { // Destroy target artifact with converted mana cost X. It can't be regenerated. Detonate deals X damage to that artifact's controller. this.getSpellAbility().addEffect(new DestroyTargetEffect(true)); - this.getSpellAbility().addTarget(new TargetArtifactPermanent(new FilterArtifactPermanent("artifact with converted mana cost X"))); + this.getSpellAbility().addTarget(new TargetArtifactPermanent(new FilterArtifactPermanent("artifact with mana value X"))); Effect effect = new DamageTargetControllerEffect(ManacostVariableValue.instance); effect.setText("{this} deals X damage to that artifact's controller"); this.getSpellAbility().addEffect(effect); @@ -52,8 +52,8 @@ enum DetonateAdjuster implements TargetAdjuster { public void adjustTargets(Ability ability, Game game) { ability.getTargets().clear(); int xValue = ability.getManaCostsToPay().getX(); - FilterArtifactPermanent filter = new FilterArtifactPermanent("artifact with converted mana cost X"); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, xValue)); + FilterArtifactPermanent filter = new FilterArtifactPermanent("artifact with mana value X"); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, xValue)); ability.addTarget(new TargetArtifactPermanent(filter)); } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/d/DevastatingMastery.java b/Mage.Sets/src/mage/cards/d/DevastatingMastery.java new file mode 100644 index 000000000000..204e2298e46c --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DevastatingMastery.java @@ -0,0 +1,95 @@ +package mage.cards.d; + +import mage.abilities.Ability; +import mage.abilities.costs.AlternativeCostSourceAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DevastatingMastery extends CardImpl { + + public DevastatingMastery(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{W}{W}{W}{W}"); + + // You may pay {2}{W}{W} rather than pay this spell's mana cost. + Ability costAbility = new AlternativeCostSourceAbility(new ManaCostsImpl<>("{2}{W}{W}")); + this.addAbility(costAbility); + + // If the {2}{W}{W} cost was paid, an opponent chooses up to two nonland permanents they control and returns them to their owner's hand. + this.getSpellAbility().addEffect(new DevastatingMasteryAlternativeCostEffect(costAbility.getOriginalId())); + + // Destroy all nonland permanents. + this.getSpellAbility().addEffect(new DestroyAllEffect(StaticFilters.FILTER_PERMANENTS_NON_LAND)); + } + + private DevastatingMastery(final DevastatingMastery card) { + super(card); + } + + @Override + public DevastatingMastery copy() { + return new DevastatingMastery(this); + } +} + +class DevastatingMasteryAlternativeCostEffect extends OneShotEffect { + + private final UUID alternativeCostOriginalID; + + DevastatingMasteryAlternativeCostEffect(UUID alternativeCostOriginalID) { + super(Outcome.Detriment); + staticText = "if the {2}{W}{W} cost was paid, an opponent chooses up to two nonland permanents " + + "they control and returns them to their owner's hand.
"; + this.alternativeCostOriginalID = alternativeCostOriginalID; + } + + private DevastatingMasteryAlternativeCostEffect(DevastatingMasteryAlternativeCostEffect effect) { + super(effect); + this.alternativeCostOriginalID = effect.alternativeCostOriginalID; + } + + @Override + public DevastatingMasteryAlternativeCostEffect copy() { + return new DevastatingMasteryAlternativeCostEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (!AlternativeCostSourceAbility.getActivatedStatus( + game, source, this.alternativeCostOriginalID, false + )) { + return false; + } + + Player player = game.getPlayer(source.getControllerId()); + TargetOpponent targetOpponent = new TargetOpponent(true); + if (!player.chooseTarget(Outcome.DrawCard, targetOpponent, source, game)) { + return false; + } + Player opponent = game.getPlayer(targetOpponent.getFirstTarget()); + if (opponent == null) { + return false; + } + TargetPermanent target = new TargetPermanent( + 0, 2, StaticFilters.FILTER_PERMANENTS_NON_LAND, true + ); + opponent.choose(Outcome.ReturnToHand, target, source.getSourceId(), game); + return opponent.moveCards(new CardsImpl(target.getTargets()), Zone.HAND, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DevotedCropMate.java b/Mage.Sets/src/mage/cards/d/DevotedCropMate.java index 438256ca7e85..2ff3a4902d72 100644 --- a/Mage.Sets/src/mage/cards/d/DevotedCropMate.java +++ b/Mage.Sets/src/mage/cards/d/DevotedCropMate.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.constants.ComparisonType; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInYourGraveyard; /** @@ -22,10 +22,10 @@ */ public final class DevotedCropMate extends CardImpl { - private static final FilterCreatureCard filter = new FilterCreatureCard("creature card with converted mana cost 2 or less from your graveyard"); + private static final FilterCreatureCard filter = new FilterCreatureCard("creature card with mana value 2 or less from your graveyard"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); } public DevotedCropMate(UUID ownerId, CardSetInfo setInfo) { @@ -38,7 +38,7 @@ public DevotedCropMate(UUID ownerId, CardSetInfo setInfo) { // You may exert Devoted Crop-Mate as it attacks. When you do, return target creature card with converted mana cost 2 or less from your graveyard to the battlefield. Effect effect = new ReturnFromGraveyardToBattlefieldTargetEffect(); - effect.setText("return target creature card with converted mana cost 2 or less from your graveyard to the battlefield"); + effect.setText("return target creature card with mana value 2 or less from your graveyard to the battlefield"); BecomesExertSourceTriggeredAbility ability = new BecomesExertSourceTriggeredAbility(effect); ability.addTarget(new TargetCardInYourGraveyard(filter)); addAbility(new ExertAbility(ability)); diff --git a/Mage.Sets/src/mage/cards/d/DevouringTendrils.java b/Mage.Sets/src/mage/cards/d/DevouringTendrils.java new file mode 100644 index 000000000000..b025e80bb8e9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DevouringTendrils.java @@ -0,0 +1,130 @@ +package mage.cards.d; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageWithPowerFromOneToAnotherTargetEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.target.Target; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class DevouringTendrils extends CardImpl { + + private static final FilterPermanent filter = new FilterCreatureOrPlaneswalkerPermanent("creature or planeswalker you don't control"); + + static { + filter.add(TargetController.NOT_YOU.getControllerPredicate()); + } + + public DevouringTendrils(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}"); + + // Target creature you control deals damage equal to its power to target creature or planeswalker you don't control. When the permanent you don't control dies this turn, you gain 2 life. + Effect effect = new DamageWithPowerFromOneToAnotherTargetEffect(); + this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + TargetPermanent target = new TargetPermanent(filter); + target.setTargetTag(2); + this.getSpellAbility().addTarget(target); + + this.getSpellAbility().addEffect(new DevouringTendrilsEffect()); + } + + private DevouringTendrils(final DevouringTendrils card) { + super(card); + } + + @Override + public DevouringTendrils copy() { + return new DevouringTendrils(this); + } +} + +class DevouringTendrilsEffect extends OneShotEffect { + + public DevouringTendrilsEffect() { + super(Outcome.Benefit); + this.staticText = "when the permanent you don't control dies this turn, you gain 2 life"; + } + + public DevouringTendrilsEffect(final DevouringTendrilsEffect effect) { + super(effect); + } + + @Override + public DevouringTendrilsEffect copy() { + return new DevouringTendrilsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Target target = source.getTargets().stream() + .filter(t -> t.getTargetTag() == 2) + .findFirst() + .orElseThrow(() -> new IllegalStateException("Expected to find target with tag 2 but found none")); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent != null) { + DelayedTriggeredAbility delayedAbility = new DevouringTendrilsDelayedTriggeredAbility(new MageObjectReference(permanent, game)); + game.addDelayedTriggeredAbility(delayedAbility, source); + return true; + } + return false; + } +} + +class DevouringTendrilsDelayedTriggeredAbility extends DelayedTriggeredAbility { + + private final MageObjectReference mor; + + DevouringTendrilsDelayedTriggeredAbility(MageObjectReference mor){ + super(new GainLifeEffect(2), Duration.EndOfTurn, false); + this.mor = mor; + } + + DevouringTendrilsDelayedTriggeredAbility(DevouringTendrilsDelayedTriggeredAbility ability) { + super(ability); + this.mor = ability.mor; + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + return zEvent.isDiesEvent() && mor.refersTo(zEvent.getTarget(), game); + } + + @Override + public DevouringTendrilsDelayedTriggeredAbility copy() { + return new DevouringTendrilsDelayedTriggeredAbility(this); + } + + @Override + public String getRule() { + return "When the permanent you don't control dies this turn, you gain 2 life."; + } +} diff --git a/Mage.Sets/src/mage/cards/d/DiabolicRevelation.java b/Mage.Sets/src/mage/cards/d/DiabolicRevelation.java index d7e3edd8be2e..b2a0598d11a3 100644 --- a/Mage.Sets/src/mage/cards/d/DiabolicRevelation.java +++ b/Mage.Sets/src/mage/cards/d/DiabolicRevelation.java @@ -1,30 +1,25 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.dynamicvalue.common.ManacostVariableValue; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; +import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; +import java.util.UUID; + /** - * * @author North */ public final class DiabolicRevelation extends CardImpl { public DiabolicRevelation(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{X}{3}{B}{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{3}{B}{B}"); // Search your library for up to X cards and put those cards into your hand. Then shuffle your library. this.getSpellAbility().addEffect(new DiabolicRevelationEffect()); @@ -42,12 +37,12 @@ public DiabolicRevelation copy() { class DiabolicRevelationEffect extends OneShotEffect { - public DiabolicRevelationEffect() { + DiabolicRevelationEffect() { super(Outcome.Benefit); - this.staticText = "Search your library for up to X cards and put those cards into your hand. Then shuffle your library"; + this.staticText = "Search your library for up to X cards and put those cards into your hand. Then shuffle"; } - public DiabolicRevelationEffect(final DiabolicRevelationEffect effect) { + private DiabolicRevelationEffect(final DiabolicRevelationEffect effect) { super(effect); } @@ -58,23 +53,24 @@ public DiabolicRevelationEffect copy() { @Override public boolean apply(Game game, Ability source) { - int amount = ManacostVariableValue.instance.calculate(game, source, this); - TargetCardInLibrary target = new TargetCardInLibrary(0, amount, new FilterCard()); - Player player = game.getPlayer(source.getControllerId()); if (player == null) { return false; } - - if (player.searchLibrary(target, source, game)) { - for (UUID cardId : target.getTargets()) { - Card card = player.getLibrary().remove(cardId, game); - if (card != null) { - card.moveToZone(Zone.HAND, source, game, false); - } + TargetCardInLibrary target = new TargetCardInLibrary( + 0, source.getManaCostsToPay().getX(), StaticFilters.FILTER_CARD + ); + player.searchLibrary(target, source, game); + Cards cards = new CardsImpl(); + for (UUID targetId : target.getTargets()) { + Card card = player.getLibrary().getCard(targetId, game); + if (card != null) { + cards.add(card); } } - + if (!cards.isEmpty()) { + player.moveCards(cards, Zone.HAND, source, game); + } player.shuffleLibrary(source, game); return true; } diff --git a/Mage.Sets/src/mage/cards/d/Dichotomancy.java b/Mage.Sets/src/mage/cards/d/Dichotomancy.java index 7a49b1fc73cc..984dd3330737 100644 --- a/Mage.Sets/src/mage/cards/d/Dichotomancy.java +++ b/Mage.Sets/src/mage/cards/d/Dichotomancy.java @@ -60,7 +60,7 @@ class DichotomancyEffect extends OneShotEffect { super(Outcome.PutCardInPlay); this.staticText = "For each tapped nonland permanent target opponent controls, " + "search that player's library for a card with the same name as that permanent. " + - "Put those cards onto the battlefield under your control, then that player shuffles their library."; + "Put those cards onto the battlefield under your control, then that player shuffles."; } private DichotomancyEffect(DichotomancyEffect effect) { diff --git a/Mage.Sets/src/mage/cards/d/DictateOfTheTwinGods.java b/Mage.Sets/src/mage/cards/d/DictateOfTheTwinGods.java index 91745fefd001..6a870e43b6cf 100644 --- a/Mage.Sets/src/mage/cards/d/DictateOfTheTwinGods.java +++ b/Mage.Sets/src/mage/cards/d/DictateOfTheTwinGods.java @@ -64,9 +64,8 @@ public DictateOfTheTwinGodsEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { switch (event.getType()) { - case DAMAGE_CREATURE: + case DAMAGE_PERMANENT: case DAMAGE_PLAYER: - case DAMAGE_PLANESWALKER: return true; default: return false; diff --git a/Mage.Sets/src/mage/cards/d/DigsiteEngineer.java b/Mage.Sets/src/mage/cards/d/DigsiteEngineer.java new file mode 100644 index 000000000000..a8e3a0e1c0a5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DigsiteEngineer.java @@ -0,0 +1,47 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterSpell; +import mage.filter.common.FilterArtifactSpell; +import mage.game.permanent.token.KarnConstructToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DigsiteEngineer extends CardImpl { + + private static final FilterSpell filter = new FilterArtifactSpell("an artifact spell"); + + public DigsiteEngineer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.DWARF); + this.subtype.add(SubType.ARTIFICER); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Whenever you cast an artifact spell, you may pay {2}. If you do, create a 0/0 colorless Construct artifact creature token with "This creature gets +1/+1 for each artifact you control." + this.addAbility(new SpellCastControllerTriggeredAbility(new DoIfCostPaid( + new CreateTokenEffect(new KarnConstructToken()), new GenericManaCost(2) + ), filter, false)); + } + + private DigsiteEngineer(final DigsiteEngineer card) { + super(card); + } + + @Override + public DigsiteEngineer copy() { + return new DigsiteEngineer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DiluvianPrimordial.java b/Mage.Sets/src/mage/cards/d/DiluvianPrimordial.java index b10ec3390d35..d2265677e14b 100644 --- a/Mage.Sets/src/mage/cards/d/DiluvianPrimordial.java +++ b/Mage.Sets/src/mage/cards/d/DiluvianPrimordial.java @@ -13,10 +13,9 @@ import mage.constants.*; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; import mage.players.Player; import mage.target.Target; @@ -88,7 +87,7 @@ public DiluvianPrimordialEffect() { super(Outcome.PlayForFree); this.staticText = "for each opponent, you may cast up to one target " + "instant or sorcery card from that player's graveyard without " - + "paying its mana cost. If a card cast this way would be put " + + "paying its mana cost. If a spell cast this way would be put " + "into a graveyard this turn, exile it instead"; } diff --git a/Mage.Sets/src/mage/cards/d/DimensionalBreach.java b/Mage.Sets/src/mage/cards/d/DimensionalBreach.java index 4bf81b473209..f8e8000a1e11 100644 --- a/Mage.Sets/src/mage/cards/d/DimensionalBreach.java +++ b/Mage.Sets/src/mage/cards/d/DimensionalBreach.java @@ -1,31 +1,32 @@ - package mage.cards.d; -import java.util.UUID; -import mage.MageObject; +import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.DelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; +import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.filter.predicate.other.OwnerIdPredicate; -import mage.game.ExileZone; +import mage.filter.StaticFilters; import mage.game.Game; +import mage.game.events.GameEvent; import mage.players.Player; +import mage.target.TargetCard; import mage.target.common.TargetCardInExile; -import mage.util.CardUtil; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; /** - * - * @author jeffwadsworth + * @author TheElk801 */ public final class DimensionalBreach extends CardImpl { @@ -34,8 +35,6 @@ public DimensionalBreach(UUID ownerId, CardSetInfo setInfo) { // Exile all permanents. For as long as any of those cards remain exiled, at the beginning of each player's upkeep, that player returns one of the exiled cards they own to the battlefield. this.getSpellAbility().addEffect(new DimensionalBreachExileEffect()); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(new BeginningOfUpkeepTriggeredAbility(Zone.ALL, new DimensionalBreachReturnFromExileEffect(), TargetController.ANY, false, true, null), new CardsStillInExileCondition(), null)); - } private DimensionalBreach(final DimensionalBreach card) { @@ -50,12 +49,14 @@ public DimensionalBreach copy() { class DimensionalBreachExileEffect extends OneShotEffect { - public DimensionalBreachExileEffect() { + DimensionalBreachExileEffect() { super(Outcome.Exile); - staticText = "Exile all permanents."; + staticText = "Exile all permanents. For as long as any of those cards remain exiled, " + + "at the beginning of each player's upkeep, that player returns " + + "one of the exiled cards they own to the battlefield."; } - public DimensionalBreachExileEffect(final DimensionalBreachExileEffect effect) { + private DimensionalBreachExileEffect(final DimensionalBreachExileEffect effect) { super(effect); } @@ -66,75 +67,107 @@ public DimensionalBreachExileEffect copy() { @Override public boolean apply(Game game, Ability source) { - MageObject sourceObject = source.getSourceObject(game); - Player controller = game.getPlayer(source.getControllerId()); - if (sourceObject != null - && controller != null) { - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), 0); - if (exileId != null) { - game.getBattlefield().getAllActivePermanents().forEach((permanent) -> { - permanent.moveToExile(exileId, sourceObject.getName(), source, game); - }); - return true; - } + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; } - return false; + Cards cards = new CardsImpl(); + game.getBattlefield().getActivePermanents(source.getControllerId(), game).stream().forEach(cards::add); + player.moveCards(cards, Zone.EXILED, source, game); + Set morSet = cards + .getCards(game) + .stream() + .filter(c -> game.getState().getZone(c.getId()) == Zone.EXILED) + .map(c -> new MageObjectReference(c, game)) + .collect(Collectors.toSet()); + game.addDelayedTriggeredAbility(new DimensionalBreachDelayedTriggeredAbility(morSet), source); + return true; } } -class DimensionalBreachReturnFromExileEffect extends OneShotEffect { +class DimensionalBreachDelayedTriggeredAbility extends DelayedTriggeredAbility { - public DimensionalBreachReturnFromExileEffect() { - super(Outcome.PutCardInPlay); - staticText = "For as long as any of those cards remain exiled, at the beginning of each player's upkeep, that player returns one of the exiled cards they own to the battlefield."; + private final Set morSet = new HashSet<>(); + + DimensionalBreachDelayedTriggeredAbility(Set morSet) { + super(new DimensionalBreachReturnEffect(morSet), Duration.Custom, false, false); + this.morSet.addAll(morSet); } - public DimensionalBreachReturnFromExileEffect(final DimensionalBreachReturnFromExileEffect effect) { - super(effect); + private DimensionalBreachDelayedTriggeredAbility(final DimensionalBreachDelayedTriggeredAbility ability) { + super(ability); + this.morSet.addAll(ability.morSet); } @Override - public DimensionalBreachReturnFromExileEffect copy() { - return new DimensionalBreachReturnFromExileEffect(this); + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.UPKEEP_STEP_PRE; } @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(targetPointer.getFirst(game, source)); - if (player != null) { - FilterCard filter = new FilterCard("card you own from the Dimensional Breach exile"); - filter.add(new OwnerIdPredicate(player.getId())); + public boolean checkTrigger(GameEvent event, Game game) { + return morSet.stream().map(mor -> mor.getCard(game)).anyMatch(Objects::nonNull); + } - TargetCardInExile target = new TargetCardInExile(filter, CardUtil.getExileZoneId(game, source.getSourceId(), 0)); - target.setNotTarget(true); - - if (target.canChoose(source.getSourceId(), player.getId(), game)) { - if (player.chooseTarget(Outcome.PutCardInPlay, target, source, game)) { - Card card = game.getCard(target.getFirstTarget()); - if (card != null - && player.moveCards(card, Zone.BATTLEFIELD, source, game)) { - return true; - } - } - } - } - return false; + @Override + public DimensionalBreachDelayedTriggeredAbility copy() { + return new DimensionalBreachDelayedTriggeredAbility(this); + } + + @Override + public boolean isInactive(Game game) { + return morSet.stream().map(mor -> mor.getCard(game)).noneMatch(Objects::nonNull); + } + + @Override + public String getRule() { + return "For as long as any of those cards remain exiled, at the beginning of each player's upkeep, " + + "that player returns one of the exiled cards they own to the battlefield."; } } -class CardsStillInExileCondition implements Condition { +class DimensionalBreachReturnEffect extends OneShotEffect { + + private final Set morSet = new HashSet<>(); + + DimensionalBreachReturnEffect(Set morSet) { + super(Outcome.PutCardInPlay); + this.morSet.addAll(morSet); + } + + private DimensionalBreachReturnEffect(final DimensionalBreachReturnEffect effect) { + super(effect); + this.morSet.addAll(effect.morSet); + } + + @Override + public DimensionalBreachReturnEffect copy() { + return new DimensionalBreachReturnEffect(this); + } @Override - public final boolean apply(Game game, Ability source) { + public boolean apply(Game game, Ability source) { Player player = game.getPlayer(game.getActivePlayerId()); - if (player != null) { - FilterCard filter = new FilterCard(); - filter.add(new OwnerIdPredicate(player.getId())); - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), 0); - ExileZone exileZone = game.getExile().getExileZone(exileId); - return (exileZone != null - && !exileZone.getCards(filter, game).isEmpty()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl( + morSet.stream() + .map(mor -> mor.getCard(game)) + .filter(Objects::nonNull) + .filter(c -> c.isOwnedBy(game.getActivePlayerId())) + .collect(Collectors.toSet()) + ); + if (cards.isEmpty()) { + return false; + } + if (cards.size() > 1) { + TargetCard target = new TargetCardInExile(StaticFilters.FILTER_CARD); + target.setNotTarget(true); + player.choose(outcome, cards, target, game); + cards.clear(); + cards.add(target.getFirstTarget()); } - return false; + return player.moveCards(cards, Zone.BATTLEFIELD, source, game); } } diff --git a/Mage.Sets/src/mage/cards/d/DimensionalInfiltrator.java b/Mage.Sets/src/mage/cards/d/DimensionalInfiltrator.java index 8a4568c5f8c7..d20cc6f96e73 100644 --- a/Mage.Sets/src/mage/cards/d/DimensionalInfiltrator.java +++ b/Mage.Sets/src/mage/cards/d/DimensionalInfiltrator.java @@ -1,14 +1,10 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.abilities.keyword.DevoidAbility; import mage.abilities.keyword.FlashAbility; import mage.abilities.keyword.FlyingAbility; @@ -16,21 +12,23 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class DimensionalInfiltrator extends CardImpl { public DimensionalInfiltrator(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.ELDRAZI); this.power = new MageInt(2); this.toughness = new MageInt(1); @@ -45,7 +43,7 @@ public DimensionalInfiltrator(UUID ownerId, CardSetInfo setInfo) { this.addAbility(FlyingAbility.getInstance()); // {1}{C}: Exile the top card of target opponent's library. If it's a land card, you may return Dimensional Infiltrator to its owner's hand. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DimensionalInfiltratorEffect(), new ManaCostsImpl("{1}{C}")); + Ability ability = new SimpleActivatedAbility(new DimensionalInfiltratorEffect(), new ManaCostsImpl("{1}{C}")); ability.addTarget(new TargetOpponent()); this.addAbility(ability); } @@ -62,12 +60,13 @@ public DimensionalInfiltrator copy() { class DimensionalInfiltratorEffect extends OneShotEffect { - public DimensionalInfiltratorEffect() { - super(Outcome.PutCreatureInPlay); - this.staticText = "Exile the top card of target opponent's library. If it's a land card, you may return Dimensional Infiltrator to its owner's hand"; + DimensionalInfiltratorEffect() { + super(Outcome.ReturnToHand); + this.staticText = "exile the top card of target opponent's library. " + + "If it's a land card, you may return {this} to its owner's hand"; } - public DimensionalInfiltratorEffect(final DimensionalInfiltratorEffect effect) { + private DimensionalInfiltratorEffect(final DimensionalInfiltratorEffect effect) { super(effect); } @@ -78,24 +77,22 @@ public DimensionalInfiltratorEffect copy() { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); + Player player = game.getPlayer(source.getControllerId()); Player opponent = game.getPlayer(source.getFirstTarget()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (opponent == null || controller == null || sourceObject == null) { + if (player == null || opponent == null) { return false; } - - if (opponent.getLibrary().hasCards()) { - Card card = opponent.getLibrary().getFromTop(game); - if (card != null) { - card.moveToExile(null, "Dimensional Infiltrator", source, game); - if (card.isLand()) { - if (controller.chooseUse(Outcome.Neutral, "Return " + sourceObject.getIdName() + " to its owner's hand?", source, game)) { - new ReturnToHandSourceEffect(true).apply(game, source); - } - } - } + Card card = opponent.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + player.moveCards(card, Zone.EXILED, source, game); + if (!card.isLand()) { + return true; } - return true; + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + return permanent != null + && player.chooseUse(outcome, "Return " + permanent.getName() + " to its owner's hand?", source, game) + && player.moveCards(permanent, Zone.HAND, source, game); } } diff --git a/Mage.Sets/src/mage/cards/d/DinaSoulSteeper.java b/Mage.Sets/src/mage/cards/d/DinaSoulSteeper.java new file mode 100644 index 000000000000..b83d0c1b5712 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DinaSoulSteeper.java @@ -0,0 +1,60 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.GainLifeControllerTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.common.SacrificeCostCreaturesPower; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DinaSoulSteeper extends CardImpl { + + public DinaSoulSteeper(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.DRYAD); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Whenever you gain life, each opponent loses 1 life. + this.addAbility(new GainLifeControllerTriggeredAbility(new LoseLifeOpponentsEffect(1), false)); + + // {1}, Sacrifice another creature: Dina, Soul Steeper gets +X/+0 until end of turn, where X is the sacrificed creature's power. + Ability ability = new SimpleActivatedAbility(new BoostSourceEffect( + SacrificeCostCreaturesPower.instance, + StaticValue.get(0), Duration.EndOfTurn + ), new GenericManaCost(1)); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent( + StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE + ))); + this.addAbility(ability); + } + + private DinaSoulSteeper(final DinaSoulSteeper card) { + super(card); + } + + @Override + public DinaSoulSteeper copy() { + return new DinaSoulSteeper(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DinosaurHunter.java b/Mage.Sets/src/mage/cards/d/DinosaurHunter.java index 267cae43011f..1ba2c0078071 100644 --- a/Mage.Sets/src/mage/cards/d/DinosaurHunter.java +++ b/Mage.Sets/src/mage/cards/d/DinosaurHunter.java @@ -1,18 +1,20 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.common.*; +import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** * @author JayDi85 */ @@ -57,19 +59,20 @@ public DinosaurHunterAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getSourceId().equals(getSourceId())) { - Permanent targetPermanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); - if (targetPermanent.hasSubtype(SubType.DINOSAUR, game)) { - getEffects().setTargetPointer(new FixedTarget(targetPermanent, game)); - return true; - } + if (!event.getSourceId().equals(getSourceId())) { + return false; + } + Permanent targetPermanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); + if (targetPermanent == null || !targetPermanent.hasSubtype(SubType.DINOSAUR, game)) { + return false; } - return false; + getEffects().setTargetPointer(new FixedTarget(targetPermanent, game)); + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/d/DiplomaticEscort.java b/Mage.Sets/src/mage/cards/d/DiplomaticEscort.java index a929d5b18554..2b4a67ffa6e9 100644 --- a/Mage.Sets/src/mage/cards/d/DiplomaticEscort.java +++ b/Mage.Sets/src/mage/cards/d/DiplomaticEscort.java @@ -16,7 +16,7 @@ import mage.constants.Zone; import mage.filter.FilterStackObject; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.other.TargetsPermanentPredicate; +import mage.filter.predicate.mageobject.TargetsPermanentPredicate; import mage.target.TargetStackObject; /** diff --git a/Mage.Sets/src/mage/cards/d/DireFleetCaptain.java b/Mage.Sets/src/mage/cards/d/DireFleetCaptain.java index 4668a1b9fe48..32f5a34f74d8 100644 --- a/Mage.Sets/src/mage/cards/d/DireFleetCaptain.java +++ b/Mage.Sets/src/mage/cards/d/DireFleetCaptain.java @@ -12,7 +12,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.common.FilterAttackingCreature; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/d/DireFleetDaredevil.java b/Mage.Sets/src/mage/cards/d/DireFleetDaredevil.java index d67063b3412d..1fe3a23b916f 100644 --- a/Mage.Sets/src/mage/cards/d/DireFleetDaredevil.java +++ b/Mage.Sets/src/mage/cards/d/DireFleetDaredevil.java @@ -15,7 +15,6 @@ import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; import mage.players.Player; import mage.target.common.TargetCardInOpponentsGraveyard; @@ -94,7 +93,7 @@ public boolean apply(Game game, Ability source) { targetCard = game.getCard(targetCard.getId()); if (targetCard != null) { // you may play and spend any mana - CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, targetCard, Duration.EndOfTurn); + CardUtil.makeCardPlayable(game, source, targetCard, Duration.EndOfTurn, true); // exile from graveyard ContinuousEffect effect = new DireFleetDaredevilReplacementEffect(); effect.setTargetPointer(new FixedTarget(targetCard, game)); diff --git a/Mage.Sets/src/mage/cards/d/DisasterRadius.java b/Mage.Sets/src/mage/cards/d/DisasterRadius.java index 7173de1d6190..36922e1ac27b 100644 --- a/Mage.Sets/src/mage/cards/d/DisasterRadius.java +++ b/Mage.Sets/src/mage/cards/d/DisasterRadius.java @@ -54,7 +54,7 @@ class DisasterRadiusEffect extends OneShotEffect { public DisasterRadiusEffect() { super(Outcome.GainLife); - staticText = "{this} deals X damage to each creature your opponents control, where X is the revealed card's converted mana cost"; + staticText = "{this} deals X damage to each creature your opponents control, where X is the revealed card's mana value"; } public DisasterRadiusEffect(DisasterRadiusEffect effect) { @@ -65,7 +65,7 @@ public DisasterRadiusEffect(DisasterRadiusEffect effect) { public boolean apply(Game game, Ability source) { RevealTargetFromHandCost cost = (RevealTargetFromHandCost) source.getCosts().get(0); if (cost != null) { - int damage = cost.convertedManaCosts; + int damage = cost.manaValues; for (Permanent creature : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { if (creature != null) { creature.damage(damage, source.getSourceId(), source, game, false, false); diff --git a/Mage.Sets/src/mage/cards/d/DiscipleOfDeceit.java b/Mage.Sets/src/mage/cards/d/DiscipleOfDeceit.java index 91b50ce7845a..e93917805ea5 100644 --- a/Mage.Sets/src/mage/cards/d/DiscipleOfDeceit.java +++ b/Mage.Sets/src/mage/cards/d/DiscipleOfDeceit.java @@ -19,7 +19,7 @@ import mage.constants.Outcome; import mage.filter.FilterCard; import mage.filter.common.FilterNonlandCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInHand; @@ -58,7 +58,7 @@ class DiscipleOfDeceitEffect extends OneShotEffect { public DiscipleOfDeceitEffect() { super(Outcome.Benefit); - this.staticText = "you may discard a nonland card. If you do, search your library for a card with the same converted mana cost as that card, reveal it, put it into your hand, then shuffle your library"; + this.staticText = "you may discard a nonland card. If you do, search your library for a card with the same mana value as that card, reveal it, put it into your hand, then shuffle"; } public DiscipleOfDeceitEffect(final DiscipleOfDeceitEffect effect) { @@ -84,9 +84,9 @@ public boolean apply(Game game, Ability source) { if (card == null) { return false; } - String targetName = "card with converted mana cost of " + card.getConvertedManaCost(); + String targetName = "card with mana value of " + card.getManaValue(); FilterCard filter = new FilterCard(targetName); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, card.getConvertedManaCost())); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, card.getManaValue())); return new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true, true).apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/d/DiscoveryDispersal.java b/Mage.Sets/src/mage/cards/d/DiscoveryDispersal.java index 6daa999a7583..08cbf7a1024f 100644 --- a/Mage.Sets/src/mage/cards/d/DiscoveryDispersal.java +++ b/Mage.Sets/src/mage/cards/d/DiscoveryDispersal.java @@ -13,7 +13,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterNonlandPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.filter.predicate.permanent.PermanentIdPredicate; import mage.game.Game; @@ -68,7 +68,7 @@ class DispersalEffect extends OneShotEffect { DispersalEffect() { super(Outcome.Benefit); this.staticText = "Each opponent returns a nonland permanent " - + "they control with the highest converted mana cost " + + "they control with the highest mana value " + "among permanents they control to its owner's hand, " + "then discards a card."; } @@ -97,11 +97,11 @@ public boolean apply(Game game, Ability source) { int highestCMC = 0; for (Permanent permanent : game.getBattlefield().getAllActivePermanents(opponentId)) { if (permanent != null) { - highestCMC = Math.max(highestCMC, permanent.getConvertedManaCost()); + highestCMC = Math.max(highestCMC, permanent.getManaValue()); } } - FilterPermanent filter = new FilterNonlandPermanent("permanent you control with converted mana cost " + highestCMC); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, highestCMC)); + FilterPermanent filter = new FilterNonlandPermanent("permanent you control with mana value " + highestCMC); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, highestCMC)); filter.add(new ControllerIdPredicate(opponentId)); Target target = new TargetPermanent(1, 1, filter, true); if (opponent.choose(outcome, target, source.getSourceId(), game)) { diff --git a/Mage.Sets/src/mage/cards/d/DisdainfulStroke.java b/Mage.Sets/src/mage/cards/d/DisdainfulStroke.java index 5082249070ad..c89c82ed6c5a 100644 --- a/Mage.Sets/src/mage/cards/d/DisdainfulStroke.java +++ b/Mage.Sets/src/mage/cards/d/DisdainfulStroke.java @@ -8,7 +8,7 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetSpell; /** @@ -17,9 +17,9 @@ */ public final class DisdainfulStroke extends CardImpl { - private static final FilterSpell filter = new FilterSpell("spell with converted mana cost 4 or greater"); + private static final FilterSpell filter = new FilterSpell("spell with mana value 4 or greater"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 3)); + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 3)); } public DisdainfulStroke(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/d/Disembowel.java b/Mage.Sets/src/mage/cards/d/Disembowel.java index 8aaf662e7f2c..d813944a4fcf 100644 --- a/Mage.Sets/src/mage/cards/d/Disembowel.java +++ b/Mage.Sets/src/mage/cards/d/Disembowel.java @@ -8,7 +8,7 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.target.common.TargetCreaturePermanent; import mage.target.targetadjustment.TargetAdjuster; @@ -24,7 +24,7 @@ public Disembowel(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{B}"); // Destroy target creature with converted mana cost X. - this.getSpellAbility().addEffect(new DestroyTargetEffect("creature with converted mana cost X")); + this.getSpellAbility().addEffect(new DestroyTargetEffect("destroy target creature with mana value X")); this.getSpellAbility().setTargetAdjuster(DisembowelAdjuster.instance); } @@ -45,8 +45,8 @@ enum DisembowelAdjuster implements TargetAdjuster { public void adjustTargets(Ability ability, Game game) { ability.getTargets().clear(); int xValue = ability.getManaCostsToPay().getX(); - FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with converted mana cost X"); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, xValue)); + FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with mana value X"); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, xValue)); ability.addTarget(new TargetCreaturePermanent(filter)); } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/d/Dispatch.java b/Mage.Sets/src/mage/cards/d/Dispatch.java index a0e51effbe0b..e9e893cd89db 100644 --- a/Mage.Sets/src/mage/cards/d/Dispatch.java +++ b/Mage.Sets/src/mage/cards/d/Dispatch.java @@ -25,9 +25,11 @@ public Dispatch(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new TapTargetEffect()); // Metalcraft — If you control three or more artifacts, exile that creature. - this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new ExileTargetEffect(), MetalcraftCondition.instance, "Metalcraft — If you control three or more artifacts, exile that creature")); + this.getSpellAbility().addEffect(new ConditionalOneShotEffect( + new ExileTargetEffect(), MetalcraftCondition.instance, + "
Metalcraft — If you control three or more artifacts, exile that creature" + )); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - this.getSpellAbility().setAbilityWord(AbilityWord.METALCRAFT); this.getSpellAbility().addHint(MetalcraftHint.instance); } diff --git a/Mage.Sets/src/mage/cards/d/DispersalShield.java b/Mage.Sets/src/mage/cards/d/DispersalShield.java index 53c633ae1697..4c0372ceb127 100644 --- a/Mage.Sets/src/mage/cards/d/DispersalShield.java +++ b/Mage.Sets/src/mage/cards/d/DispersalShield.java @@ -4,7 +4,7 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.common.HighestConvertedManaCostValue; +import mage.abilities.dynamicvalue.common.HighestManaValueCount; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; @@ -43,7 +43,7 @@ class DispersalShieldEffect extends OneShotEffect { public DispersalShieldEffect() { super(Outcome.Detriment); - staticText = "Counter target spell if its converted mana cost is less than or equal to the highest converted mana cost among permanents you control"; + staticText = "Counter target spell if its mana value is less than or equal to the highest mana value among permanents you control"; } public DispersalShieldEffect(DispersalShieldEffect effect) { @@ -57,9 +57,9 @@ public Effect copy() { @Override public boolean apply(Game game, Ability source) { - DynamicValue amount = new HighestConvertedManaCostValue(); + DynamicValue amount = new HighestManaValueCount(); Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); - if (spell != null && spell.getConvertedManaCost() <= amount.calculate(game, source, this)) { + if (spell != null && spell.getManaValue() <= amount.calculate(game, source, this)) { return game.getStack().counter(source.getFirstTarget(), source, game); } return false; diff --git a/Mage.Sets/src/mage/cards/d/DisplacementWave.java b/Mage.Sets/src/mage/cards/d/DisplacementWave.java index 1640a57fbdf5..d05fc94cde78 100644 --- a/Mage.Sets/src/mage/cards/d/DisplacementWave.java +++ b/Mage.Sets/src/mage/cards/d/DisplacementWave.java @@ -1,26 +1,28 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; + +import java.util.UUID; /** - * * @author fireshoes */ public final class DisplacementWave extends CardImpl { public DisplacementWave(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{X}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{U}{U}"); // Return all nonland permanents with converted mana cost X or less to their owners' hands. this.getSpellAbility().addEffect(new DisplacementWaveEffect()); @@ -38,27 +40,32 @@ public DisplacementWave copy() { class DisplacementWaveEffect extends OneShotEffect { - public DisplacementWaveEffect() { + DisplacementWaveEffect() { super(Outcome.ReturnToHand); - staticText = "Return all nonland permanents with converted mana cost X or less to their owners' hands"; + staticText = "Return all nonland permanents with mana value X or less to their owners' hands"; } - public DisplacementWaveEffect(final DisplacementWaveEffect effect) { + private DisplacementWaveEffect(final DisplacementWaveEffect effect) { super(effect); } @Override public DisplacementWaveEffect copy() { return new DisplacementWaveEffect(this); - } - + } + @Override public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(); for (Permanent permanent : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) { - if (!permanent.isLand() && permanent.getConvertedManaCost() <= source.getManaCostsToPay().getX()) { - permanent.moveToZone(Zone.HAND, source, game, true); + if (!permanent.isLand() && permanent.getManaValue() <= source.getManaCostsToPay().getX()) { + cards.add(permanent); } } - return true; + return player.moveCards(cards, Zone.HAND, source, game); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/d/DisruptingShoal.java b/Mage.Sets/src/mage/cards/d/DisruptingShoal.java index 9b6281022924..b0c7d91c62d9 100644 --- a/Mage.Sets/src/mage/cards/d/DisruptingShoal.java +++ b/Mage.Sets/src/mage/cards/d/DisruptingShoal.java @@ -34,7 +34,7 @@ public DisruptingShoal(UUID ownerId, CardSetInfo setInfo) { this.subtype.add(SubType.ARCANE); // You may exile a blue card with converted mana cost X from your hand rather than pay Disrupting Shoal's mana cost. - FilterOwnedCard filter = new FilterOwnedCard("a blue card with converted mana cost X from your hand"); + FilterOwnedCard filter = new FilterOwnedCard("a blue card with mana value X from your hand"); filter.add(new ColorPredicate(ObjectColor.BLUE)); filter.add(Predicates.not(new CardIdPredicate(this.getId()))); // the exile cost can never be paid with the card itself this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(filter), true))); @@ -73,17 +73,17 @@ public DisruptingShoalCounterTargetEffect copy() { @Override public boolean apply(Game game, Ability source) { Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); - if (spell != null && isConvertedManaCostEqual(source, spell.getConvertedManaCost())) { + if (spell != null && isManaValueEqual(source, spell.getManaValue())) { return game.getStack().counter(source.getFirstTarget(), source, game); } return false; } - private boolean isConvertedManaCostEqual(Ability sourceAbility, int amount) { + private boolean isManaValueEqual(Ability sourceAbility, int amount) { for (Cost cost : sourceAbility.getCosts()) { if (cost.isPaid() && cost instanceof ExileFromHandCost) { for (Card card : ((ExileFromHandCost) cost).getCards()) { - return card.getConvertedManaCost() == amount; + return card.getManaValue() == amount; } return false; } @@ -94,7 +94,7 @@ private boolean isConvertedManaCostEqual(Ability sourceAbility, int amount) { @Override public String getText(Mode mode) { - return "Counter target spell if its converted mana cost is X"; + return "Counter target spell if its mana value is X"; } } diff --git a/Mage.Sets/src/mage/cards/d/DissensionInTheRanks.java b/Mage.Sets/src/mage/cards/d/DissensionInTheRanks.java index 32055a425333..aa495af369f3 100644 --- a/Mage.Sets/src/mage/cards/d/DissensionInTheRanks.java +++ b/Mage.Sets/src/mage/cards/d/DissensionInTheRanks.java @@ -7,7 +7,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.filter.predicate.permanent.BlockingPredicate; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/d/DistendedMindbender.java b/Mage.Sets/src/mage/cards/d/DistendedMindbender.java index a44e1353836e..3a3f64d7b50a 100644 --- a/Mage.Sets/src/mage/cards/d/DistendedMindbender.java +++ b/Mage.Sets/src/mage/cards/d/DistendedMindbender.java @@ -16,7 +16,7 @@ import mage.constants.SubType; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; @@ -59,19 +59,19 @@ public DistendedMindbender copy() { class DistendedMindbenderEffect extends OneShotEffect { - private static final FilterCard filterFourOrGreater = new FilterCard("a card from it with converted mana cost 4 or greater"); - private static final FilterCard filterThreeOrLess = new FilterCard("a nonland card from it with converted mana cost 3 or less"); + private static final FilterCard filterFourOrGreater = new FilterCard("a card from it with mana value 4 or greater"); + private static final FilterCard filterThreeOrLess = new FilterCard("a nonland card from it with mana value 3 or less"); static { - filterFourOrGreater.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 3)); - filterThreeOrLess.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filterFourOrGreater.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 3)); + filterThreeOrLess.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); filterThreeOrLess.add(Predicates.not(CardType.LAND.getPredicate())); } public DistendedMindbenderEffect() { super(Outcome.Discard); - this.staticText = "target opponent reveals their hand. You choose from it a nonland card with converted mana cost 3 or less and a card with " - + "converted mana cost 4 or greater. That player discards those cards."; + this.staticText = "target opponent reveals their hand. You choose from it a nonland card with mana value 3 or less and a card with " + + "mana value 4 or greater. That player discards those cards."; } public DistendedMindbenderEffect(final DistendedMindbenderEffect effect) { diff --git a/Mage.Sets/src/mage/cards/d/DivideByZero.java b/Mage.Sets/src/mage/cards/d/DivideByZero.java new file mode 100644 index 000000000000..4852b204f0e9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DivideByZero.java @@ -0,0 +1,50 @@ +package mage.cards.d; + +import mage.abilities.effects.common.LearnEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.filter.common.FilterSpellOrPermanent; +import mage.filter.predicate.Predicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.target.common.TargetSpellOrPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DivideByZero extends CardImpl { + + private static final FilterSpellOrPermanent filter + = new FilterSpellOrPermanent("spell or permanent with mana value 1 or greater"); + private static final Predicate predicate + = new ManaValuePredicate(ComparisonType.MORE_THAN, 0); + + static { + filter.getPermanentFilter().add(predicate); + filter.getSpellFilter().add(predicate); + } + + public DivideByZero(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}"); + + // Return target spell or permanent with mana value 1 or greater to its owner's hand. + this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); + this.getSpellAbility().addTarget(new TargetSpellOrPermanent(filter)); + + // Learn. + this.getSpellAbility().addEffect(new LearnEffect().concatBy("
")); + } + + private DivideByZero(final DivideByZero card) { + super(card); + } + + @Override + public DivideByZero copy() { + return new DivideByZero(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DivineOffering.java b/Mage.Sets/src/mage/cards/d/DivineOffering.java index 458ec7c33cde..bb48f8d734c7 100644 --- a/Mage.Sets/src/mage/cards/d/DivineOffering.java +++ b/Mage.Sets/src/mage/cards/d/DivineOffering.java @@ -2,7 +2,7 @@ package mage.cards.d; import java.util.UUID; -import mage.abilities.dynamicvalue.common.TargetConvertedManaCost; +import mage.abilities.dynamicvalue.common.TargetManaValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.GainLifeEffect; @@ -23,8 +23,8 @@ public DivineOffering(UUID ownerId, CardSetInfo setInfo) { // Destroy target artifact. You gain life equal to its converted mana cost. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - Effect effect = new GainLifeEffect(TargetConvertedManaCost.instance); - effect.setText("You gain life equal to its converted mana cost"); + Effect effect = new GainLifeEffect(TargetManaValue.instance); + effect.setText("You gain life equal to its mana value"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetArtifactPermanent()); } diff --git a/Mage.Sets/src/mage/cards/d/DivinePresence.java b/Mage.Sets/src/mage/cards/d/DivinePresence.java index e1d12cc0a974..cb137154449c 100644 --- a/Mage.Sets/src/mage/cards/d/DivinePresence.java +++ b/Mage.Sets/src/mage/cards/d/DivinePresence.java @@ -56,9 +56,8 @@ public DivinePresenceEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { switch (event.getType()) { - case DAMAGE_CREATURE: + case DAMAGE_PERMANENT: case DAMAGE_PLAYER: - case DAMAGE_PLANESWALKER: return true; default: return false; diff --git a/Mage.Sets/src/mage/cards/d/DjeruWithEyesOpen.java b/Mage.Sets/src/mage/cards/d/DjeruWithEyesOpen.java index e9dfe4c072c7..54acf82562b0 100644 --- a/Mage.Sets/src/mage/cards/d/DjeruWithEyesOpen.java +++ b/Mage.Sets/src/mage/cards/d/DjeruWithEyesOpen.java @@ -49,7 +49,7 @@ public DjeruWithEyesOpen(UUID ownerId, CardSetInfo setInfo) { // When Djeru, With Eyes Open enters the battlefield, you may search your library for a planeswalker card, reveal it, put it into your hand, then shuffle your library. Effect effect = new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 1, filter), true, true); - effect.setText("you may search your library for a planeswalker card, reveal it, put it into your hand, then shuffle your library"); + effect.setText("you may search your library for a planeswalker card, reveal it, put it into your hand, then shuffle"); this.addAbility(new EntersBattlefieldTriggeredAbility(effect, true)); // If a source would deal damage to a planeswalker you control, prevent 1 of that damage. @@ -84,9 +84,9 @@ public boolean apply(Game game, Ability source) { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER) { + if (event.getType() == GameEvent.EventType.DAMAGE_PERMANENT) { Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.isControlledBy(source.getControllerId())) { + if (permanent != null && permanent.isPlaneswalker() && permanent.isControlledBy(source.getControllerId())) { return super.applies(event, source, game); } } diff --git a/Mage.Sets/src/mage/cards/d/Dominate.java b/Mage.Sets/src/mage/cards/d/Dominate.java index e47c5d851547..1449ca8b771f 100644 --- a/Mage.Sets/src/mage/cards/d/Dominate.java +++ b/Mage.Sets/src/mage/cards/d/Dominate.java @@ -9,7 +9,7 @@ import mage.constants.Duration; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.target.common.TargetCreaturePermanent; import mage.target.targetadjustment.TargetAdjuster; @@ -26,7 +26,7 @@ public Dominate(UUID ownerId, CardSetInfo setInfo) { // Gain control of target creature with converted mana cost X or less. this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.Custom, true)); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature with converted mana cost X or less"))); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature with mana value X or less"))); this.getSpellAbility().setTargetAdjuster(DominateAdjuster.instance); } @@ -47,8 +47,8 @@ enum DominateAdjuster implements TargetAdjuster { public void adjustTargets(Ability ability, Game game) { ability.getTargets().clear(); int xValue = ability.getManaCostsToPay().getX(); - FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with converted mana cost X or less"); - filter.add(Predicates.not(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, xValue))); + FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with mana value X or less"); + filter.add(Predicates.not(new ManaValuePredicate(ComparisonType.MORE_THAN, xValue))); ability.addTarget(new TargetCreaturePermanent(filter)); } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/d/DominatorDrone.java b/Mage.Sets/src/mage/cards/d/DominatorDrone.java index e9a2ff4cae04..e7f5f7abbbcd 100644 --- a/Mage.Sets/src/mage/cards/d/DominatorDrone.java +++ b/Mage.Sets/src/mage/cards/d/DominatorDrone.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.ColorlessPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/d/DomriRade.java b/Mage.Sets/src/mage/cards/d/DomriRade.java index 7ad65620c153..7bc987cc020b 100644 --- a/Mage.Sets/src/mage/cards/d/DomriRade.java +++ b/Mage.Sets/src/mage/cards/d/DomriRade.java @@ -19,7 +19,7 @@ import mage.constants.SuperType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.command.emblems.DomriRadeEmblem; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/d/DoubleMajor.java b/Mage.Sets/src/mage/cards/d/DoubleMajor.java new file mode 100644 index 000000000000..000fc435845b --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DoubleMajor.java @@ -0,0 +1,85 @@ +package mage.cards.d; + +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.constants.SuperType; +import mage.filter.StaticFilters; +import mage.filter.predicate.mageobject.MageObjectReferencePredicate; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.game.stack.StackObject; +import mage.target.TargetSpell; +import mage.util.functions.StackObjectCopyApplier; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DoubleMajor extends CardImpl { + + public DoubleMajor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}{U}"); + + // Copy target creature spell you control, except it isn't legendary if the spell is legendary. + this.getSpellAbility().addEffect(new DoubleMajorEffect()); + this.getSpellAbility().addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_CREATURE)); + } + + private DoubleMajor(final DoubleMajor card) { + super(card); + } + + @Override + public DoubleMajor copy() { + return new DoubleMajor(this); + } +} + +class DoubleMajorEffect extends OneShotEffect { + + DoubleMajorEffect() { + super(Outcome.Copy); + staticText = "copy target creature spell you control, except it isn't legendary if the spell is legendary"; + } + + private DoubleMajorEffect(final DoubleMajorEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Spell spell = game.getSpell(source.getFirstTarget()); + if (spell == null) { + return false; + } + spell.createCopyOnStack( + game, source, source.getControllerId(), + false, 1, DoubleMajorApplier.instance + ); + return true; + } + + @Override + public DoubleMajorEffect copy() { + return new DoubleMajorEffect(this); + } +} + +enum DoubleMajorApplier implements StackObjectCopyApplier { + instance; + + @Override + public void modifySpell(StackObject stackObject, Game game) { + stackObject.getSuperType().remove(SuperType.LEGENDARY); + } + + @Override + public MageObjectReferencePredicate getNextPredicate() { + return null; + } +} diff --git a/Mage.Sets/src/mage/cards/d/DoubleVision.java b/Mage.Sets/src/mage/cards/d/DoubleVision.java index f71749174e4c..57a49802c5ac 100644 --- a/Mage.Sets/src/mage/cards/d/DoubleVision.java +++ b/Mage.Sets/src/mage/cards/d/DoubleVision.java @@ -1,5 +1,6 @@ package mage.cards.d; +import mage.MageObject; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.effects.common.CopyTargetSpellEffect; import mage.cards.CardImpl; @@ -71,7 +72,7 @@ private boolean isFirstInstantOrSorceryCastByPlayerOnTurn(Spell spell, Game game if (watcher != null) { List eligibleSpells = watcher.getSpellsCastThisTurn(this.getControllerId()) .stream() - .filter(s -> s.isInstant() || s.isSorcery()) + .filter(MageObject::isInstantOrSorcery) .collect(Collectors.toList()); return eligibleSpells.size() == 1 && eligibleSpells.get(0).getId().equals(spell.getId()); } diff --git a/Mage.Sets/src/mage/cards/d/Doublecast.java b/Mage.Sets/src/mage/cards/d/Doublecast.java index da91c5a274a9..78376a5a5b94 100644 --- a/Mage.Sets/src/mage/cards/d/Doublecast.java +++ b/Mage.Sets/src/mage/cards/d/Doublecast.java @@ -1,8 +1,6 @@ package mage.cards.d; -import java.util.UUID; import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.CopyTargetSpellEffect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.cards.CardImpl; @@ -14,8 +12,9 @@ import mage.game.stack.Spell; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class Doublecast extends CardImpl { @@ -43,11 +42,11 @@ public Doublecast copy() { class DoublecastAbility extends DelayedTriggeredAbility { - public DoublecastAbility() { + DoublecastAbility() { super(new CopyTargetSpellEffect(true), Duration.EndOfTurn); } - public DoublecastAbility(final DoublecastAbility ability) { + private DoublecastAbility(final DoublecastAbility ability) { super(ability); } @@ -63,16 +62,15 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getPlayerId().equals(this.getControllerId())) { - Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && (spell.isInstant() || spell.isSorcery())) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); - } - return true; - } + if (!isControlledBy(event.getPlayerId())) { + return false; + } + Spell spell = game.getStack().getSpell(event.getTargetId()); + if (spell == null || !spell.isInstantOrSorcery()) { + return false; } - return false; + this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId())); + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/d/DoublingChant.java b/Mage.Sets/src/mage/cards/d/DoublingChant.java index 90d0de75a313..1b12baf23327 100644 --- a/Mage.Sets/src/mage/cards/d/DoublingChant.java +++ b/Mage.Sets/src/mage/cards/d/DoublingChant.java @@ -48,7 +48,7 @@ public DoublingChantEffect() { super(Outcome.PutCreatureInPlay); this.staticText = "For each creature you control, " + "you may search your library for a creature card with the same name as that creature. " + - "Put those cards onto the battlefield, then shuffle your library"; + "Put those cards onto the battlefield, then shuffle"; } public DoublingChantEffect(final DoublingChantEffect effect) { diff --git a/Mage.Sets/src/mage/cards/d/DoublingSeason.java b/Mage.Sets/src/mage/cards/d/DoublingSeason.java index 3fafd52e6fa6..471950ef73e3 100644 --- a/Mage.Sets/src/mage/cards/d/DoublingSeason.java +++ b/Mage.Sets/src/mage/cards/d/DoublingSeason.java @@ -3,13 +3,11 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; -import mage.abilities.effects.common.replacement.CreateTwiceThatManyTokensEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -25,11 +23,10 @@ public DoublingSeason(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{G}"); // If an effect would create one or more tokens under your control, it creates twice that many of those tokens instead. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CreateTwiceThatManyTokensEffect())); + this.addAbility(new SimpleStaticAbility(new DoublingSeasonTokenEffect())); // If an effect would put one or more counters on a permanent you control, it puts twice that many of those counters on that permanent instead. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DoublingSeasonCounterEffect())); - + this.addAbility(new SimpleStaticAbility(new DoublingSeasonCounterEffect())); } private DoublingSeason(final DoublingSeason card) { @@ -42,16 +39,52 @@ public DoublingSeason copy() { } } +class DoublingSeasonTokenEffect extends ReplacementEffectImpl { + + DoublingSeasonTokenEffect() { + super(Duration.WhileOnBattlefield, Outcome.Copy); + staticText = "If an effect would create one or more tokens under your control, " + + "it creates twice that many of those tokens instead"; + } + + private DoublingSeasonTokenEffect(final DoublingSeasonTokenEffect effect) { + super(effect); + } + + @Override + public DoublingSeasonTokenEffect copy() { + return new DoublingSeasonTokenEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.CREATE_TOKEN; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + // TODO: this should only apply to effects + return event.getPlayerId().equals(source.getControllerId()); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + event.setAmount(event.getAmount() * 2); + return false; + } + +} + class DoublingSeasonCounterEffect extends ReplacementEffectImpl { - boolean landPlayed = false; // a played land is not an effect + private boolean landPlayed = false; // a played land is not an effect DoublingSeasonCounterEffect() { super(Duration.WhileOnBattlefield, Outcome.BoostCreature, false); staticText = "If an effect would put one or more counters on a permanent you control, it puts twice that many of those counters on that permanent instead"; } - DoublingSeasonCounterEffect(final DoublingSeasonCounterEffect effect) { + private DoublingSeasonCounterEffect(final DoublingSeasonCounterEffect effect) { super(effect); } diff --git a/Mage.Sets/src/mage/cards/d/Dovescape.java b/Mage.Sets/src/mage/cards/d/Dovescape.java index 86088d5d2829..31370e443747 100644 --- a/Mage.Sets/src/mage/cards/d/Dovescape.java +++ b/Mage.Sets/src/mage/cards/d/Dovescape.java @@ -50,7 +50,7 @@ class DovescapeEffect extends OneShotEffect { DovescapeEffect() { super(Outcome.Benefit); - this.staticText = "counter that spell. That player creates X 1/1 white and blue Bird creature tokens with flying, where X is the spell's converted mana cost"; + this.staticText = "counter that spell. That player creates X 1/1 white and blue Bird creature tokens with flying, where X is the spell's mana value"; } DovescapeEffect(final DovescapeEffect effect) { @@ -68,7 +68,7 @@ public boolean apply(Game game, Ability source) { int spellCMC = 0; UUID spellControllerID = null; if (spell != null) { - spellCMC = spell.getConvertedManaCost(); + spellCMC = spell.getManaValue(); spellControllerID = spell.getControllerId(); game.getStack().counter(spell.getId(), source, game); } diff --git a/Mage.Sets/src/mage/cards/d/DrJuliusJumblemorph.java b/Mage.Sets/src/mage/cards/d/DrJuliusJumblemorph.java index 91aeeab5fcd6..44fe88863f86 100644 --- a/Mage.Sets/src/mage/cards/d/DrJuliusJumblemorph.java +++ b/Mage.Sets/src/mage/cards/d/DrJuliusJumblemorph.java @@ -2,17 +2,15 @@ import mage.MageInt; import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; -import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.InfoEffect; -import mage.abilities.effects.common.continuous.IsAllCreatureTypesSourceEffect; +import mage.abilities.keyword.ChangelingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.ComparisonType; import mage.constants.SuperType; -import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import java.util.UUID; @@ -24,7 +22,7 @@ public final class DrJuliusJumblemorph extends CardImpl { private static final FilterPermanent filter = new FilterPermanent("a host"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, -1)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, -1)); } public DrJuliusJumblemorph(UUID ownerId, CardSetInfo setInfo) { @@ -34,13 +32,13 @@ public DrJuliusJumblemorph(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(4); // Dr. Julius Jumblemorph is every creature type (even if this card isn't on the battlefield). - this.addAbility(new SimpleStaticAbility(Zone.ALL, new IsAllCreatureTypesSourceEffect())); + this.addAbility(new ChangelingAbility(false)); // Whenever a host enters the battlefield under your control, you may search your library and/or graveyard for a card with augment and combine it with that host. If you search your library this way, shuffle it. // TODO: Host currently isn't implemented, so this ability currently would never trigger this.addAbility(new EntersBattlefieldControlledTriggeredAbility( new InfoEffect("you may search your library and/or graveyard for a card with augment " + - "and combine it with that host. If you search your library this way, shuffle it."), filter + "and combine it with that host. If you search your library this way, shuffle."), filter )); } diff --git a/Mage.Sets/src/mage/cards/d/Draco.java b/Mage.Sets/src/mage/cards/d/Draco.java index cd4939a5be00..cee7ac1fe50a 100644 --- a/Mage.Sets/src/mage/cards/d/Draco.java +++ b/Mage.Sets/src/mage/cards/d/Draco.java @@ -55,7 +55,7 @@ class DracoCostReductionEffect extends CostModificationEffectImpl { public DracoCostReductionEffect() { super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "Domain — {this} costs {2} less to cast for each basic land type among lands you control."; + staticText = "Domain — This spell costs {2} less to cast for each basic land type among lands you control."; } protected DracoCostReductionEffect(final DracoCostReductionEffect effect) { diff --git a/Mage.Sets/src/mage/cards/d/DraconicIntervention.java b/Mage.Sets/src/mage/cards/d/DraconicIntervention.java new file mode 100644 index 000000000000..b47ec3ba5bd5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DraconicIntervention.java @@ -0,0 +1,98 @@ +package mage.cards.d; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.costs.common.ExileFromGraveCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DamageAllEffect; +import mage.abilities.effects.common.ExileSpellEffect; +import mage.abilities.effects.common.replacement.DealtDamageToCreatureBySourceDies; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.target.common.TargetCardInYourGraveyard; +import mage.watchers.common.DamagedByWatcher; + +import java.util.Collection; +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DraconicIntervention extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("non-Dragon creature"); + + static { + filter.add(Predicates.not(SubType.DRAGON.getPredicate())); + } + + public DraconicIntervention(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}{R}"); + + // As an additional cost to cast this spell, exile an instant or sorcery card from your graveyard. + this.getSpellAbility().addCost(new ExileFromGraveCost( + new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY) + )); + + // Draconic Intervention deals X damage to each non-Dragon creature, where X is the exiled card's mana value. If a creature dealt damage this way would die this turn, exile it instead. + this.getSpellAbility().addEffect(new DamageAllEffect(DraconicInterventionValue.instance, filter)); + this.getSpellAbility().addEffect(new DealtDamageToCreatureBySourceDies(this, Duration.EndOfTurn)); + this.getSpellAbility().addWatcher(new DamagedByWatcher(false)); + + // Exile Draconic Intervention. + this.getSpellAbility().addEffect(new ExileSpellEffect().concatBy("
")); + } + + private DraconicIntervention(final DraconicIntervention card) { + super(card); + } + + @Override + public DraconicIntervention copy() { + return new DraconicIntervention(this); + } +} + +enum DraconicInterventionValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return sourceAbility + .getCosts() + .stream() + .filter(ExileFromGraveCost.class::isInstance) + .map(ExileFromGraveCost.class::cast) + .map(ExileFromGraveCost::getExiledCards) + .flatMap(Collection::stream) + .filter(Objects::nonNull) + .map(MageObject::getManaValue) + .findFirst() + .orElse(0); + } + + @Override + public DraconicInterventionValue copy() { + return instance; + } + + @Override + public String toString() { + return "X"; + } + + @Override + public String getMessage() { + return "the exiled card's mana value"; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/d/DraconicRoar.java b/Mage.Sets/src/mage/cards/d/DraconicRoar.java index 2868e3152556..6bcb8922161f 100644 --- a/Mage.Sets/src/mage/cards/d/DraconicRoar.java +++ b/Mage.Sets/src/mage/cards/d/DraconicRoar.java @@ -1,23 +1,17 @@ package mage.cards.d; import mage.abilities.Ability; -import mage.abilities.costs.CostAdjuster; -import mage.abilities.costs.common.RevealTargetFromHandCost; +import mage.abilities.condition.common.RevealedOrControlledDragonCondition; +import mage.abilities.costs.common.RevealDragonFromHandCost; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.DamageTargetEffect; -import mage.abilities.effects.common.InfoEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.SubType; -import mage.filter.FilterCard; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.common.TargetCardInHand; import mage.target.common.TargetCreaturePermanent; -import mage.watchers.common.DragonOnTheBattlefieldWhileSpellWasCastWatcher; import java.util.UUID; @@ -30,14 +24,11 @@ public DraconicRoar(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); // As an additional cost to cast Draconic Roar, you may reveal a Dragon card from your hand. - this.getSpellAbility().addEffect(new InfoEffect("as an additional cost to cast this spell, you may reveal a Dragon card from your hand")); - this.getSpellAbility().setCostAdjuster(DraconicRoarAdjuster.instance); + this.getSpellAbility().addCost(new RevealDragonFromHandCost()); // Draconic Roar deals 3 damage to target creature. If you revealed a Dragon card or controlled a Dragon as you cast Draconic Roar, Draconic Roar deals 3 damage to that creature's controller. - this.getSpellAbility().addEffect(new DamageTargetEffect(3)); - this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addEffect(new DraconicRoarEffect()); - this.getSpellAbility().addWatcher(new DragonOnTheBattlefieldWhileSpellWasCastWatcher()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } private DraconicRoar(final DraconicRoar card) { @@ -50,34 +41,15 @@ public DraconicRoar copy() { } } -enum DraconicRoarAdjuster implements CostAdjuster { - instance; - - private static final FilterCard filter = new FilterCard("a Dragon card from your hand (you don't have to)"); - - static { - filter.add(SubType.DRAGON.getPredicate()); - } - - @Override - public void adjustCosts(Ability ability, Game game) { - Player controller = game.getPlayer(ability.getControllerId()); - if (controller != null) { - if (controller.getHand().count(filter, game) > 0) { - ability.addCost(new RevealTargetFromHandCost(new TargetCardInHand(0, 1, filter))); - } - } - } -} - class DraconicRoarEffect extends OneShotEffect { - public DraconicRoarEffect() { + DraconicRoarEffect() { super(Outcome.Benefit); - this.staticText = "If you revealed a Dragon card or controlled a Dragon as you cast {this}, {this} deals 3 damage to that creature's controller"; + staticText = "{this} deals 3 damage to target creature. If you revealed a Dragon card or controlled " + + "a Dragon as you cast this spell, {this} deals 3 damage to that creature's controller."; } - public DraconicRoarEffect(final DraconicRoarEffect effect) { + private DraconicRoarEffect(final DraconicRoarEffect effect) { super(effect); } @@ -88,21 +60,18 @@ public DraconicRoarEffect copy() { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - DragonOnTheBattlefieldWhileSpellWasCastWatcher watcher = game.getState().getWatcher(DragonOnTheBattlefieldWhileSpellWasCastWatcher.class); - if (watcher != null && watcher.castWithConditionTrue(source.getId())) { - Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); - if (permanent != null) { - Player player = game.getPlayer(permanent.getControllerId()); - if (player != null) { - player.damage(3, source.getSourceId(), source, game); - } - } - } + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + permanent.damage(3, source.getSourceId(), source, game); + if (!RevealedOrControlledDragonCondition.instance.apply(game, source)) { return true; } - return false; + Player player = game.getPlayer(permanent.getControllerId()); + if (player != null) { + player.damage(3, source.getSourceId(), source, game); + } + return true; } } - diff --git a/Mage.Sets/src/mage/cards/d/Dracoplasm.java b/Mage.Sets/src/mage/cards/d/Dracoplasm.java index 0b96b81a2e61..80b1a77d3c73 100644 --- a/Mage.Sets/src/mage/cards/d/Dracoplasm.java +++ b/Mage.Sets/src/mage/cards/d/Dracoplasm.java @@ -16,7 +16,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; diff --git a/Mage.Sets/src/mage/cards/d/DragonBreath.java b/Mage.Sets/src/mage/cards/d/DragonBreath.java index ecebae659249..e068bc911b96 100644 --- a/Mage.Sets/src/mage/cards/d/DragonBreath.java +++ b/Mage.Sets/src/mage/cards/d/DragonBreath.java @@ -18,7 +18,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -31,9 +31,9 @@ */ public final class DragonBreath extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature with converted mana cost 6 or greater"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature with mana value 6 or greater"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 5)); + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 5)); } public DragonBreath(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/d/DragonFangs.java b/Mage.Sets/src/mage/cards/d/DragonFangs.java index cf21cea961b5..79f652d8ca3b 100644 --- a/Mage.Sets/src/mage/cards/d/DragonFangs.java +++ b/Mage.Sets/src/mage/cards/d/DragonFangs.java @@ -16,7 +16,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -29,9 +29,9 @@ */ public final class DragonFangs extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature with converted mana cost 6 or greater"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature with mana value 6 or greater"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 5)); + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 5)); } public DragonFangs(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/d/DragonScales.java b/Mage.Sets/src/mage/cards/d/DragonScales.java index 69623860976f..0128adc3df29 100644 --- a/Mage.Sets/src/mage/cards/d/DragonScales.java +++ b/Mage.Sets/src/mage/cards/d/DragonScales.java @@ -16,7 +16,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -29,9 +29,9 @@ */ public final class DragonScales extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature with converted mana cost 6 or greater"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature with mana value 6 or greater"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 5)); + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 5)); } public DragonScales(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/d/DragonShadow.java b/Mage.Sets/src/mage/cards/d/DragonShadow.java index 4ed5cd4b93ed..21ea31de54e8 100644 --- a/Mage.Sets/src/mage/cards/d/DragonShadow.java +++ b/Mage.Sets/src/mage/cards/d/DragonShadow.java @@ -16,7 +16,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -29,9 +29,9 @@ */ public final class DragonShadow extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature with converted mana cost 6 or greater"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature with mana value 6 or greater"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 5)); + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 5)); } public DragonShadow(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/d/DragonWings.java b/Mage.Sets/src/mage/cards/d/DragonWings.java index 6a7b9a7da701..a5d4cd607138 100644 --- a/Mage.Sets/src/mage/cards/d/DragonWings.java +++ b/Mage.Sets/src/mage/cards/d/DragonWings.java @@ -17,7 +17,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -30,9 +30,9 @@ */ public final class DragonWings extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature with converted mana cost 6 or greater"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature with mana value 6 or greater"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 5)); + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 5)); } public DragonWings(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/d/DragonlordsPrerogative.java b/Mage.Sets/src/mage/cards/d/DragonlordsPrerogative.java index 7f5685292e8f..405555be6170 100644 --- a/Mage.Sets/src/mage/cards/d/DragonlordsPrerogative.java +++ b/Mage.Sets/src/mage/cards/d/DragonlordsPrerogative.java @@ -1,28 +1,15 @@ - package mage.cards.d; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.Condition; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostAdjuster; -import mage.abilities.costs.common.RevealTargetFromHandCost; +import mage.abilities.condition.common.RevealedOrControlledDragonCondition; +import mage.abilities.costs.common.RevealDragonFromHandCost; import mage.abilities.decorator.ConditionalContinuousRuleModifyingEffect; -import mage.abilities.effects.ContinuousRuleModifyingEffect; import mage.abilities.effects.common.CantBeCounteredSourceEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.abilities.effects.common.InfoEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.filter.common.FilterControlledPermanent; -import mage.game.Game; -import mage.game.stack.Spell; -import mage.players.Player; -import mage.target.common.TargetCardInHand; import java.util.UUID; @@ -35,20 +22,18 @@ public DragonlordsPrerogative(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{U}{U}"); // As an additional cost to cast Dragonlord's Prerogative, you may reveal a Dragon card from your hand. - this.getSpellAbility().addEffect(new InfoEffect("as an additional cost to cast this spell, you may reveal a Dragon card from your hand")); - this.getSpellAbility().setCostAdjuster(DragonlordsPrerogativeAdjuster.instance); + this.getSpellAbility().addCost(new RevealDragonFromHandCost()); // If you revealed a Dragon card or controlled a Dragon as you cast Dragonlord's Prerogative, Dragonlord's Prerogative can't be countered. - Condition condition = new DragonlordsPrerogativeCondition(); - ContinuousRuleModifyingEffect cantBeCountered = new CantBeCounteredSourceEffect(); - ConditionalContinuousRuleModifyingEffect conditionalCantBeCountered = new ConditionalContinuousRuleModifyingEffect(cantBeCountered, condition); - conditionalCantBeCountered.setText("
If you revealed a Dragon card or controlled a Dragon as you cast {this}, this spell can't be countered"); - Ability ability = new SimpleStaticAbility(Zone.STACK, conditionalCantBeCountered); - this.addAbility(ability); + this.addAbility(new SimpleStaticAbility( + Zone.STACK, + new ConditionalContinuousRuleModifyingEffect( + new CantBeCounteredSourceEffect(), RevealedOrControlledDragonCondition.instance + ).setText("if you revealed a Dragon card or controlled a Dragon as you cast this spell, this spell can't be countered") + )); // Draw four cards. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(4)); - } private DragonlordsPrerogative(final DragonlordsPrerogative card) { @@ -60,49 +45,3 @@ public DragonlordsPrerogative copy() { return new DragonlordsPrerogative(this); } } - -enum DragonlordsPrerogativeAdjuster implements CostAdjuster { - instance; - private static final FilterCard filter = new FilterCard("a Dragon card from your hand"); - - static { - filter.add(SubType.DRAGON.getPredicate()); - } - - @Override - public void adjustCosts(Ability ability, Game game) { - Player controller = game.getPlayer(ability.getControllerId()); - if (controller != null) { - if (controller.getHand().count(filter, game) > 0) { - ability.addCost(new RevealTargetFromHandCost(new TargetCardInHand(0, 1, filter))); - } - } - } -} - -class DragonlordsPrerogativeCondition implements Condition { - - private static final FilterControlledPermanent filter = new FilterControlledPermanent("Dragon"); - - static { - filter.add(SubType.DRAGON.getPredicate()); - } - - @Override - public boolean apply(Game game, Ability source) { - boolean applies = false; - Spell spell = game.getStack().getSpell(source.getSourceId()); - if (spell != null && spell.getSpellAbility() != null) { - for (Cost cost : spell.getSpellAbility().getCosts()) { - if (cost instanceof RevealTargetFromHandCost) { - applies = !cost.getTargets().isEmpty(); - break; - } - } - } - if (!applies) { - applies = game.getBattlefield().countAll(filter, source.getControllerId(), game) > 0; - } - return applies; - } -} diff --git a/Mage.Sets/src/mage/cards/d/DragonsApproach.java b/Mage.Sets/src/mage/cards/d/DragonsApproach.java new file mode 100644 index 000000000000..1ce8ad9f1567 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DragonsApproach.java @@ -0,0 +1,63 @@ +package mage.cards.d; + +import mage.abilities.costs.CompositeCost; +import mage.abilities.costs.common.ExileFromGraveCost; +import mage.abilities.costs.common.ExileSourceCost; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DragonsApproach extends CardImpl { + + private static final FilterCard filter = new FilterCreatureCard("Dragon creature card"); + private static final FilterCard filter2 = new FilterCard("cards named Dragon's Approach"); + + static { + filter.add(SubType.DRAGON.getPredicate()); + filter2.add(new NamePredicate("Dragon's Approach")); + } + + public DragonsApproach(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}"); + + // Dragon's Approach deals 3 damage to each opponent. You may exile Dragon's Approach and four cards named Dragon's Approach from your graveyard. If you do, search your library for a Dragon creature card, put it onto the battlefield, then shuffle. + this.getSpellAbility().addEffect(new DamagePlayersEffect(3, TargetController.OPPONENT)); + this.getSpellAbility().addEffect(new DoIfCostPaid( + new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter)), + new CompositeCost( + new ExileSourceCost(), new ExileFromGraveCost(new TargetCardInYourGraveyard(filter2)), + "exile {this} and four cards named Dragon's Approach from your graveyard" + ) + )); + + // A deck can have any number of cards named Dragon's Approach. + this.getSpellAbility().addEffect(new InfoEffect( + "A deck can have any number of cards named Dragon's Approach." + ).concatBy("
")); + } + + private DragonsApproach(final DragonsApproach card) { + super(card); + } + + @Override + public DragonsApproach copy() { + return new DragonsApproach(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DragonsHerald.java b/Mage.Sets/src/mage/cards/d/DragonsHerald.java index c1e8e799afcf..d93f811c5caf 100644 --- a/Mage.Sets/src/mage/cards/d/DragonsHerald.java +++ b/Mage.Sets/src/mage/cards/d/DragonsHerald.java @@ -1,9 +1,6 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeTargetCost; @@ -14,34 +11,26 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.mageobject.ColorPredicate; import mage.filter.predicate.mageobject.NamePredicate; import mage.target.common.TargetCardInLibrary; -import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetControlledCreatureEachColor; + +import java.util.UUID; /** - * * @author North */ public final class DragonsHerald extends CardImpl { private static final FilterCard filter = new FilterCard("card named Hellkite Overlord"); - private static final FilterControlledCreaturePermanent filterBlack = new FilterControlledCreaturePermanent("a black creature"); - private static final FilterControlledCreaturePermanent filterRed = new FilterControlledCreaturePermanent("a red creature"); - private static final FilterControlledCreaturePermanent filterGreen = new FilterControlledCreaturePermanent("a green creature"); static { filter.add(new NamePredicate("Hellkite Overlord")); - filterBlack.add(new ColorPredicate(ObjectColor.BLACK)); - filterRed.add(new ColorPredicate(ObjectColor.RED)); - filterGreen.add(new ColorPredicate(ObjectColor.GREEN)); } public DragonsHerald(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}"); this.subtype.add(SubType.GOBLIN); this.subtype.add(SubType.SHAMAN); @@ -50,14 +39,11 @@ public DragonsHerald(UUID ownerId, CardSetInfo setInfo) { // {2}{R}, {tap}, Sacrifice a black creature, a red creature, and a green creature: // Search your library for a card named Hellkite Overlord and put it onto the battlefield. Then shuffle your library. - TargetCardInLibrary target = new TargetCardInLibrary(1, 1, new FilterCard(filter)); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new SearchLibraryPutInPlayEffect(target), - new ManaCostsImpl("{2}{R}")); + Ability ability = new SimpleActivatedAbility(new SearchLibraryPutInPlayEffect( + new TargetCardInLibrary(filter)), new ManaCostsImpl<>("{2}{R}") + ); ability.addCost(new TapSourceCost()); - ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filterBlack, false))); - ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filterRed, false))); - ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filterGreen, false))); + ability.addCost(new SacrificeTargetCost(new TargetControlledCreatureEachColor("BRG"))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/d/DragonsguardElite.java b/Mage.Sets/src/mage/cards/d/DragonsguardElite.java new file mode 100644 index 000000000000..4d830d18d09b --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DragonsguardElite.java @@ -0,0 +1,45 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DoubleCountersSourceEffect; +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 java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DragonsguardElite extends CardImpl { + + public DragonsguardElite(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, put a +1/+1 counter on Dragonsguard Elite. + this.addAbility(new MagecraftAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()))); + + // {4}{G}{G}: Double the number of +1/+1 counters on Dragonsguard Elite. + this.addAbility(new SimpleActivatedAbility(new DoubleCountersSourceEffect(CounterType.P1P1), new ManaCostsImpl<>("{4}{G}{G}"))); + } + + private DragonsguardElite(final DragonsguardElite card) { + super(card); + } + + @Override + public DragonsguardElite copy() { + return new DragonsguardElite(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DrainingWhelk.java b/Mage.Sets/src/mage/cards/d/DrainingWhelk.java index cf659a978a12..068124c6772a 100644 --- a/Mage.Sets/src/mage/cards/d/DrainingWhelk.java +++ b/Mage.Sets/src/mage/cards/d/DrainingWhelk.java @@ -57,7 +57,7 @@ class DrainingWhelkEffect extends CounterTargetEffect { DrainingWhelkEffect() { super(); - staticText = "counter target spell. Put X +1/+1 counters on Draining Whelk, where X is that spell's converted mana cost"; + staticText = "counter target spell. Put X +1/+1 counters on Draining Whelk, where X is that spell's mana value"; } DrainingWhelkEffect(final DrainingWhelkEffect effect) { @@ -73,7 +73,7 @@ public DrainingWhelkEffect copy() { public boolean apply(Game game, Ability source) { Spell targetSpell = game.getStack().getSpell(getTargetPointer().getFirst(game, source)); if (targetSpell != null) { - int spellCMC = targetSpell.getConvertedManaCost(); + int spellCMC = targetSpell.getManaValue(); super.apply(game, source); new AddCountersSourceEffect(CounterType.P1P1.createInstance(spellCMC)).apply(game, source); return true; diff --git a/Mage.Sets/src/mage/cards/d/DrakeFamiliar.java b/Mage.Sets/src/mage/cards/d/DrakeFamiliar.java index 2ed208915aa7..9f9c46743e6d 100644 --- a/Mage.Sets/src/mage/cards/d/DrakeFamiliar.java +++ b/Mage.Sets/src/mage/cards/d/DrakeFamiliar.java @@ -1,17 +1,15 @@ - package mage.cards.d; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; @@ -22,20 +20,19 @@ import java.util.UUID; /** - * * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ public final class DrakeFamiliar extends CardImpl { public DrakeFamiliar(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.DRAKE); this.power = new MageInt(2); this.toughness = new MageInt(1); // Flying this.addAbility(FlyingAbility.getInstance()); - + // When Drake Familiar enters the battlefield, sacrifice it unless you return an enchantment to its owner's hand. this.addAbility(new EntersBattlefieldTriggeredAbility(new DrakeFamiliarEffect())); } @@ -52,38 +49,33 @@ public DrakeFamiliar copy() { class DrakeFamiliarEffect extends OneShotEffect { - private static final String effectText = "sacrifice it unless you return an enchantment to its owner's hand."; - - DrakeFamiliarEffect () { + DrakeFamiliarEffect() { super(Outcome.Sacrifice); - staticText = effectText; + staticText = "sacrifice it unless you return an enchantment to its owner's hand."; } - DrakeFamiliarEffect (DrakeFamiliarEffect effect) { + private DrakeFamiliarEffect(DrakeFamiliarEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - boolean targetChosen = false; - TargetPermanent target = new TargetPermanent(1, 1, StaticFilters.FILTER_ENCHANTMENT_PERMANENT, true); - if (target.canChoose(source.getSourceId(), controller.getId(), game) && controller.chooseUse(outcome, "Return an enchantment to its owner's hand?", source, game)) { - controller.chooseTarget(Outcome.Sacrifice, target, source, game); - Permanent permanent = game.getPermanent(target.getFirstTarget()); - if (permanent != null) { - targetChosen = true; - permanent.moveToZone(Zone.HAND, source, game, false); - } - } - - if (!targetChosen) { - new SacrificeSourceEffect().apply(game, source); + if (controller == null) { + return false; + } + TargetPermanent target = new TargetPermanent(StaticFilters.FILTER_ENCHANTMENT_PERMANENT); + target.setNotTarget(true); + if (target.canChoose(source.getSourceId(), controller.getId(), game) + && controller.chooseUse(outcome, "Return an enchantment to its owner's hand?", source, game)) { + controller.chooseTarget(Outcome.ReturnToHand, target, source, game); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent != null) { + return controller.moveCards(permanent, Zone.HAND, source, game); } - return true; } - return false; + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + return permanent != null && permanent.sacrifice(source, game); } @Override diff --git a/Mage.Sets/src/mage/cards/d/DrakusethMawOfFlames.java b/Mage.Sets/src/mage/cards/d/DrakusethMawOfFlames.java index 486356376928..d0ad46a38316 100644 --- a/Mage.Sets/src/mage/cards/d/DrakusethMawOfFlames.java +++ b/Mage.Sets/src/mage/cards/d/DrakusethMawOfFlames.java @@ -12,7 +12,7 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.common.FilterCreaturePlayerOrPlaneswalker; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/d/DralnuLichLord.java b/Mage.Sets/src/mage/cards/d/DralnuLichLord.java index ec6e4a97aad4..c32b2780ab17 100644 --- a/Mage.Sets/src/mage/cards/d/DralnuLichLord.java +++ b/Mage.Sets/src/mage/cards/d/DralnuLichLord.java @@ -19,7 +19,7 @@ import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; import mage.game.Game; -import mage.game.events.DamageCreatureEvent; +import mage.game.events.DamageEvent; import mage.game.events.GameEvent; import mage.target.common.TargetCardInYourGraveyard; @@ -76,14 +76,14 @@ class DralnuLichLordReplacementEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - DamageCreatureEvent damageEvent = (DamageCreatureEvent) event; + DamageEvent damageEvent = (DamageEvent) event; new SacrificeControllerEffect(new FilterPermanent(), damageEvent.getAmount(), "").apply(game, source); return true; } @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/d/DralnusPet.java b/Mage.Sets/src/mage/cards/d/DralnusPet.java index 1d4f430c0e4b..6c4ccd33688e 100644 --- a/Mage.Sets/src/mage/cards/d/DralnusPet.java +++ b/Mage.Sets/src/mage/cards/d/DralnusPet.java @@ -50,7 +50,7 @@ public DralnusPet(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new KickerAbility(kickerCosts)); // If Dralnu's Pet was kicked, it enters the battlefield with flying and with X +1/+1 counters on it, where X is the discarded card's converted mana cost. Ability ability = new EntersBattlefieldAbility(new DralnusPetEffect(), KickedCondition.instance, - "If {this} was kicked, it enters the battlefield with flying and with X +1/+1 counters on it, where X is the discarded card's converted mana cost.", ""); + "If {this} was kicked, it enters the battlefield with flying and with X +1/+1 counters on it, where X is the discarded card's mana value.", ""); ability.addEffect(new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield)); this.addAbility(ability); } @@ -69,7 +69,7 @@ class DralnusPetEffect extends OneShotEffect { public DralnusPetEffect() { super(Outcome.BoostCreature); - this.staticText = "and with X +1/+1 counters on it, where X is the discarded card's converted mana cost"; + this.staticText = "and with X +1/+1 counters on it, where X is the discarded card's mana value"; } public DralnusPetEffect(final DralnusPetEffect effect) { @@ -96,7 +96,7 @@ public boolean apply(Game game, Ability source) { int cmc = 0; for (Cost cost : spellAbility.getCosts()) { if (cost instanceof DiscardCardCost && !((DiscardCardCost) cost).getCards().isEmpty()) { - cmc = ((DiscardCardCost) cost).getCards().get(0).getConvertedManaCost(); + cmc = ((DiscardCardCost) cost).getCards().get(0).getManaValue(); } if (cmc > 0) { return new AddCountersSourceEffect(CounterType.P1P1.createInstance(cmc), true).apply(game, source); diff --git a/Mage.Sets/src/mage/cards/d/DramaticFinale.java b/Mage.Sets/src/mage/cards/d/DramaticFinale.java new file mode 100644 index 000000000000..4f962f0b3124 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DramaticFinale.java @@ -0,0 +1,54 @@ +package mage.cards.d; + +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +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.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.permanent.token.SilverquillToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DramaticFinale extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("one or more nontoken creatures you control"); + + static { + filter.add(Predicates.not(TokenPredicate.instance)); + } + + public DramaticFinale(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W/B}{W/B}{W/B}{W/B}"); + + // Creature tokens you control get +1/+1. + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 1, 1, Duration.WhileOnBattlefield, StaticFilters.FILTER_CREATURE_TOKENS + ))); + + // Whenever one or more nontoken creatures you control die, create a 2/1 white and black Inkling creature token with flying. This ability triggers only once each turn. + this.addAbility(new DiesCreatureTriggeredAbility( + new CreateTokenEffect(new SilverquillToken()), false, filter + ).setTriggersOnce(true)); + } + + private DramaticFinale(final DramaticFinale card) { + super(card); + } + + @Override + public DramaticFinale copy() { + return new DramaticFinale(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DraugrNecromancer.java b/Mage.Sets/src/mage/cards/d/DraugrNecromancer.java index 7e17180d3cce..776ee712a22e 100644 --- a/Mage.Sets/src/mage/cards/d/DraugrNecromancer.java +++ b/Mage.Sets/src/mage/cards/d/DraugrNecromancer.java @@ -6,11 +6,10 @@ import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.AsThoughManaEffect; import mage.abilities.effects.ReplacementEffectImpl; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; +import mage.cards.*; import mage.constants.*; import mage.counters.CounterType; +import mage.game.CardState; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; @@ -82,7 +81,7 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { } Card card = game.getCard(permanent.getId()); controller.moveCards(permanent, Zone.EXILED, source, game); - card.addCounters(CounterType.ICE.createInstance(), source.getControllerId(), source, game); + card.getMainCard().addCounters(CounterType.ICE.createInstance(), source.getControllerId(), source, game); return true; } @@ -123,15 +122,17 @@ public DraugrNecromancerCastFromExileEffect copy() { @Override public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + Card card = game.getCard(sourceId); + if (card == null) { + return false; + } if (!source.isControlledBy(affectedControllerId) - || game.getState().getZone(sourceId) != Zone.EXILED) { + || game.getState().getZone(card.getMainCard().getId()) != Zone.EXILED) { return false; } - Card card = game.getCard(sourceId); - return card != null - && !card.isLand() + return !card.isLand() && game.getOpponents(card.getOwnerId()).contains(source.getControllerId()) - && card.getCounters(game).getCount(CounterType.ICE) > 0; + && card.getMainCard().getCounters(game).getCount(CounterType.ICE) > 0; } } @@ -159,14 +160,35 @@ public DraugrNecromancerSpendAnyManaEffect copy() { @Override public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { if (!source.isControlledBy(affectedControllerId) - || game.getState().getZone(sourceId) != Zone.EXILED) { + || !game.getOpponents(game.getOwnerId(sourceId)).contains(source.getControllerId())) { return false; } + Card card = game.getCard(sourceId); - return card != null - && !card.isLand() - && game.getOpponents(card.getOwnerId()).contains(source.getControllerId()) - && card.getCounters(game).getCount(CounterType.ICE) > 0; + if (card == null) { + return false; + } + card = card.getMainCard(); + + // card can be in exile or stack zones + if (game.getState().getZone(card.getId()) == Zone.EXILED) { + // exile zone + return card.getCounters(game).getCount(CounterType.ICE) > 0; + } else { + // stack zone + // you must look at exile zone (use LKI to see ice counters from the past) + CardState cardState; + if (card instanceof SplitCard) { + cardState = game.getLastKnownInformationCard(card.getId(), Zone.EXILED); + } else if (card instanceof AdventureCard) { + cardState = game.getLastKnownInformationCard(card.getId(), Zone.EXILED); + } else if (card instanceof ModalDoubleFacesCard) { + cardState = game.getLastKnownInformationCard(((ModalDoubleFacesCard) card).getLeftHalfCard().getId(), Zone.EXILED); + } else { + cardState = game.getLastKnownInformationCard(card.getId(), Zone.EXILED); + } + return cardState != null && cardState.getCounters().getCount(CounterType.ICE) > 0; + } } @Override @@ -174,6 +196,6 @@ public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID a if (mana.getSourceObject().isSnow()) { return mana.getFirstAvailable(); } - return manaType; + return null; } } diff --git a/Mage.Sets/src/mage/cards/d/DreadCacodemon.java b/Mage.Sets/src/mage/cards/d/DreadCacodemon.java index fc193a1d3dc8..fc3961783756 100644 --- a/Mage.Sets/src/mage/cards/d/DreadCacodemon.java +++ b/Mage.Sets/src/mage/cards/d/DreadCacodemon.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.watchers.common.CastFromHandWatcher; /** diff --git a/Mage.Sets/src/mage/cards/d/DreadReturn.java b/Mage.Sets/src/mage/cards/d/DreadReturn.java index 0383ed1aac00..f68946f58a00 100644 --- a/Mage.Sets/src/mage/cards/d/DreadReturn.java +++ b/Mage.Sets/src/mage/cards/d/DreadReturn.java @@ -1,7 +1,5 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.abilities.keyword.FlashbackAbility; @@ -11,23 +9,28 @@ import mage.constants.TimingRule; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledPermanent; import mage.target.common.TargetCardInYourGraveyard; -import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; /** - * * @author jonubuu */ public final class DreadReturn extends CardImpl { + private static final FilterControlledPermanent filter = new FilterControlledCreaturePermanent("creatures"); + public DreadReturn(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}{B}"); // Return target creature card from your graveyard to the battlefield. this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); + // Flashback-Sacrifice three creatures. - this.addAbility(new FlashbackAbility(new SacrificeTargetCost(new TargetControlledCreaturePermanent(3, 3, new FilterControlledCreaturePermanent("three creatures"), true)), TimingRule.SORCERY)); + this.addAbility(new FlashbackAbility(new SacrificeTargetCost(new TargetControlledPermanent(3, filter)), TimingRule.SORCERY)); } private DreadReturn(final DreadReturn card) { diff --git a/Mage.Sets/src/mage/cards/d/DreadWanderer.java b/Mage.Sets/src/mage/cards/d/DreadWanderer.java index b0f2fb71210a..c23799d48430 100644 --- a/Mage.Sets/src/mage/cards/d/DreadWanderer.java +++ b/Mage.Sets/src/mage/cards/d/DreadWanderer.java @@ -39,7 +39,7 @@ public DreadWanderer(UUID ownerId, CardSetInfo setInfo) { new ManaCostsImpl("{2}{B}"), HeckbentCondition.instance, "{2}{B}: Return {this} from your graveyard to the battlefield. " - + "Activate this ability only any time you could cast a sorcery " + + "Activate only as a sorcery " + "and only if you have one or fewer cards in hand." ); ability.setTiming(TimingRule.SORCERY); diff --git a/Mage.Sets/src/mage/cards/d/DreadhordeArcanist.java b/Mage.Sets/src/mage/cards/d/DreadhordeArcanist.java index 0df75782b32d..5c1f0edb6dfb 100644 --- a/Mage.Sets/src/mage/cards/d/DreadhordeArcanist.java +++ b/Mage.Sets/src/mage/cards/d/DreadhordeArcanist.java @@ -32,7 +32,7 @@ public final class DreadhordeArcanist extends CardImpl { private static final FilterCard filter = new FilterInstantOrSorceryCard( - "instant or sorcery card with converted mana cost less than or equal to this creature's power" + "instant or sorcery card with mana value less than or equal to this creature's power" ); static { @@ -76,7 +76,7 @@ enum DreadhordeArcanistPredicate implements ObjectSourcePlayerPredicate input, Game game) { Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(input.getSourceId()); return sourcePermanent != null - & input.getObject().getConvertedManaCost() <= sourcePermanent.getPower().getValue(); + & input.getObject().getManaValue() <= sourcePermanent.getPower().getValue(); } } @@ -84,9 +84,9 @@ class DreadhordeArcanistEffect extends OneShotEffect { DreadhordeArcanistEffect() { super(Outcome.PlayForFree); - this.staticText = "you may cast target instant or sorcery card with converted mana cost " + this.staticText = "you may cast target instant or sorcery card with mana value " + "less than or equal to {this}'s power from your graveyard without paying its mana cost. " - + "If that card would be put into your graveyard this turn, exile it instead."; + + "If that spell would be put into your graveyard this turn, exile it instead."; } private DreadhordeArcanistEffect(final DreadhordeArcanistEffect effect) { diff --git a/Mage.Sets/src/mage/cards/d/Dreadmalkin.java b/Mage.Sets/src/mage/cards/d/Dreadmalkin.java index 8cc76d83a6ce..cdf6f361a95b 100644 --- a/Mage.Sets/src/mage/cards/d/Dreadmalkin.java +++ b/Mage.Sets/src/mage/cards/d/Dreadmalkin.java @@ -14,7 +14,7 @@ import mage.counters.CounterType; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/d/DreamChisel.java b/Mage.Sets/src/mage/cards/d/DreamChisel.java index 512b217a7f9d..f9fdf124c7fd 100644 --- a/Mage.Sets/src/mage/cards/d/DreamChisel.java +++ b/Mage.Sets/src/mage/cards/d/DreamChisel.java @@ -7,7 +7,7 @@ import mage.constants.CardType; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.other.FaceDownCastablePredicate; +import mage.filter.predicate.card.FaceDownCastablePredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/d/DreamCoat.java b/Mage.Sets/src/mage/cards/d/DreamCoat.java index eb470ab79a1e..caa7f5a7e7f6 100644 --- a/Mage.Sets/src/mage/cards/d/DreamCoat.java +++ b/Mage.Sets/src/mage/cards/d/DreamCoat.java @@ -83,7 +83,7 @@ public boolean apply(Game game, Ability source) { } for (int i = 0; i < 5; i++) { if (i > 0) { - if (!controller.chooseUse(Outcome.Neutral, "Do you wish to choose another color?", source, game)) { + if (!controller.chooseUse(Outcome.Neutral, "Choose another color?", source, game)) { break; } } diff --git a/Mage.Sets/src/mage/cards/d/DreamStrix.java b/Mage.Sets/src/mage/cards/d/DreamStrix.java new file mode 100644 index 000000000000..68382471fbe7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DreamStrix.java @@ -0,0 +1,50 @@ +package mage.cards.d; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.BecomesTargetTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.common.LearnEffect; +import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.constants.SubType; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; + +/** + * + * @author TheElk801 + */ +public final class DreamStrix extends CardImpl { + + public DreamStrix(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.BIRD); + this.subtype.add(SubType.ILLUSION); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Dream Strix becomes the target of a spell, sacrifice it. + this.addAbility(new BecomesTargetTriggeredAbility( + new SacrificeSourceEffect().setText("sacrifice it"), StaticFilters.FILTER_SPELL_A + )); + + // When Dream Strix dies, learn. + this.addAbility(new DiesSourceTriggeredAbility(new LearnEffect())); + } + + private DreamStrix(final DreamStrix card) { + super(card); + } + + @Override + public DreamStrix copy() { + return new DreamStrix(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DreamcallerSiren.java b/Mage.Sets/src/mage/cards/d/DreamcallerSiren.java index beb1d6334eb8..894a50ecc8d9 100644 --- a/Mage.Sets/src/mage/cards/d/DreamcallerSiren.java +++ b/Mage.Sets/src/mage/cards/d/DreamcallerSiren.java @@ -17,7 +17,7 @@ import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetNonlandPermanent; /** diff --git a/Mage.Sets/src/mage/cards/d/DreamscapeArtist.java b/Mage.Sets/src/mage/cards/d/DreamscapeArtist.java index fbb6bbd5f69d..ac2d6b15a71f 100644 --- a/Mage.Sets/src/mage/cards/d/DreamscapeArtist.java +++ b/Mage.Sets/src/mage/cards/d/DreamscapeArtist.java @@ -36,7 +36,7 @@ public DreamscapeArtist(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(1); // {2}{U}, {tap}, Discard a card, Sacrifice a land: Search your library for up to two basic land cards and put them onto the battlefield. Then shuffle your library. - TargetCardInLibrary target = new TargetCardInLibrary(0, 2, StaticFilters.FILTER_CARD_BASIC_LAND); + TargetCardInLibrary target = new TargetCardInLibrary(0, 2, StaticFilters.FILTER_CARD_BASIC_LANDS); Ability ability = new SimpleActivatedAbility( Zone.BATTLEFIELD, new SearchLibraryPutInPlayEffect(target, false, Outcome.PutLandInPlay), diff --git a/Mage.Sets/src/mage/cards/d/DrillSkimmer.java b/Mage.Sets/src/mage/cards/d/DrillSkimmer.java index 98ca896b9a9a..33e5ad1ea450 100644 --- a/Mage.Sets/src/mage/cards/d/DrillSkimmer.java +++ b/Mage.Sets/src/mage/cards/d/DrillSkimmer.java @@ -16,7 +16,7 @@ import mage.constants.Duration; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/d/DriverOfTheDead.java b/Mage.Sets/src/mage/cards/d/DriverOfTheDead.java index 00c34c3d6349..e3e848ceaebc 100644 --- a/Mage.Sets/src/mage/cards/d/DriverOfTheDead.java +++ b/Mage.Sets/src/mage/cards/d/DriverOfTheDead.java @@ -12,7 +12,7 @@ import mage.constants.SubType; import mage.constants.ComparisonType; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.Target; import mage.target.common.TargetCardInYourGraveyard; @@ -21,10 +21,10 @@ */ public final class DriverOfTheDead extends CardImpl { - private static final FilterCreatureCard filter = new FilterCreatureCard("creature card with converted mana cost 2 or less from your graveyard"); + private static final FilterCreatureCard filter = new FilterCreatureCard("creature card with mana value 2 or less from your graveyard"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); } public DriverOfTheDead(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/d/DrogskolCavalry.java b/Mage.Sets/src/mage/cards/d/DrogskolCavalry.java index 6e76745a6906..5b999319a77b 100644 --- a/Mage.Sets/src/mage/cards/d/DrogskolCavalry.java +++ b/Mage.Sets/src/mage/cards/d/DrogskolCavalry.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.SpiritWhiteToken; /** diff --git a/Mage.Sets/src/mage/cards/d/DroningBureaucrats.java b/Mage.Sets/src/mage/cards/d/DroningBureaucrats.java index f100d135e0c1..bc16f08bd035 100644 --- a/Mage.Sets/src/mage/cards/d/DroningBureaucrats.java +++ b/Mage.Sets/src/mage/cards/d/DroningBureaucrats.java @@ -17,7 +17,7 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; /** @@ -54,7 +54,7 @@ class DroningBureaucratsEffect extends OneShotEffect { DroningBureaucratsEffect() { super(Outcome.Benefit); - this.staticText = "Each creature with converted mana cost X can't attack or block this turn"; + this.staticText = "Each creature with mana value X can't attack or block this turn"; } DroningBureaucratsEffect(final DroningBureaucratsEffect effect) { @@ -69,8 +69,8 @@ public DroningBureaucratsEffect copy() { @Override public boolean apply(Game game, Ability source) { int xValue = source.getManaCostsToPay().getX(); - FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with converted mana cost X"); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, xValue)); + FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with mana value X"); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, xValue)); game.addEffect(new CantAttackBlockAllEffect(Duration.EndOfTurn, filter), source); return true; } diff --git a/Mage.Sets/src/mage/cards/d/DroolingGroodion.java b/Mage.Sets/src/mage/cards/d/DroolingGroodion.java index c20e24c882e2..555e22a37747 100644 --- a/Mage.Sets/src/mage/cards/d/DroolingGroodion.java +++ b/Mage.Sets/src/mage/cards/d/DroolingGroodion.java @@ -13,7 +13,7 @@ import mage.constants.*; import static mage.filter.StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/d/DrownInTheLoch.java b/Mage.Sets/src/mage/cards/d/DrownInTheLoch.java index 0fcbf673e25b..7a84ffa4ee64 100644 --- a/Mage.Sets/src/mage/cards/d/DrownInTheLoch.java +++ b/Mage.Sets/src/mage/cards/d/DrownInTheLoch.java @@ -24,10 +24,10 @@ public final class DrownInTheLoch extends CardImpl { private static final FilterSpell filter - = new FilterSpell("spell with converted mana cost less than or equal to " + + = new FilterSpell("spell with mana value less than or equal to " + "the number of cards in its controller's graveyard"); private static final FilterPermanent filter2 - = new FilterCreaturePermanent("creature with converted mana cost less than or equal to " + + = new FilterCreaturePermanent("creature with mana value less than or equal to " + "the number of cards in its controller's graveyard"); static { @@ -65,6 +65,6 @@ enum DrownInTheLochPredicate implements Predicate { @Override public boolean apply(MageObject input, Game game) { Player player = game.getPlayer(game.getControllerId(input.getId())); - return player != null && input.getConvertedManaCost() <= player.getGraveyard().size(); + return player != null && input.getManaValue() <= player.getGraveyard().size(); } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/d/DualCasting.java b/Mage.Sets/src/mage/cards/d/DualCasting.java index f2c9db7c7f31..b2b6b2381798 100644 --- a/Mage.Sets/src/mage/cards/d/DualCasting.java +++ b/Mage.Sets/src/mage/cards/d/DualCasting.java @@ -25,7 +25,7 @@ * @author Loki */ public final class DualCasting extends CardImpl { - private static final FilterSpell filter = new FilterSpell("instant or sorcery spell"); + private static final FilterSpell filter = new FilterSpell("instant or sorcery spell you control"); static { filter.add(Predicates.or( diff --git a/Mage.Sets/src/mage/cards/d/DualStrike.java b/Mage.Sets/src/mage/cards/d/DualStrike.java index 1bd267eaceaf..bbc143e43fcb 100644 --- a/Mage.Sets/src/mage/cards/d/DualStrike.java +++ b/Mage.Sets/src/mage/cards/d/DualStrike.java @@ -68,7 +68,7 @@ public boolean checkTrigger(GameEvent event, Game game) { Spell spell = game.getStack().getSpell(event.getTargetId()); if (spell == null || !spell.isInstantOrSorcery() - || spell.getConvertedManaCost() > 4) { + || spell.getManaValue() > 4) { return false; } this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId())); @@ -78,7 +78,7 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { return "When you cast your next instant or sorcery spell " + - "with converted mana cost 4 or less this turn, " + + "with mana value 4 or less this turn, " + "copy that spell. You may choose new targets for the copy."; } } diff --git a/Mage.Sets/src/mage/cards/d/DubiousChallenge.java b/Mage.Sets/src/mage/cards/d/DubiousChallenge.java index a62e73d8c331..2ab053903868 100644 --- a/Mage.Sets/src/mage/cards/d/DubiousChallenge.java +++ b/Mage.Sets/src/mage/cards/d/DubiousChallenge.java @@ -44,7 +44,7 @@ class DubiousChallengeEffect extends OneShotEffect { public DubiousChallengeEffect() { super(Outcome.Benefit); - this.staticText = "Look at the top ten cards of your library, exile up to two creature cards from among them, then shuffle your library. Target opponent may choose one of the exiled cards and put it onto the battlefield under their control. Put the rest onto the battlefield under your control."; + this.staticText = "Look at the top ten cards of your library, exile up to two creature cards from among them, then shuffle. Target opponent may choose one of the exiled cards and put it onto the battlefield under their control. Put the rest onto the battlefield under your control."; } public DubiousChallengeEffect(final DubiousChallengeEffect effect) { diff --git a/Mage.Sets/src/mage/cards/d/DuelingCoach.java b/Mage.Sets/src/mage/cards/d/DuelingCoach.java new file mode 100644 index 000000000000..61ef28bf2e83 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DuelingCoach.java @@ -0,0 +1,65 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +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.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DuelingCoach extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("creature you control with a +1/+1 counter on it"); + + static { + filter.add(CounterType.P1P1.getPredicate()); + } + + public DuelingCoach(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.MONK); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // When Dueling Coach enters the battlefield, put a +1/+1 counter on target creature. + Ability ability = new EntersBattlefieldTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()) + ); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + + // {4}{W}, {T}: Put a +1/+1 counter on each creature you control with a +1/+1 counter on it. + ability = new SimpleActivatedAbility( + new AddCountersAllEffect(CounterType.P1P1.createInstance(), filter), new ManaCostsImpl("{4}{W}") + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private DuelingCoach(final DuelingCoach card) { + super(card); + } + + @Override + public DuelingCoach copy() { + return new DuelingCoach(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DuelistsHeritage.java b/Mage.Sets/src/mage/cards/d/DuelistsHeritage.java index 5b9175a3ba21..90c16e2009e8 100644 --- a/Mage.Sets/src/mage/cards/d/DuelistsHeritage.java +++ b/Mage.Sets/src/mage/cards/d/DuelistsHeritage.java @@ -81,6 +81,6 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { - return "Whenever one or more creatures attack, " + super.getRule(); + return "Whenever one or more creatures attack, you may have target attacking creature gain double strike until end of turn."; } } diff --git a/Mage.Sets/src/mage/cards/d/DuskmantleSeer.java b/Mage.Sets/src/mage/cards/d/DuskmantleSeer.java index 51a515d86937..598a361a174d 100644 --- a/Mage.Sets/src/mage/cards/d/DuskmantleSeer.java +++ b/Mage.Sets/src/mage/cards/d/DuskmantleSeer.java @@ -52,7 +52,7 @@ class DuskmantleSeerEffect extends OneShotEffect { public DuskmantleSeerEffect() { super(Outcome.Detriment); - this.staticText = "each player reveals the top card of their library, loses life equal to that card's converted mana cost, then puts it into their hand"; + this.staticText = "each player reveals the top card of their library, loses life equal to that card's mana value, then puts it into their hand"; } public DuskmantleSeerEffect(final DuskmantleSeerEffect effect) { @@ -75,7 +75,7 @@ public boolean apply(Game game, Ability source) { Card card = player.getLibrary().getFromTop(game); if (card != null) { player.revealCards(source, ": Revealed by " + player.getName(), new CardsImpl(card), game); - player.loseLife(card.getConvertedManaCost(), game, source, false); + player.loseLife(card.getManaValue(), game, source, false); player.moveCards(card, Zone.HAND, source, game); } } diff --git a/Mage.Sets/src/mage/cards/d/DuskwatchRecruiter.java b/Mage.Sets/src/mage/cards/d/DuskwatchRecruiter.java index cb32120c71c5..ec4aa4d065a4 100644 --- a/Mage.Sets/src/mage/cards/d/DuskwatchRecruiter.java +++ b/Mage.Sets/src/mage/cards/d/DuskwatchRecruiter.java @@ -1,4 +1,3 @@ - package mage.cards.d; import java.util.UUID; @@ -7,6 +6,7 @@ import mage.abilities.TriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; @@ -48,8 +48,7 @@ public DuskwatchRecruiter(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each upkeep, if no spells were cast last turn, transform Duskwatch Recruiter. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private DuskwatchRecruiter(final DuskwatchRecruiter card) { diff --git a/Mage.Sets/src/mage/cards/d/DustStalker.java b/Mage.Sets/src/mage/cards/d/DustStalker.java index 3aa24b269878..56ee4d7da59f 100644 --- a/Mage.Sets/src/mage/cards/d/DustStalker.java +++ b/Mage.Sets/src/mage/cards/d/DustStalker.java @@ -17,7 +17,7 @@ import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorlessPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/d/DwarvenLandslide.java b/Mage.Sets/src/mage/cards/d/DwarvenLandslide.java index 52cb405194fe..46350c45bce1 100644 --- a/Mage.Sets/src/mage/cards/d/DwarvenLandslide.java +++ b/Mage.Sets/src/mage/cards/d/DwarvenLandslide.java @@ -35,7 +35,7 @@ public DwarvenLandslide(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new KickerAbility(kickerCosts)); // Destroy target land. If Dwarven Landslide was kicked, destroy another target land. - getSpellAbility().addEffect(new DestroyTargetEffect("Destroy target land. if this spell was kicked, destroy another target land")); + getSpellAbility().addEffect(new DestroyTargetEffect("Destroy target land. If this spell was kicked, destroy another target land")); getSpellAbility().addTarget(new TargetLandPermanent()); getSpellAbility().setTargetAdjuster(DwarvenLandslideAdjuster.instance); } diff --git a/Mage.Sets/src/mage/cards/d/DwarvenMine.java b/Mage.Sets/src/mage/cards/d/DwarvenMine.java index bf26b0e4b4f7..3bf540dc3fea 100644 --- a/Mage.Sets/src/mage/cards/d/DwarvenMine.java +++ b/Mage.Sets/src/mage/cards/d/DwarvenMine.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.DwarfToken; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/d/DwarvenMiner.java b/Mage.Sets/src/mage/cards/d/DwarvenMiner.java index 3c71f0e09846..7803e8120d73 100644 --- a/Mage.Sets/src/mage/cards/d/DwarvenMiner.java +++ b/Mage.Sets/src/mage/cards/d/DwarvenMiner.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.cards.d; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/d/DwellOnThePast.java b/Mage.Sets/src/mage/cards/d/DwellOnThePast.java index 7484dbbe8203..81214128485d 100644 --- a/Mage.Sets/src/mage/cards/d/DwellOnThePast.java +++ b/Mage.Sets/src/mage/cards/d/DwellOnThePast.java @@ -1,36 +1,26 @@ - package mage.cards.d; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; +import mage.abilities.effects.common.TargetPlayerShufflesTargetCardsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.CardsImpl; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.game.Game; -import mage.players.Player; import mage.target.TargetPlayer; -import mage.target.common.TargetCardInGraveyard; +import mage.target.common.TargetCardInTargetPlayersGraveyard; + +import java.util.UUID; /** - * * @author fireshoes */ public final class DwellOnThePast extends CardImpl { public DwellOnThePast(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{G}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{G}"); // Target player shuffles up to four target cards from their graveyard into their library. - this.getSpellAbility().addEffect(new DwellOnThePastEffect()); + this.getSpellAbility().addEffect(new TargetPlayerShufflesTargetCardsEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); - this.getSpellAbility().addTarget(new DwellOnThePastTarget()); - + this.getSpellAbility().addTarget(new TargetCardInTargetPlayersGraveyard(4)); } private DwellOnThePast(final DwellOnThePast card) { @@ -42,57 +32,3 @@ public DwellOnThePast copy() { return new DwellOnThePast(this); } } - -class DwellOnThePastEffect extends OneShotEffect { - - public DwellOnThePastEffect() { - super(Outcome.Neutral); - this.staticText = "Target player shuffles up to four target cards from their graveyard into their library"; - } - - public DwellOnThePastEffect(final DwellOnThePastEffect effect) { - super(effect); - } - - @Override - public DwellOnThePastEffect copy() { - return new DwellOnThePastEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getFirstTarget()); - if (controller != null) { - return controller.shuffleCardsToLibrary(new CardsImpl(source.getTargets().get(1).getTargets()), game, source); - } - return false; - } -} - -class DwellOnThePastTarget extends TargetCardInGraveyard { - - public DwellOnThePastTarget() { - super(0, 4, new FilterCard("cards from target player's graveyard")); - } - - public DwellOnThePastTarget(final DwellOnThePastTarget target) { - super(target); - } - - @Override - public boolean canTarget(UUID id, Ability source, Game game) { - Card card = game.getCard(id); - if (card != null && game.getState().getZone(card.getId()) == Zone.GRAVEYARD) { - UUID firstTarget = source.getFirstTarget(); - if (firstTarget != null && game.getPlayer(firstTarget).getGraveyard().contains(id)) { - return filter.match(card, game); - } - } - return false; - } - - @Override - public DwellOnThePastTarget copy() { - return new DwellOnThePastTarget(this); - } -} diff --git a/Mage.Sets/src/mage/cards/d/DwynensElite.java b/Mage.Sets/src/mage/cards/d/DwynensElite.java index 4dcce5b15c0d..f72f424959f4 100644 --- a/Mage.Sets/src/mage/cards/d/DwynensElite.java +++ b/Mage.Sets/src/mage/cards/d/DwynensElite.java @@ -11,7 +11,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.ElfWarriorToken; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/e/EagerFirstYear.java b/Mage.Sets/src/mage/cards/e/EagerFirstYear.java new file mode 100644 index 000000000000..3267b090994e --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EagerFirstYear.java @@ -0,0 +1,39 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EagerFirstYear extends CardImpl { + + public EagerFirstYear(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Magecraft - Whenever you cast or copy an instant or sorcery spell, Eager First-Year gets +1/+0 until end of turn. + this.addAbility(new MagecraftAbility(new BoostSourceEffect(1, 0, Duration.EndOfTurn))); + } + + private EagerFirstYear(final EagerFirstYear card) { + super(card); + } + + @Override + public EagerFirstYear copy() { + return new EagerFirstYear(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EarwigSquad.java b/Mage.Sets/src/mage/cards/e/EarwigSquad.java index e92d6bd44b37..516afaaaddce 100644 --- a/Mage.Sets/src/mage/cards/e/EarwigSquad.java +++ b/Mage.Sets/src/mage/cards/e/EarwigSquad.java @@ -44,7 +44,7 @@ public EarwigSquad(UUID ownerId, CardSetInfo setInfo) { EntersBattlefieldTriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new EarwigSquadEffect(), false); ability.addTarget(new TargetOpponent()); this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, ProwlCostWasPaidCondition.instance, - "When {this} enters the battlefield, if its prowl cost was paid, search target opponent's library for three cards and exile them. Then that player shuffles their library.") + "When {this} enters the battlefield, if its prowl cost was paid, search target opponent's library for three cards and exile them. Then that player shuffles.") .addHint(ProwlCostWasPaidHint.instance)); } @@ -63,7 +63,7 @@ class EarwigSquadEffect extends OneShotEffect { public EarwigSquadEffect() { super(Outcome.Benefit); - staticText = "search target opponent's library for three cards and exile them. Then that player shuffles their library"; + staticText = "search target opponent's library for three cards and exile them. Then that player shuffles"; } public EarwigSquadEffect(final EarwigSquadEffect effect) { diff --git a/Mage.Sets/src/mage/cards/e/EasyPrey.java b/Mage.Sets/src/mage/cards/e/EasyPrey.java index 988b57063c0d..a008fe9114bc 100644 --- a/Mage.Sets/src/mage/cards/e/EasyPrey.java +++ b/Mage.Sets/src/mage/cards/e/EasyPrey.java @@ -9,7 +9,7 @@ import mage.constants.ComparisonType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetPermanent; import java.util.UUID; @@ -20,10 +20,10 @@ public final class EasyPrey extends CardImpl { private static final FilterPermanent filter - = new FilterCreaturePermanent("creature with converted mana cost 2 or less"); + = new FilterCreaturePermanent("creature with mana value 2 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); } public EasyPrey(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/e/EaterOfHope.java b/Mage.Sets/src/mage/cards/e/EaterOfHope.java index f12398e9ddad..b3fbc447806b 100644 --- a/Mage.Sets/src/mage/cards/e/EaterOfHope.java +++ b/Mage.Sets/src/mage/cards/e/EaterOfHope.java @@ -17,7 +17,7 @@ import mage.constants.Zone; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/e/EaterOfTheDead.java b/Mage.Sets/src/mage/cards/e/EaterOfTheDead.java index 69da60ae3dca..4c34144d7524 100644 --- a/Mage.Sets/src/mage/cards/e/EaterOfTheDead.java +++ b/Mage.Sets/src/mage/cards/e/EaterOfTheDead.java @@ -1,6 +1,5 @@ package mage.cards.e; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -16,10 +15,12 @@ import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.common.TargetCardInGraveyard; +import java.util.UUID; + /** - * * @author fireshoes */ public final class EaterOfTheDead extends CardImpl { @@ -31,7 +32,7 @@ public EaterOfTheDead(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(4); // {0}: If Eater of the Dead is tapped, exile target creature card from a graveyard and untap Eater of the Dead. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new EaterOfTheDeadEffect(), new GenericManaCost(0)); + Ability ability = new SimpleActivatedAbility(new EaterOfTheDeadEffect(), new GenericManaCost(0)); ability.addTarget(new TargetCardInGraveyard(StaticFilters.FILTER_CARD_CREATURE)); this.addAbility(ability); } @@ -49,28 +50,31 @@ public EaterOfTheDead copy() { class EaterOfTheDeadEffect extends OneShotEffect { EaterOfTheDeadEffect() { - super(Outcome.DestroyPermanent); + super(Outcome.Exile); staticText = "If {this} is tapped, exile target creature card from a graveyard and untap {this}"; } - EaterOfTheDeadEffect(final EaterOfTheDeadEffect effect) { + private EaterOfTheDeadEffect(final EaterOfTheDeadEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = source.getSourcePermanentOrLKI(game); + if (player == null || permanent == null || !permanent.isTapped()) { + return false; + } Card card = game.getCard(source.getFirstTarget()); - if (sourcePermanent != null && sourcePermanent.isTapped() && card != null) { - card.moveToExile(null, "Eater of the Dead", source, game); - sourcePermanent.untap(game); + if (card != null) { + player.moveCards(card, Zone.EXILED, source, game); } - return false; + permanent.untap(game); + return true; } @Override public EaterOfTheDeadEffect copy() { return new EaterOfTheDeadEffect(this); } - } diff --git a/Mage.Sets/src/mage/cards/e/EchoBaseCommando.java b/Mage.Sets/src/mage/cards/e/EchoBaseCommando.java index b9f525d81105..7812cab8b0b7 100644 --- a/Mage.Sets/src/mage/cards/e/EchoBaseCommando.java +++ b/Mage.Sets/src/mage/cards/e/EchoBaseCommando.java @@ -86,7 +86,7 @@ public boolean apply(Game game, Ability source, Ability abilityToModify) { public boolean applies(Ability abilityToModify, Ability source, Game game) { if (abilityToModify.getAbilityType() == AbilityType.ACTIVATED || (abilityToModify.getAbilityType() == AbilityType.MANA && (abilityToModify instanceof ActivatedAbility))) { Permanent permanent = game.getPermanent(abilityToModify.getSourceId()); - if (permanent != null && filter.match(permanent, source.getSourceId(), source.getControllerId(), game)) { + if (filter.match(permanent, source.getSourceId(), source.getControllerId(), game)) { return true; } } diff --git a/Mage.Sets/src/mage/cards/e/EcologicalAppreciation.java b/Mage.Sets/src/mage/cards/e/EcologicalAppreciation.java new file mode 100644 index 000000000000..51900dabbacf --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EcologicalAppreciation.java @@ -0,0 +1,157 @@ +package mage.cards.e; + +import com.google.common.collect.Sets; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileSpellEffect; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.common.TargetOpponent; +import mage.util.CardUtil; + +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * + * @author htrajan + */ +public final class EcologicalAppreciation extends CardImpl { + + public EcologicalAppreciation(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{2}{G}"); + + // Search your library and graveyard for up to four creature cards with different names that each have mana value X or less and reveal them. An opponent chooses two of those cards. Shuffle the chosen cards into your library and put the rest onto the battlefield. Exile Ecological Appreciation. + this.getSpellAbility().addEffect(new EcologicalAppreciationEffect()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); + } + + private EcologicalAppreciation(final EcologicalAppreciation card) { + super(card); + } + + @Override + public EcologicalAppreciation copy() { + return new EcologicalAppreciation(this); + } +} + +class EcologicalAppreciationEffect extends OneShotEffect { + + EcologicalAppreciationEffect() { + super(Outcome.Benefit); + staticText = "search your library and graveyard for up to four creature cards with different names that each have mana value X or less and reveal them. An opponent chooses two of those cards. Shuffle the chosen cards into your library and put the rest onto the battlefield"; + } + + private EcologicalAppreciationEffect(final EcologicalAppreciationEffect effect) { + super(effect); + } + + @Override + public EcologicalAppreciationEffect copy() { + return new EcologicalAppreciationEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + int xValue = source.getManaCostsToPay().getX(); + FilterCard filter = new FilterCreatureCard(); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); + TargetCard targetCardsInLibrary = new TargetCardInLibrary(0, 4, filter) { + @Override + public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) { + if (!super.canTarget(playerId, id, source, game)) { + return false; + } + Card card = game.getCard(id); + Set disallowedCards = this.getTargets().stream() + .map(game::getCard) + .collect(Collectors.toSet()); + return isValidTarget(card, disallowedCards); + } + }; + targetCardsInLibrary.setNotTarget(true); + targetCardsInLibrary.withChooseHint("Step 1 of 2: Search library"); + player.choose(Outcome.PutCreatureInPlay, new CardsImpl(player.getLibrary().getCards(game)), targetCardsInLibrary, game); + Cards cards = new CardsImpl(targetCardsInLibrary.getTargets()); + + boolean status = !cards.isEmpty(); + + if (status) { + int remainingCards = 4 - cards.size(); + if (remainingCards > 0) { + TargetCard targetCardsInGY = new TargetCardInYourGraveyard(0, remainingCards, filter) { + @Override + public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) { + if (!super.canTarget(playerId, id, source, game)) { + return false; + } + Card card = game.getCard(id); + Set disallowedCards = this.getTargets().stream() + .map(game::getCard) + .collect(Collectors.toSet()); + return isValidTarget(card, Sets.union(disallowedCards, cards.getCards(game))); + } + }; + targetCardsInGY.setNotTarget(true); + targetCardsInGY.withChooseHint("Step 2 of 2: Search graveyard"); + player.choose(Outcome.PutCreatureInPlay, new CardsImpl(player.getGraveyard().getCards(game)), targetCardsInGY, game); + cards.addAll(targetCardsInGY.getTargets()); + } + + TargetOpponent targetOpponent = new TargetOpponent(); + targetOpponent.setNotTarget(true); + player.choose(outcome, targetOpponent, source.getSourceId(), game); + Player opponent = game.getPlayer(targetOpponent.getFirstTarget()); + + if (opponent == null) { + status = false; + } + + if (status) { + TargetCard chosenCards = new TargetCard(2, Zone.ALL, StaticFilters.FILTER_CARD); + chosenCards.setNotTarget(true); + opponent.choose(outcome, cards, chosenCards, game); + Cards toShuffle = new CardsImpl(chosenCards.getTargets().stream() + .map(game::getCard) + .collect(Collectors.toList())); + + player.putCardsOnTopOfLibrary(toShuffle, game, source, false); + cards.removeAll(toShuffle); + + player.moveCards(cards, Zone.BATTLEFIELD, source, game); + } + } + + player.shuffleLibrary(source, game); + + return status; + } + + private boolean isValidTarget(Card target, Set disallowedCards) { + return target != null && + disallowedCards.stream() + .filter(Objects::nonNull) + .map(MageObject::getName) + .noneMatch(name -> CardUtil.haveSameNames(name, target.getName())); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/e/EddytrailHawk.java b/Mage.Sets/src/mage/cards/e/EddytrailHawk.java index 1779d06ad0fd..d89c7edb8e27 100644 --- a/Mage.Sets/src/mage/cards/e/EddytrailHawk.java +++ b/Mage.Sets/src/mage/cards/e/EddytrailHawk.java @@ -17,7 +17,7 @@ import mage.constants.SubType; import mage.constants.Duration; import mage.filter.common.FilterAttackingCreature; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/e/EdgeOfAutumn.java b/Mage.Sets/src/mage/cards/e/EdgeOfAutumn.java index e38dbbaa4bbc..6770ba3e97a9 100644 --- a/Mage.Sets/src/mage/cards/e/EdgeOfAutumn.java +++ b/Mage.Sets/src/mage/cards/e/EdgeOfAutumn.java @@ -32,7 +32,7 @@ public EdgeOfAutumn(UUID ownerId, CardSetInfo setInfo) { // If you control four or fewer lands, search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library. this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true), new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.FEWER_THAN, 5), - "If you control four or fewer lands, search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library.")); + "If you control four or fewer lands, search your library for a basic land card, put it onto the battlefield tapped, then shuffle.")); // Cycling-Sacrifice a land. this.addAbility(new CyclingAbility(new SacrificeTargetCost(new TargetControlledPermanent(filter)))); diff --git a/Mage.Sets/src/mage/cards/e/EdificeOfAuthority.java b/Mage.Sets/src/mage/cards/e/EdificeOfAuthority.java index 9ecc32d1008b..543deb527d08 100644 --- a/Mage.Sets/src/mage/cards/e/EdificeOfAuthority.java +++ b/Mage.Sets/src/mage/cards/e/EdificeOfAuthority.java @@ -27,7 +27,7 @@ */ public final class EdificeOfAuthority extends CardImpl { - private static final String rule = "{1}, {T}: Until your next turn, target creature can't attack or block and its activated abilities can't be activated. Activate this ability only if there are three or more brick counter on {this}."; + private static final String rule = "{1}, {T}: Until your next turn, target creature can't attack or block and its activated abilities can't be activated. Activate only if there are three or more brick counter on {this}."; public EdificeOfAuthority(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); diff --git a/Mage.Sets/src/mage/cards/e/EfreetFlamepainter.java b/Mage.Sets/src/mage/cards/e/EfreetFlamepainter.java new file mode 100644 index 000000000000..b99ecf11423d --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EfreetFlamepainter.java @@ -0,0 +1,88 @@ +package mage.cards.e; + +import mage.ApprovingObject; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileCardEnteringGraveyardReplacementEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class EfreetFlamepainter extends CardImpl { + + public EfreetFlamepainter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.EFREET); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // Double strike + this.addAbility(DoubleStrikeAbility.getInstance()); + + // Whenever Efreet Flamepainter deals combat damage to a player, you may cast target instant or sorcery card from your graveyard without paying its mana cost. If that spell would be put into your graveyard, exile it instead. + Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new EfreetFlamepainterEffect(), false); + ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY)); + this.addAbility(ability); + } + + private EfreetFlamepainter(final EfreetFlamepainter card) { + super(card); + } + + @Override + public EfreetFlamepainter copy() { + return new EfreetFlamepainter(this); + } +} + +class EfreetFlamepainterEffect extends OneShotEffect { + + EfreetFlamepainterEffect() { + super(Outcome.PlayForFree); + staticText = "you may cast target instant or sorcery card from your graveyard without paying its mana cost. If that spell would be put into your graveyard, exile it instead"; + } + + EfreetFlamepainterEffect(EfreetFlamepainterEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + UUID targetId = source.getFirstTarget(); + if (targetId != null) { + Card card = game.getCard(targetId); + if (card != null && controller.chooseUse(outcome, "Cast " + card.getName() + " without paying its mana cost?", source, game)) { + game.addEffect(new ExileCardEnteringGraveyardReplacementEffect(card.getId()), source); + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); + controller.cast(controller.chooseAbilityForCast(card, game, true), + game, true, new ApprovingObject(source, game)); + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); + } + } + return true; + } + + @Override + public EfreetFlamepainterEffect copy() { + return new EfreetFlamepainterEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EfreetWeaponmaster.java b/Mage.Sets/src/mage/cards/e/EfreetWeaponmaster.java index 33b9aed35c4d..62be553400f0 100644 --- a/Mage.Sets/src/mage/cards/e/EfreetWeaponmaster.java +++ b/Mage.Sets/src/mage/cards/e/EfreetWeaponmaster.java @@ -15,10 +15,9 @@ import mage.constants.Duration; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/e/EidolonOfTheGreatRevel.java b/Mage.Sets/src/mage/cards/e/EidolonOfTheGreatRevel.java index e087b053504d..afca5fc4de8d 100644 --- a/Mage.Sets/src/mage/cards/e/EidolonOfTheGreatRevel.java +++ b/Mage.Sets/src/mage/cards/e/EidolonOfTheGreatRevel.java @@ -13,7 +13,6 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.stack.Spell; import mage.target.targetpointer.FixedTarget; @@ -71,7 +70,7 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getConvertedManaCost() <= 3){ + if (spell != null && spell.getManaValue() <= 3){ for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getPlayerId())); } @@ -82,6 +81,6 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { - return "Whenever a player casts a spell with converted mana cost 3 or less, {this} deals 2 damage to that player."; + return "Whenever a player casts a spell with mana value 3 or less, {this} deals 2 damage to that player."; } } diff --git a/Mage.Sets/src/mage/cards/e/ElderPineOfJukai.java b/Mage.Sets/src/mage/cards/e/ElderPineOfJukai.java index 85d33d865b06..0363c022d523 100644 --- a/Mage.Sets/src/mage/cards/e/ElderPineOfJukai.java +++ b/Mage.Sets/src/mage/cards/e/ElderPineOfJukai.java @@ -28,7 +28,7 @@ public ElderPineOfJukai(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(1); // Whenever you cast a Spirit or Arcane spell, reveal the top three cards of your library. Put all land cards revealed this way into your hand and the rest on the bottom of your library in any order. - this.addAbility(new SpellCastControllerTriggeredAbility(new RevealLibraryPutIntoHandEffect(3, new FilterLandCard(), Zone.LIBRARY), StaticFilters.SPIRIT_OR_ARCANE_CARD, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new RevealLibraryPutIntoHandEffect(3, StaticFilters.FILTER_CARD_LANDS, Zone.LIBRARY), StaticFilters.SPIRIT_OR_ARCANE_CARD, false)); // Soulshift 2 this.addAbility(new SoulshiftAbility(2)); diff --git a/Mage.Sets/src/mage/cards/e/ElderSpawn.java b/Mage.Sets/src/mage/cards/e/ElderSpawn.java index 5fc01de792d2..1e6d943c5016 100644 --- a/Mage.Sets/src/mage/cards/e/ElderSpawn.java +++ b/Mage.Sets/src/mage/cards/e/ElderSpawn.java @@ -84,7 +84,7 @@ public boolean apply(Game game, Ability source) { if (controller != null && sourcePermanent != null) { TargetControlledPermanent target = new TargetControlledPermanent(1, 1, filter, true); SacrificeTargetCost cost = new SacrificeTargetCost(target); - if (!controller.chooseUse(Outcome.AIDontUseIt, "Do you wish to sacrifice an Island?", source, game) + if (!controller.chooseUse(Outcome.AIDontUseIt, "Sacrifice an Island?", source, game) || !cost.canPay(source, source, source.getControllerId(), game) || !cost.pay(source, game, source, source.getControllerId(), true)) { sourcePermanent.sacrifice(source, game); diff --git a/Mage.Sets/src/mage/cards/e/EldraziAggressor.java b/Mage.Sets/src/mage/cards/e/EldraziAggressor.java index b145ca088b23..f5ac7fec13c6 100644 --- a/Mage.Sets/src/mage/cards/e/EldraziAggressor.java +++ b/Mage.Sets/src/mage/cards/e/EldraziAggressor.java @@ -17,7 +17,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.ColorlessPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/e/EldraziDisplacer.java b/Mage.Sets/src/mage/cards/e/EldraziDisplacer.java index 7fbe6397109b..13d0ab9db8f3 100644 --- a/Mage.Sets/src/mage/cards/e/EldraziDisplacer.java +++ b/Mage.Sets/src/mage/cards/e/EldraziDisplacer.java @@ -4,7 +4,6 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.ExileTargetForSourceEffect; import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect; import mage.abilities.keyword.DevoidAbility; @@ -14,7 +13,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/e/EldraziMimic.java b/Mage.Sets/src/mage/cards/e/EldraziMimic.java index 6ddeeeda5318..938f6f1205ec 100644 --- a/Mage.Sets/src/mage/cards/e/EldraziMimic.java +++ b/Mage.Sets/src/mage/cards/e/EldraziMimic.java @@ -13,7 +13,7 @@ import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorlessPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/e/EldritchEvolution.java b/Mage.Sets/src/mage/cards/e/EldritchEvolution.java index 3d5bbd0a761a..be08a954d3a2 100644 --- a/Mage.Sets/src/mage/cards/e/EldritchEvolution.java +++ b/Mage.Sets/src/mage/cards/e/EldritchEvolution.java @@ -16,7 +16,7 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.StaticFilters; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -38,7 +38,7 @@ public EldritchEvolution(UUID ownerId, CardSetInfo setInfo) { // Search your library for a creature card with converted mana cost X or less, where X is 2 plus the sacrificed creature's converted mana cost. // Put that card onto the battlefield, then shuffle your library. Exile Eldritch Evolution. this.getSpellAbility().addEffect(new EldritchEvolutionEffect()); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private EldritchEvolution(final EldritchEvolution card) { @@ -55,8 +55,8 @@ class EldritchEvolutionEffect extends OneShotEffect { EldritchEvolutionEffect() { super(Outcome.Benefit); - staticText = "Search your library for a creature card with converted mana cost X or less, where X is 2 plus the sacrificed creature's converted mana cost. Put that card " - + "onto the battlefield, then shuffle your library"; + staticText = "Search your library for a creature card with mana value X or less, where X is 2 plus the sacrificed creature's mana value. Put that card " + + "onto the battlefield, then shuffle"; } EldritchEvolutionEffect(final EldritchEvolutionEffect effect) { @@ -77,9 +77,9 @@ public boolean apply(Game game, Ability source) { } Player controller = game.getPlayer(source.getControllerId()); if (sacrificedPermanent != null && controller != null) { - int newConvertedCost = sacrificedPermanent.getConvertedManaCost() + 2; - FilterCard filter = new FilterCard("creature card with converted mana cost " + newConvertedCost + " or less"); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, newConvertedCost+1)); + int newConvertedCost = sacrificedPermanent.getManaValue() + 2; + FilterCard filter = new FilterCard("creature card with mana value " + newConvertedCost + " or less"); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, newConvertedCost+1)); filter.add(CardType.CREATURE.getPredicate()); TargetCardInLibrary target = new TargetCardInLibrary(filter); if (controller.searchLibrary(target, source, game)) { diff --git a/Mage.Sets/src/mage/cards/e/Electrolyze.java b/Mage.Sets/src/mage/cards/e/Electrolyze.java index bea848a0eff8..9e5540d91292 100644 --- a/Mage.Sets/src/mage/cards/e/Electrolyze.java +++ b/Mage.Sets/src/mage/cards/e/Electrolyze.java @@ -22,11 +22,11 @@ public Electrolyze(UUID ownerId, CardSetInfo setInfo) { // Electrolyze deals 2 damage divided as you choose among one or two target creatures and/or players. Effect effect = new DamageMultiEffect(2); - effect.setText("{this} deals 2 damage divided as you choose among one or two target creatures and/or players"); + effect.setText("{this} deals 2 damage divided as you choose among one or two targets"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(2)); // Draw a card. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); } private Electrolyze(final Electrolyze card) { diff --git a/Mage.Sets/src/mage/cards/e/ElementalExpressionist.java b/Mage.Sets/src/mage/cards/e/ElementalExpressionist.java new file mode 100644 index 000000000000..039a1f079f61 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ElementalExpressionist.java @@ -0,0 +1,129 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.ZoneChangeTriggeredAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.token.PrismariToken; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ElementalExpressionist extends CardImpl { + + public ElementalExpressionist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U/R}{U/R}{U/R}{U/R}"); + + this.subtype.add(SubType.ORC); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, choose target creature you control. Until end of turn, it gains "If this creature would leave the battlefield, exile it instead of putting it anywhere else" and "When you exile this creature, create a 4/4 blue and red Elemental creature token." + Ability ability = new MagecraftAbility(new GainAbilityTargetEffect( + new SimpleStaticAbility(new ElementalExpressionistReplacementEffect()), + Duration.EndOfTurn, "choose target creature you control. Until end of turn, " + + "it gains \"If this creature would leave the battlefield, exile it instead of putting it anywhere else\"" + )); + ability.addEffect(new GainAbilityTargetEffect( + new ElementalExpressionistTriggeredAbility(), Duration.EndOfTurn, + "and \"When this creature is put into exile, create a 4/4 blue and red Elemental creature token.\"" + )); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + private ElementalExpressionist(final ElementalExpressionist card) { + super(card); + } + + @Override + public ElementalExpressionist copy() { + return new ElementalExpressionist(this); + } +} + +class ElementalExpressionistReplacementEffect extends ReplacementEffectImpl { + + ElementalExpressionistReplacementEffect() { + super(Duration.WhileOnBattlefield, Outcome.Tap); + staticText = "If this creature would leave the battlefield, exile it instead of putting it anywhere else"; + } + + private ElementalExpressionistReplacementEffect(final ElementalExpressionistReplacementEffect effect) { + super(effect); + } + + @Override + public ElementalExpressionistReplacementEffect copy() { + return new ElementalExpressionistReplacementEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + ((ZoneChangeEvent) event).setToZone(Zone.EXILED); + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return event.getTargetId().equals(source.getSourceId()) + && ((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD + && ((ZoneChangeEvent) event).getToZone() != Zone.EXILED; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } +} + +class ElementalExpressionistTriggeredAbility extends ZoneChangeTriggeredAbility { + + ElementalExpressionistTriggeredAbility() { + super(Zone.BATTLEFIELD, Zone.BATTLEFIELD, Zone.EXILED, new CreateTokenEffect(new PrismariToken()), "", false); + } + + private ElementalExpressionistTriggeredAbility(final ElementalExpressionistTriggeredAbility ability) { + super(ability); + } + + @Override + public ElementalExpressionistTriggeredAbility copy() { + return new ElementalExpressionistTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.getTarget() == null || zEvent.getTarget() != getSourcePermanentOrLKI(game)) { + return false; + } + // custom check cause ZoneChangeTriggeredAbility for source object only + return (fromZone == null || zEvent.getFromZone() == fromZone) + && (zEvent.getToZone() == toZone || zEvent.getOriginalToZone() == toZone); + } + + @Override + public String getRule() { + return "When this creature is put into exile, create a 4/4 blue and red Elemental creature token."; + } +} diff --git a/Mage.Sets/src/mage/cards/e/ElementalMasterpiece.java b/Mage.Sets/src/mage/cards/e/ElementalMasterpiece.java new file mode 100644 index 000000000000..4442a2f101ba --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ElementalMasterpiece.java @@ -0,0 +1,44 @@ +package mage.cards.e; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.game.permanent.token.PrismariToken; +import mage.game.permanent.token.TreasureToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ElementalMasterpiece extends CardImpl { + + public ElementalMasterpiece(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{U}{R}"); + + // Create two 4/4 blue and red Elemental creature tokens. + this.getSpellAbility().addEffect(new CreateTokenEffect(new PrismariToken(), 2)); + + // {U/R}{U/R}, Discard Elemental Masterpiece: Create a Treasure token. + Ability ability = new SimpleActivatedAbility( + Zone.HAND, new CreateTokenEffect(new TreasureToken()), new ManaCostsImpl("{U/R}{U/R}") + ); + ability.addCost(new DiscardSourceCost()); + this.addAbility(ability); + } + + private ElementalMasterpiece(final ElementalMasterpiece card) { + super(card); + } + + @Override + public ElementalMasterpiece copy() { + return new ElementalMasterpiece(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/ElementalSummoning.java b/Mage.Sets/src/mage/cards/e/ElementalSummoning.java new file mode 100644 index 000000000000..2feedeb1ec83 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ElementalSummoning.java @@ -0,0 +1,34 @@ +package mage.cards.e; + +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.PrismariToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ElementalSummoning extends CardImpl { + + public ElementalSummoning(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U/R}{U/R}"); + + this.subtype.add(SubType.LESSON); + + // Create a 4/4 blue and red Elemental creature token. + this.getSpellAbility().addEffect(new CreateTokenEffect(new PrismariToken())); + } + + private ElementalSummoning(final ElementalSummoning card) { + super(card); + } + + @Override + public ElementalSummoning copy() { + return new ElementalSummoning(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/ElementalistsPalette.java b/Mage.Sets/src/mage/cards/e/ElementalistsPalette.java new file mode 100644 index 000000000000..435f11e532ea --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ElementalistsPalette.java @@ -0,0 +1,161 @@ +package mage.cards.e; + +import mage.ConditionalMana; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.VariableManaCost; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.mana.ManaEffect; +import mage.abilities.mana.AnyColorManaAbility; +import mage.abilities.mana.SimpleManaAbility; +import mage.abilities.mana.builder.ConditionalManaBuilder; +import mage.abilities.mana.conditional.ManaCondition; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.VariableManaCostPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ElementalistsPalette extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("a spell with {X} in its mana cost"); + + static { + filter.add(VariableManaCostPredicate.instance); + } + + public ElementalistsPalette(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + // Whenever you cast a spell with {X} in its mana cost, put two charge counters on Elementalist's Palette. + this.addAbility(new SpellCastControllerTriggeredAbility( + new AddCountersSourceEffect(CounterType.CHARGE.createInstance(2)), filter, false + )); + + // {T}: Add one mana of any color. + this.addAbility(new AnyColorManaAbility()); + + // {T}: Add {C} for each charge counter on Elementalist's Palette. Spend this mana only on costs that contain {X}. + this.addAbility(new SimpleManaAbility( + Zone.BATTLEFIELD, new ElementalistsPaletteManaEffect(), new TapSourceCost() + )); + } + + private ElementalistsPalette(final ElementalistsPalette card) { + super(card); + } + + @Override + public ElementalistsPalette copy() { + return new ElementalistsPalette(this); + } +} + +class ElementalistsPaletteManaEffect extends ManaEffect { + + private final ConditionalManaBuilder manaBuilder = new ElementalistsPaletteManaBuilder(); + + ElementalistsPaletteManaEffect() { + this.staticText = "add {C} for each charge counter on {this}. Spend this mana only on costs that contain {X}"; + } + + private ElementalistsPaletteManaEffect(final ElementalistsPaletteManaEffect effect) { + super(effect); + } + + @Override + public ElementalistsPaletteManaEffect copy() { + return new ElementalistsPaletteManaEffect(this); + } + + @Override + public List getNetMana(Game game, Ability source) { + List netMana = new ArrayList<>(); + if (game == null) { + return netMana; + } + Permanent permanent = source.getSourcePermanentOrLKI(game); + if (permanent != null) { + netMana.add(manaBuilder.setMana(Mana.ColorlessMana( + permanent.getCounters(game).getCount(CounterType.CHARGE) + ), source, game).build()); + } + return netMana; + } + + @Override + public Mana produceMana(Game game, Ability source) { + Mana mana = new Mana(); + if (game == null) { + return mana; + } + Permanent permanent = source.getSourcePermanentOrLKI(game); + if (permanent == null) { + return mana; + } + return manaBuilder.setMana(Mana.ColorlessMana( + permanent.getCounters(game).getCount(CounterType.CHARGE) + ), source, game).build(); + } +} + +class ElementalistsPaletteConditionalMana extends ConditionalMana { + + ElementalistsPaletteConditionalMana(Mana mana) { + super(mana); + addCondition(new ElementalistsPaletteManaCondition()); + } +} + +class ElementalistsPaletteManaBuilder extends ConditionalManaBuilder { + @Override + public ConditionalMana build(Object... options) { + return new ElementalistsPaletteConditionalMana(this.mana); + } + + @Override + public String getRule() { + return "Spend this mana only on costs that contain {X}"; + } +} + +class ElementalistsPaletteManaCondition extends ManaCondition { + + /* + A “cost that contains {X}” may be a spell’s total cost, an activated ability’s cost, a suspend cost, or a cost you’re + asked to pay as part of the resolution of a spell or ability (such as Condescend). A spell’s total cost includes either + its mana cost (printed in the upper right corner) or its alternative cost (such as flashback), as well as any additional + costs (such as kicker). If it’s something you can spend mana on, it’s a cost. If that cost includes the {X} symbol in it, + you can spend mana generated by Rosheen on that cost. (2017-11-17) + */ + @Override + public boolean apply(Game game, Ability source, UUID originalId, Cost costToPay) { + boolean result; + if (costToPay instanceof ManaCosts) { + result = !((ManaCosts) costToPay).getVariableCosts().isEmpty(); + } else { + result = costToPay instanceof VariableManaCost; + } + if (!result) { + if (game != null && game.inCheckPlayableState()) { + return true; // TODO: Check the card if there are related abilities with {X} costs. + } + } + return result; + } +} diff --git a/Mage.Sets/src/mage/cards/e/Eliminate.java b/Mage.Sets/src/mage/cards/e/Eliminate.java index 39423122e385..aff0dffe8fa9 100644 --- a/Mage.Sets/src/mage/cards/e/Eliminate.java +++ b/Mage.Sets/src/mage/cards/e/Eliminate.java @@ -7,7 +7,7 @@ import mage.constants.ComparisonType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetPermanent; import java.util.UUID; @@ -18,11 +18,11 @@ public final class Eliminate extends CardImpl { private static final FilterPermanent filter = new FilterCreatureOrPlaneswalkerPermanent( - "creature or planeswalker with converted mana cost 3 or less" + "creature or planeswalker with mana value 3 or less" ); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public Eliminate(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/e/EliteArcanist.java b/Mage.Sets/src/mage/cards/e/EliteArcanist.java index e7e4b7d434a5..1bc1403f0225 100644 --- a/Mage.Sets/src/mage/cards/e/EliteArcanist.java +++ b/Mage.Sets/src/mage/cards/e/EliteArcanist.java @@ -73,7 +73,7 @@ public void adjustCosts(Ability ability, Game game) { if (imprintedInstant == null) { return; } - int cmc = imprintedInstant.getConvertedManaCost(); + int cmc = imprintedInstant.getManaValue(); if (cmc > 0) { ability.getManaCostsToPay().clear(); ability.getManaCostsToPay().add(new GenericManaCost(cmc)); @@ -132,7 +132,7 @@ class EliteArcanistCopyEffect extends OneShotEffect { public EliteArcanistCopyEffect() { super(Outcome.PlayForFree); this.staticText = "Copy the exiled card. You may cast the copy " - + "without paying its mana cost. X is the converted mana cost of the exiled card"; + + "without paying its mana cost. X is the mana value of the exiled card"; } public EliteArcanistCopyEffect(final EliteArcanistCopyEffect effect) { diff --git a/Mage.Sets/src/mage/cards/e/EliteSpellbinder.java b/Mage.Sets/src/mage/cards/e/EliteSpellbinder.java new file mode 100644 index 000000000000..b79bae430428 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EliteSpellbinder.java @@ -0,0 +1,162 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.keyword.FlyingAbility; +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.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInHand; +import mage.target.common.TargetOpponent; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EliteSpellbinder extends CardImpl { + + public EliteSpellbinder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Elite Spellbinder enters the battlefield, look at target opponent's hand. You may exile a nonland card from it. For as long as that card remains exiled, its owner may play it. A spell cast this way costs {2} more to cast. + Ability ability = new EntersBattlefieldTriggeredAbility(new EliteSpellbinderEffect()); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + } + + private EliteSpellbinder(final EliteSpellbinder card) { + super(card); + } + + @Override + public EliteSpellbinder copy() { + return new EliteSpellbinder(this); + } +} + +class EliteSpellbinderEffect extends OneShotEffect { + + EliteSpellbinderEffect() { + super(Outcome.Benefit); + staticText = "look at target opponent's hand. You may exile a nonland card from it. " + + "For as long as that card remains exiled, its owner may play it. " + + "A spell cast this way costs {2} more to cast"; + } + + private EliteSpellbinderEffect(final EliteSpellbinderEffect effect) { + super(effect); + } + + @Override + public EliteSpellbinderEffect copy() { + return new EliteSpellbinderEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player opponent = game.getPlayer(source.getFirstTarget()); + if (controller == null || opponent == null || opponent.getHand().isEmpty()) { + return false; + } + TargetCard target = new TargetCardInHand( + 0, 1, StaticFilters.FILTER_CARD_A_NON_LAND + ); + controller.choose(outcome, opponent.getHand(), target, game); + Card card = opponent.getHand().get(target.getFirstTarget(), game); + if (card == null) { + return false; + } + controller.moveCards(card, Zone.EXILED, source, game); + game.addEffect(new EliteSpellbinderCastEffect(card, game), source); + game.addEffect(new EliteSpellbinderCostEffect(card, game), source); + return true; + } +} + +class EliteSpellbinderCastEffect extends AsThoughEffectImpl { + + private final MageObjectReference mor; + + public EliteSpellbinderCastEffect(Card card, Game game) { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit); + this.mor = new MageObjectReference(card, game); + } + + private EliteSpellbinderCastEffect(final EliteSpellbinderCastEffect effect) { + super(effect); + this.mor = effect.mor; + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public EliteSpellbinderCastEffect copy() { + return new EliteSpellbinderCastEffect(this); + } + + @Override + public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + Card card = mor.getCard(game); + if (card == null) { + discard(); + return false; + } + return mor.refersTo(CardUtil.getMainCardId(game, sourceId), game) + && card.isOwnedBy(affectedControllerId); + } +} + +class EliteSpellbinderCostEffect extends CostModificationEffectImpl { + + private final MageObjectReference mor; + + EliteSpellbinderCostEffect(Card card, Game game) { + super(Duration.Custom, Outcome.Benefit, CostModificationType.INCREASE_COST); + mor = new MageObjectReference(card, game, 1); + } + + private EliteSpellbinderCostEffect(EliteSpellbinderCostEffect effect) { + super(effect); + this.mor = effect.mor; + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + CardUtil.increaseCost(abilityToModify, 2); + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + return mor.refersTo(CardUtil.getMainCardId(game, abilityToModify.getSourceId()), game); + } + + @Override + public EliteSpellbinderCostEffect copy() { + return new EliteSpellbinderCostEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/ElixirOfImmortality.java b/Mage.Sets/src/mage/cards/e/ElixirOfImmortality.java index b72f3dac3590..5812a370c44b 100644 --- a/Mage.Sets/src/mage/cards/e/ElixirOfImmortality.java +++ b/Mage.Sets/src/mage/cards/e/ElixirOfImmortality.java @@ -46,7 +46,7 @@ class ElixerOfImmortalityEffect extends OneShotEffect { public ElixerOfImmortalityEffect() { super(Outcome.GainLife); - staticText = "You gain 5 life. Shuffle {this} and your graveyard into your library"; + staticText = "You gain 5 life. Shuffle {this} and your graveyard into their owner's library"; } public ElixerOfImmortalityEffect(final ElixerOfImmortalityEffect effect) { diff --git a/Mage.Sets/src/mage/cards/e/ElshaOfTheInfinite.java b/Mage.Sets/src/mage/cards/e/ElshaOfTheInfinite.java index 5d4f9255c2d3..c400fc2d8cc5 100644 --- a/Mage.Sets/src/mage/cards/e/ElshaOfTheInfinite.java +++ b/Mage.Sets/src/mage/cards/e/ElshaOfTheInfinite.java @@ -16,6 +16,7 @@ import mage.filter.FilterCard; import mage.filter.common.FilterNonlandCard; import mage.filter.predicate.Predicates; +import mage.filter.predicate.card.CardOnTopOfLibraryPredicate; import java.util.UUID; @@ -28,6 +29,7 @@ public final class ElshaOfTheInfinite extends CardImpl { static { filter.add(Predicates.not(CardType.CREATURE.getPredicate())); + filter.add(CardOnTopOfLibraryPredicate.instance); } public ElshaOfTheInfinite(UUID ownerId, CardSetInfo setInfo) { @@ -45,8 +47,8 @@ public ElshaOfTheInfinite(UUID ownerId, CardSetInfo setInfo) { // You may look at the top card of your library any time. this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect())); - // You may cast the top card of your library if it's a noncreature, nonland card, and you may cast it as though it had flash. - Ability ability = new SimpleStaticAbility(new PlayTheTopCardEffect(filter)); + // You may cast noncreature spells from the top of your library. If you cast a spell this way, you may cast it as though it had flash. + Ability ability = new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false)); ability.addEffect(new CastAsThoughItHadFlashAllEffect( Duration.WhileOnBattlefield, filter ).setText("If you cast a spell this way, you may cast it as though it had flash.")); @@ -62,3 +64,4 @@ public ElshaOfTheInfinite copy() { return new ElshaOfTheInfinite(this); } } + diff --git a/Mage.Sets/src/mage/cards/e/ElspethConquersDeath.java b/Mage.Sets/src/mage/cards/e/ElspethConquersDeath.java index 83ddd3ce744b..ae9ac27fe749 100644 --- a/Mage.Sets/src/mage/cards/e/ElspethConquersDeath.java +++ b/Mage.Sets/src/mage/cards/e/ElspethConquersDeath.java @@ -16,7 +16,7 @@ import mage.filter.FilterCard; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -30,13 +30,13 @@ public final class ElspethConquersDeath extends CardImpl { private static final FilterPermanent filter - = new FilterPermanent("permanent an opponent controls with converted mana cost 3 or greater"); + = new FilterPermanent("permanent an opponent controls with mana value 3 or greater"); private static final FilterCard filter2 = new FilterCard("creature or planeswalker card from your graveyard"); static { filter.add(TargetController.OPPONENT.getControllerPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 2)); + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 2)); filter2.add(Predicates.or( CardType.CREATURE.getPredicate(), CardType.PLANESWALKER.getPredicate() diff --git a/Mage.Sets/src/mage/cards/e/ElvishSpiritGuide.java b/Mage.Sets/src/mage/cards/e/ElvishSpiritGuide.java index 49e93e312d97..c08d939ce608 100644 --- a/Mage.Sets/src/mage/cards/e/ElvishSpiritGuide.java +++ b/Mage.Sets/src/mage/cards/e/ElvishSpiritGuide.java @@ -1,4 +1,3 @@ - package mage.cards.e; import java.util.UUID; @@ -7,6 +6,7 @@ import mage.abilities.Ability; import mage.abilities.costs.Cost; import mage.abilities.costs.CostImpl; +import mage.abilities.costs.common.ExileSourceFromHandCost; import mage.abilities.mana.SimpleManaAbility; import mage.cards.Card; import mage.cards.CardImpl; @@ -44,40 +44,3 @@ public ElvishSpiritGuide copy() { return new ElvishSpiritGuide(this); } } - - -class ExileSourceFromHandCost extends CostImpl { - - public ExileSourceFromHandCost() { - this.text = "Exile {this} from your hand"; - } - - public ExileSourceFromHandCost(ExileSourceFromHandCost cost) { - super(cost); - } - - @Override - public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - Card card = game.getCard(source.getSourceId()); - Player player = game.getPlayer(controllerId); - if (player != null && player.getHand().contains(source.getSourceId()) && card != null) { - paid = card.moveToExile(ability.getSourceId(), "from Hand", source, game); - } - return paid; - } - - @Override - public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { - Player player = game.getPlayer(controllerId); - if (player != null && player.getHand().contains(source.getSourceId())) { - return true; - } - return false; - } - - @Override - public ExileSourceFromHandCost copy() { - return new ExileSourceFromHandCost(this); - } - -} diff --git a/Mage.Sets/src/mage/cards/e/ElvishVanguard.java b/Mage.Sets/src/mage/cards/e/ElvishVanguard.java index 27729eb07ce1..96cb511e301d 100644 --- a/Mage.Sets/src/mage/cards/e/ElvishVanguard.java +++ b/Mage.Sets/src/mage/cards/e/ElvishVanguard.java @@ -11,7 +11,7 @@ import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/e/ElvishWarmaster.java b/Mage.Sets/src/mage/cards/e/ElvishWarmaster.java index e83424af1294..547a38b67472 100644 --- a/Mage.Sets/src/mage/cards/e/ElvishWarmaster.java +++ b/Mage.Sets/src/mage/cards/e/ElvishWarmaster.java @@ -5,7 +5,6 @@ import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; @@ -17,11 +16,8 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.ElfWarriorToken; -import mage.util.CardUtil; import java.util.UUID; @@ -46,7 +42,9 @@ public ElvishWarmaster(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(2); // Whenever one or more other Elves enters the battlefield under your control, create a 1/1 green Elf Warrior creature token. This ability triggers only once each turn. - this.addAbility(new ElvishWarmasterTriggeredAbility(new CreateTokenEffect(new ElfWarriorToken()), filter)); + this.addAbility(new EntersBattlefieldControlledTriggeredAbility( + new CreateTokenEffect(new ElfWarriorToken()), filter + ).setTriggersOnce(true)); // {5}{G}{G}: Elves you control get +2/+2 and gain deathtouch until end of turn. Ability ability = new SimpleActivatedAbility(new BoostControlledEffect( @@ -67,42 +65,3 @@ public ElvishWarmaster copy() { return new ElvishWarmaster(this); } } - -class ElvishWarmasterTriggeredAbility extends EntersBattlefieldControlledTriggeredAbility { - - public ElvishWarmasterTriggeredAbility(Effect effect, FilterPermanent filter) { - super(effect, filter); - } - - public ElvishWarmasterTriggeredAbility(final ElvishWarmasterTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Integer lastTurnTriggered = (Integer) game.getState() - .getValue(CardUtil.getCardZoneString("lastTurnTriggered" + originalId, sourceId, game)); - if (lastTurnTriggered != null && lastTurnTriggered == game.getTurnNum()) { - return false; - } - return super.checkTrigger(event, game); - } - - @Override - public void trigger(Game game, UUID controllerId) { - game.getState().setValue(CardUtil - .getCardZoneString("lastTurnTriggered" + originalId, sourceId, game), game.getTurnNum()); - super.trigger(game, controllerId); - } - - @Override - public String getRule() { - return "Whenever one or more other Elves enter the battlefield under your control, " + - "create a 1/1 green Elf Warrior creature token. This ability triggers only once each turn."; - } - - @Override - public ElvishWarmasterTriggeredAbility copy() { - return new ElvishWarmasterTriggeredAbility(this); - } -} diff --git a/Mage.Sets/src/mage/cards/e/EmbalmersTools.java b/Mage.Sets/src/mage/cards/e/EmbalmersTools.java index d745bd7b71ce..80e45a47fca2 100644 --- a/Mage.Sets/src/mage/cards/e/EmbalmersTools.java +++ b/Mage.Sets/src/mage/cards/e/EmbalmersTools.java @@ -91,7 +91,7 @@ public boolean applies(Ability abilityToModify, Ability source, Game game) { || (abilityToModify.getAbilityType() == AbilityType.MANA && (abilityToModify instanceof ActivatedAbility))) { // Activated abilities of creatures Card card = game.getCard(abilityToModify.getSourceId()); - if (card != null && filter.match(card, source.getSourceId(), source.getControllerId(), game) && game.getState().getZone(card.getId()) == Zone.GRAVEYARD) { + if (filter.match(card, source.getSourceId(), source.getControllerId(), game) && game.getState().getZone(card.getId()) == Zone.GRAVEYARD) { return true; } } diff --git a/Mage.Sets/src/mage/cards/e/EmbermawHellion.java b/Mage.Sets/src/mage/cards/e/EmbermawHellion.java index d88a3e8c120e..58388c02b205 100644 --- a/Mage.Sets/src/mage/cards/e/EmbermawHellion.java +++ b/Mage.Sets/src/mage/cards/e/EmbermawHellion.java @@ -63,8 +63,7 @@ class EmbermawHellionEffect extends ReplacementEffectImpl { @Override public boolean checksEventType(GameEvent event, Game game) { switch(event.getType()) { - case DAMAGE_CREATURE: - case DAMAGE_PLANESWALKER: + case DAMAGE_PERMANENT: case DAMAGE_PLAYER: return true; default: diff --git a/Mage.Sets/src/mage/cards/e/EmberwildeCaliph.java b/Mage.Sets/src/mage/cards/e/EmberwildeCaliph.java index a5294051b973..075d90dae31c 100644 --- a/Mage.Sets/src/mage/cards/e/EmberwildeCaliph.java +++ b/Mage.Sets/src/mage/cards/e/EmberwildeCaliph.java @@ -72,9 +72,8 @@ public EmberwildeCaliphTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE - || event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT + || event.getType() == GameEvent.EventType.DAMAGED_PLAYER; } @Override diff --git a/Mage.Sets/src/mage/cards/e/EmbodimentOfAgonies.java b/Mage.Sets/src/mage/cards/e/EmbodimentOfAgonies.java index dec776690b9e..b52a0dc83a52 100644 --- a/Mage.Sets/src/mage/cards/e/EmbodimentOfAgonies.java +++ b/Mage.Sets/src/mage/cards/e/EmbodimentOfAgonies.java @@ -68,7 +68,7 @@ public int calculate(Game game, Ability sourceAbility, Effect effect) { .getCards(game) .stream() .filter(card -> !card.isLand()) - .forEach(card -> stringSet.add(getCosts(card.getManaCost()))); + .forEach(card -> stringSet.add(getCosts(card.getManaCostSymbols()))); stringSet.removeIf(s -> s == null || s.equals("")); return stringSet.size(); } @@ -83,11 +83,11 @@ public String getMessage() { return ""; } - private static String getCosts(ManaCosts costs) { + private static String getCosts(List manaCostSymbols) { List newList = new ArrayList(); int generic = 0; boolean hasGeneric = false; - for (String s : costs.getSymbols()) { + for (String s : manaCostSymbols) { if (s.matches("\\{\\d*\\}")) { generic += Integer.parseInt(s.substring(1, s.length() - 1)); hasGeneric = true; diff --git a/Mage.Sets/src/mage/cards/e/EmergencyPowers.java b/Mage.Sets/src/mage/cards/e/EmergencyPowers.java index e2495e3607e9..2ccf92d80eea 100644 --- a/Mage.Sets/src/mage/cards/e/EmergencyPowers.java +++ b/Mage.Sets/src/mage/cards/e/EmergencyPowers.java @@ -13,7 +13,7 @@ import mage.constants.ComparisonType; import mage.constants.Outcome; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import java.util.UUID; @@ -47,16 +47,16 @@ public EmergencyPowers copy() { class EmergencyPowersEffect extends OneShotEffect { public static final FilterPermanentCard filter - = new FilterPermanentCard("a permanent card with converted mana cost 7 or less"); + = new FilterPermanentCard("a permanent card with mana value 7 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 8)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 8)); } EmergencyPowersEffect() { super(Outcome.Benefit); staticText = "Exile {this}.
Addendum — If you cast this spell during your main phase, " + - "you may put a permanent card with converted mana cost 7 or less from your hand onto the battlefield."; + "you may put a permanent card with mana value 7 or less from your hand onto the battlefield."; } private EmergencyPowersEffect(final EmergencyPowersEffect effect) { @@ -73,7 +73,7 @@ public boolean apply(Game game, Ability source) { if (AddendumCondition.instance.apply(game, source)) { new PutCardFromHandOntoBattlefieldEffect(filter).apply(game, source); } - return ExileSpellEffect.getInstance().apply(game, source); + return new ExileSpellEffect().apply(game, source); } } // I am the senate! diff --git a/Mage.Sets/src/mage/cards/e/EmergentSequence.java b/Mage.Sets/src/mage/cards/e/EmergentSequence.java new file mode 100644 index 000000000000..d833ea5b3834 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EmergentSequence.java @@ -0,0 +1,138 @@ +package mage.cards.e; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect; +import mage.abilities.hint.Hint; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.QuandrixToken; +import mage.players.Player; +import mage.target.common.TargetCardInLibrary; +import mage.target.targetpointer.FixedTarget; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EmergentSequence extends CardImpl { + + public EmergentSequence(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}"); + + // Search your library for a basic land card, put it onto the battlefield tapped, then shuffle. That land becomes a 0/0 green and blue Fractal creature that's still a land. Put a +1/+1 counter on it for each land you had enter the battlefield under your control this turn. + this.getSpellAbility().addEffect(new EmergentSequenceEffect()); + this.getSpellAbility().addWatcher(new EmergentSequenceWatcher()); + this.getSpellAbility().addHint(EmergentSequenceHint.instance); + } + + private EmergentSequence(final EmergentSequence card) { + super(card); + } + + @Override + public EmergentSequence copy() { + return new EmergentSequence(this); + } +} + +class EmergentSequenceEffect extends OneShotEffect { + + EmergentSequenceEffect() { + super(Outcome.Benefit); + staticText = "search your library for a basic land card, put it onto the battlefield tapped, then shuffle. " + + "That land becomes a 0/0 green and blue Fractal creature that's still a land. " + + "Put a +1/+1 counter on it for each land you had enter the battlefield under your control this turn"; + } + + private EmergentSequenceEffect(final EmergentSequenceEffect effect) { + super(effect); + } + + @Override + public EmergentSequenceEffect copy() { + return new EmergentSequenceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + TargetCardInLibrary target = new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND_A); + player.searchLibrary(target, source, game); + Card card = player.getLibrary().getCard(target.getFirstTarget(), game); + Permanent permanent = null; + if (card != null) { + player.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null); + permanent = game.getPermanent(target.getFirstTarget()); + } + player.shuffleLibrary(source, game); + if (permanent == null) { + return true; + } + game.addEffect(new BecomesCreatureTargetEffect( + new QuandrixToken(), true, true, Duration.Custom + ).setTargetPointer(new FixedTarget(permanent, game)), source); + permanent.addCounters(CounterType.P1P1.createInstance( + EmergentSequenceWatcher.getAmount(source.getControllerId(), game) + ), source.getControllerId(), source, game); + return true; + } +} + +class EmergentSequenceWatcher extends Watcher { + + private final Map playerMap = new HashMap<>(); + + EmergentSequenceWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD + && ((EntersTheBattlefieldEvent) event).getTarget().isLand()) { + playerMap.compute(event.getPlayerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + } + } + + @Override + public void reset() { + super.reset(); + playerMap.clear(); + } + + static int getAmount(UUID playerId, Game game) { + EmergentSequenceWatcher watcher = game.getState().getWatcher(EmergentSequenceWatcher.class); + return watcher != null ? watcher.playerMap.getOrDefault(playerId, 0) : 0; + } +} + +enum EmergentSequenceHint implements Hint { + instance; + + @Override + public String getText(Game game, Ability ability) { + return "Lands entered under your control this turn: " + + EmergentSequenceWatcher.getAmount(ability.getControllerId(), game); + } + + @Override + public EmergentSequenceHint copy() { + return instance; + } +} diff --git a/Mage.Sets/src/mage/cards/e/EmergentUltimatum.java b/Mage.Sets/src/mage/cards/e/EmergentUltimatum.java index 5d5d980c496c..98a21bd5d909 100644 --- a/Mage.Sets/src/mage/cards/e/EmergentUltimatum.java +++ b/Mage.Sets/src/mage/cards/e/EmergentUltimatum.java @@ -30,7 +30,7 @@ public EmergentUltimatum(UUID ownerId, CardSetInfo setInfo) { // Search your library for up to three monocolored cards with different names and exile them. An opponent chooses one of those cards. Shuffle that card into your library. You may cast the other cards without paying their mana costs. Exile Emergent Ultimatum. this.getSpellAbility().addEffect(new EmergentUltimatumEffect()); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private EmergentUltimatum(final EmergentUltimatum card) { @@ -68,6 +68,7 @@ public boolean apply(Game game, Ability source) { return false; } TargetCardInLibrary targetCardInLibrary = new EmergentUltimatumTarget(); + targetCardInLibrary.setNotTarget(true); boolean searched = player.searchLibrary(targetCardInLibrary, source, game); Cards cards = new CardsImpl(targetCardInLibrary.getTargets()); player.moveCards(cards, Zone.EXILED, source, game); @@ -85,9 +86,10 @@ public boolean apply(Game game, Ability source) { if (searched) { player.shuffleLibrary(source, game); } - return true; + return false; } TargetCardInExile targetCardInExile = new TargetCardInExile(StaticFilters.FILTER_CARD); + targetCardInExile.setNotTarget(true); opponent.choose(outcome, cards, targetCardInExile, game); Card toShuffle = game.getCard(targetCardInExile.getFirstTarget()); if (toShuffle != null) { diff --git a/Mage.Sets/src/mage/cards/e/EmielTheBlessed.java b/Mage.Sets/src/mage/cards/e/EmielTheBlessed.java index 875a264fee37..d60d00eead78 100644 --- a/Mage.Sets/src/mage/cards/e/EmielTheBlessed.java +++ b/Mage.Sets/src/mage/cards/e/EmielTheBlessed.java @@ -17,7 +17,7 @@ import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; diff --git a/Mage.Sets/src/mage/cards/e/EmissaryOfGrudges.java b/Mage.Sets/src/mage/cards/e/EmissaryOfGrudges.java index 36d5d9fc8df6..80d2d256f427 100644 --- a/Mage.Sets/src/mage/cards/e/EmissaryOfGrudges.java +++ b/Mage.Sets/src/mage/cards/e/EmissaryOfGrudges.java @@ -3,10 +3,9 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.Mode; +import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.RevealSecretOpponentCost; -import mage.abilities.effects.EntersBattlefieldEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ChooseSecretOpponentEffect; import mage.abilities.keyword.FlyingAbility; @@ -16,7 +15,6 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.StackObject; @@ -44,10 +42,11 @@ public EmissaryOfGrudges(UUID ownerId, CardSetInfo setInfo) { this.addAbility(HasteAbility.getInstance()); // As Emissary of Grudges enters the battlefield, secretly choose an opponent. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new EntersBattlefieldEffect(new ChooseSecretOpponentEffect(), "As {this} enters the battlefield, secretly choose an opponent."))); + this.addAbility(new AsEntersBattlefieldAbility(new ChooseSecretOpponentEffect())); + // Choose new targets for target spell or ability if it’s controlled by the chosen player and if it targets you // or a permanent you control. Activate this ability only once. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new EmissaryOfGrudgesEffect(), new RevealSecretOpponentCost()); + Ability ability = new SimpleActivatedAbility(new EmissaryOfGrudgesEffect(), new RevealSecretOpponentCost()); ability.addTarget(new TargetStackObject()); this.addAbility(ability); } @@ -67,7 +66,7 @@ class EmissaryOfGrudgesEffect extends OneShotEffect { public EmissaryOfGrudgesEffect() { super(Outcome.Neutral); this.staticText = "Choose new targets for target spell or ability if it's controlled by the chosen player and" + - " if it targets you or a permanent you control. Activate this ability only once."; + " if it targets you or a permanent you control. Activate only once."; } public EmissaryOfGrudgesEffect(final EmissaryOfGrudgesEffect effect) { @@ -82,34 +81,30 @@ public EmissaryOfGrudgesEffect copy() { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - StackObject stackObject = game.getStack().getStackObject(source.getFirstTarget()); - if (stackObject != null) { - UUID opponentId = (UUID) game.getState().getValue(source.getSourceId() + ChooseSecretOpponentEffect.SECRET_OPPONENT); - if (opponentId != null && opponentId.equals(stackObject.getControllerId())) { - // find if it targets you or a permanent you control - boolean targetsYouOrAPermanentYouControl = false; - for (UUID modeId : stackObject.getStackAbility().getModes().getSelectedModes()) { - Mode mode = stackObject.getStackAbility().getModes().get(modeId); - for (Target target : mode.getTargets()) { - for (UUID targetId : target.getTargets()) { - if (source.isControlledBy(targetId)) { - targetsYouOrAPermanentYouControl = true; - } - Permanent permanent = game.getPermanent(targetId); - if (permanent != null && source.isControlledBy(permanent.getControllerId())) { - targetsYouOrAPermanentYouControl = true; - } - } - } + StackObject stackObject = game.getStack().getStackObject(source.getFirstTarget()); + if (controller == null || stackObject == null + || !stackObject.isControlledBy(ChooseSecretOpponentEffect.getChosenPlayer(source, game))) { + return false; + } + // find if it targets you or a permanent you control + boolean targetsYouOrAPermanentYouControl = false; + for (UUID modeId : stackObject.getStackAbility().getModes().getSelectedModes()) { + Mode mode = stackObject.getStackAbility().getModes().get(modeId); + for (Target target : mode.getTargets()) { + for (UUID targetId : target.getTargets()) { + if (source.isControlledBy(targetId)) { + targetsYouOrAPermanentYouControl = true; } - if (targetsYouOrAPermanentYouControl) { - return stackObject.chooseNewTargets(game, source.getControllerId(), false, false, null); + Permanent permanent = game.getPermanent(targetId); + if (permanent != null && source.isControlledBy(permanent.getControllerId())) { + targetsYouOrAPermanentYouControl = true; } } } - return true; } - return false; + if (targetsYouOrAPermanentYouControl) { + return stackObject.chooseNewTargets(game, source.getControllerId(), false, false, null); + } + return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/e/EmptyTheWarrens.java b/Mage.Sets/src/mage/cards/e/EmptyTheWarrens.java index a6ce63653841..bd11a8a6cd91 100644 --- a/Mage.Sets/src/mage/cards/e/EmptyTheWarrens.java +++ b/Mage.Sets/src/mage/cards/e/EmptyTheWarrens.java @@ -1,4 +1,3 @@ - package mage.cards.e; import java.util.UUID; @@ -20,6 +19,7 @@ public EmptyTheWarrens(UUID ownerId, CardSetInfo setInfo) { // Create two 1/1 red Goblin creature tokens. this.getSpellAbility().addEffect(new CreateTokenEffect(new GoblinToken(), 2)); + // Storm this.addAbility(new StormAbility()); } diff --git a/Mage.Sets/src/mage/cards/e/EmrakulThePromisedEnd.java b/Mage.Sets/src/mage/cards/e/EmrakulThePromisedEnd.java index d84e9f8879f6..734e8ad815b0 100644 --- a/Mage.Sets/src/mage/cards/e/EmrakulThePromisedEnd.java +++ b/Mage.Sets/src/mage/cards/e/EmrakulThePromisedEnd.java @@ -79,7 +79,7 @@ class EmrakulThePromisedEndCostReductionEffect extends CostModificationEffectImp EmrakulThePromisedEndCostReductionEffect() { super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "{this} costs {1} less to cast for each card type among cards in your graveyard"; + staticText = "this spell costs {1} less to cast for each card type among cards in your graveyard"; } EmrakulThePromisedEndCostReductionEffect(EmrakulThePromisedEndCostReductionEffect effect) { diff --git a/Mage.Sets/src/mage/cards/e/EmrakulsEvangel.java b/Mage.Sets/src/mage/cards/e/EmrakulsEvangel.java index 55070b581ac9..c5b0170ac0a3 100644 --- a/Mage.Sets/src/mage/cards/e/EmrakulsEvangel.java +++ b/Mage.Sets/src/mage/cards/e/EmrakulsEvangel.java @@ -17,7 +17,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.EldraziHorrorToken; diff --git a/Mage.Sets/src/mage/cards/e/EmrakulsInfluence.java b/Mage.Sets/src/mage/cards/e/EmrakulsInfluence.java index 368c45f9c985..d0434d953e30 100644 --- a/Mage.Sets/src/mage/cards/e/EmrakulsInfluence.java +++ b/Mage.Sets/src/mage/cards/e/EmrakulsInfluence.java @@ -10,7 +10,7 @@ import mage.constants.ComparisonType; import mage.constants.SubType; import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; /** * @@ -18,11 +18,11 @@ */ public final class EmrakulsInfluence extends CardImpl { - private static final FilterSpell filterSpell = new FilterSpell("Eldrazi creature spell with converted mana cost 7 or greater"); + private static final FilterSpell filterSpell = new FilterSpell("Eldrazi creature spell with mana value 7 or greater"); static { filterSpell.add(SubType.ELDRAZI.getPredicate()); - filterSpell.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 6)); + filterSpell.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 6)); } public EmrakulsInfluence(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/e/EnchantersBane.java b/Mage.Sets/src/mage/cards/e/EnchantersBane.java index 90c164185dea..186c75801c3a 100644 --- a/Mage.Sets/src/mage/cards/e/EnchantersBane.java +++ b/Mage.Sets/src/mage/cards/e/EnchantersBane.java @@ -46,7 +46,7 @@ class EnchantersBaneEffect extends OneShotEffect { public EnchantersBaneEffect() { super(Outcome.Benefit); this.staticText = "target enchantment deals damage equal to " - + "its converted mana cost to its controller " + + "its mana value to its controller " + "unless that player sacrifices it"; } @@ -72,7 +72,7 @@ public boolean apply(Game game, Ability source) { if (player.chooseUse(Outcome.GainLife, "Sacrifice " + permanent.getLogName() + "?", source, game)) { permanent.sacrifice(source, game); } else { - player.damage(permanent.getConvertedManaCost(), permanent.getId(), source, game); + player.damage(permanent.getManaValue(), permanent.getId(), source, game); } return true; } diff --git a/Mage.Sets/src/mage/cards/e/EnchantmentAlteration.java b/Mage.Sets/src/mage/cards/e/EnchantmentAlteration.java index da7be3c8cdc3..db6b59c9e96d 100644 --- a/Mage.Sets/src/mage/cards/e/EnchantmentAlteration.java +++ b/Mage.Sets/src/mage/cards/e/EnchantmentAlteration.java @@ -13,7 +13,7 @@ import mage.filter.predicate.ObjectSourcePlayer; import mage.filter.predicate.ObjectSourcePlayerPredicate; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.AttachmentAttachedToCardTypePredicate; +import mage.filter.predicate.permanent.AttachmentAttachedToCardTypePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.StackObject; diff --git a/Mage.Sets/src/mage/cards/e/EndemicPlague.java b/Mage.Sets/src/mage/cards/e/EndemicPlague.java index 1b541013b054..417fdb27acc6 100644 --- a/Mage.Sets/src/mage/cards/e/EndemicPlague.java +++ b/Mage.Sets/src/mage/cards/e/EndemicPlague.java @@ -10,7 +10,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.SharesCreatureTypePredicate; +import mage.filter.predicate.mageobject.SharesCreatureTypePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/e/EndlessHorizons.java b/Mage.Sets/src/mage/cards/e/EndlessHorizons.java index 3dd419e8695e..faee405ca900 100644 --- a/Mage.Sets/src/mage/cards/e/EndlessHorizons.java +++ b/Mage.Sets/src/mage/cards/e/EndlessHorizons.java @@ -1,40 +1,36 @@ - package mage.cards.e; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.SearchEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; +import mage.cards.*; import mage.constants.*; import mage.filter.FilterCard; -import mage.filter.common.FilterLandCard; -import mage.game.ExileZone; 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.TargetCardInLibrary; import mage.util.CardUtil; +import java.util.Objects; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class EndlessHorizons extends CardImpl { public EndlessHorizons(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}"); // When Endless Horizons enters the battlefield, search your library for any number of Plains cards and exile them. Then shuffle your library. this.addAbility(new EntersBattlefieldTriggeredAbility(new EndlessHorizonsEffect(), false)); // At the beginning of your upkeep, you may put a card you own exiled with Endless Horizons into your hand. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new EndlessHorizonsEffect2(), TargetController.YOU, true)); - + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new EndlessHorizonsEffect2(), TargetController.YOU, false)); } private EndlessHorizons(final EndlessHorizons card) { @@ -47,20 +43,20 @@ public EndlessHorizons copy() { } } -class EndlessHorizonsEffect extends SearchEffect { +class EndlessHorizonsEffect extends OneShotEffect { - private static final FilterLandCard filter = new FilterLandCard("Plains card"); + private static final FilterCard filter = new FilterCard("Plains card"); static { filter.add(SubType.PLAINS.getPredicate()); } - public EndlessHorizonsEffect() { - super(new TargetCardInLibrary(0, Integer.MAX_VALUE, filter), Outcome.Neutral); - this.staticText = "search your library for any number of Plains cards and exile them. Then shuffle your library"; + EndlessHorizonsEffect() { + super(Outcome.Neutral); + this.staticText = "search your library for any number of Plains cards and exile them. Then shuffle"; } - public EndlessHorizonsEffect(final EndlessHorizonsEffect effect) { + private EndlessHorizonsEffect(final EndlessHorizonsEffect effect) { super(effect); } @@ -71,35 +67,39 @@ public EndlessHorizonsEffect copy() { @Override public boolean apply(Game game, Ability source) { - Player you = game.getPlayer(source.getControllerId()); - if (you != null) { - if (you.searchLibrary(target, source, game)) { - UUID exileZone = CardUtil.getCardExileZoneId(game, source); - if (!target.getTargets().isEmpty()) { - for (UUID cardId : target.getTargets()) { - Card card = you.getLibrary().getCard(cardId, game); - if (card != null) { - card.moveToExile(exileZone, "Endless Horizons", source, game); - } - } - } - } - you.shuffleLibrary(source, game); - return true; - + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = source.getSourcePermanentOrLKI(game); + if (permanent == null || player == null) { + return false; } - return false; + TargetCardInLibrary target = new TargetCardInLibrary(0, Integer.MAX_VALUE, filter); + player.searchLibrary(target, source, game); + Cards cards = new CardsImpl(); + target.getTargets() + .stream() + .map(uuid -> player.getLibrary().getCard(uuid, game)) + .filter(Objects::nonNull) + .forEach(cards::add); + player.moveCardsToExile(cards.getCards(game), source, game, true, CardUtil.getExileZoneId(game, source), permanent.getIdName()); + player.shuffleLibrary(source, game); + return true; } } class EndlessHorizonsEffect2 extends OneShotEffect { - public EndlessHorizonsEffect2() { + private static final FilterCard filter = new FilterCard(); + + static { + filter.add(TargetController.YOU.getOwnerPredicate()); + } + + EndlessHorizonsEffect2() { super(Outcome.ReturnToHand); this.staticText = "you may put a card you own exiled with {this} into your hand"; } - public EndlessHorizonsEffect2(final EndlessHorizonsEffect2 effect) { + private EndlessHorizonsEffect2(final EndlessHorizonsEffect2 effect) { super(effect); } @@ -111,22 +111,15 @@ public EndlessHorizonsEffect2 copy() { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - ExileZone exZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); - if (exZone != null) { - Card card = null; - if (exZone.size() > 1) { - TargetCard target = new TargetCard(Zone.EXILED, new FilterCard()); - controller.choose(outcome, exZone, target, game); - card = game.getCard(target.getFirstTarget()); - } else { - card = exZone.getRandom(game); - } - controller.moveCards(card, Zone.HAND, source, game); - } - return true; + if (controller == null) { + return false; } - return false; + TargetCard target = new TargetCardInExile( + 0, 1, filter, CardUtil.getExileZoneId(game, source) + ); + target.setNotTarget(true); + controller.choose(outcome, target, source.getSourceId(), game); + Card card = game.getCard(target.getFirstTarget()); + return card != null && controller.moveCards(card, Zone.HAND, source, game); } - } diff --git a/Mage.Sets/src/mage/cards/e/EndrekSahrMasterBreeder.java b/Mage.Sets/src/mage/cards/e/EndrekSahrMasterBreeder.java index 3ae6c7784a73..09b3eb84f016 100644 --- a/Mage.Sets/src/mage/cards/e/EndrekSahrMasterBreeder.java +++ b/Mage.Sets/src/mage/cards/e/EndrekSahrMasterBreeder.java @@ -54,7 +54,7 @@ class EndrekSahrMasterBreederEffect extends OneShotEffect { public EndrekSahrMasterBreederEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "create X 1/1 black Thrull creature tokens, where X is that spell's converted mana cost"; + this.staticText = "create X 1/1 black Thrull creature tokens, where X is that spell's mana value"; } public EndrekSahrMasterBreederEffect(final EndrekSahrMasterBreederEffect effect) { @@ -70,7 +70,7 @@ public EndrekSahrMasterBreederEffect copy() { public boolean apply(Game game, Ability source) { Spell spell = game.getSpellOrLKIStack(this.getTargetPointer().getFirst(game, source)); if (spell != null) { - int cmc = spell.getConvertedManaCost(); + int cmc = spell.getManaValue(); if (cmc > 0) { return new CreateTokenEffect(new ThrullToken(), cmc).apply(game, source); } diff --git a/Mage.Sets/src/mage/cards/e/EnduringIdeal.java b/Mage.Sets/src/mage/cards/e/EnduringIdeal.java index 39ebe7e2d40c..cb0024130c97 100644 --- a/Mage.Sets/src/mage/cards/e/EnduringIdeal.java +++ b/Mage.Sets/src/mage/cards/e/EnduringIdeal.java @@ -47,7 +47,7 @@ class EnduringIdealEffect extends OneShotEffect { public EnduringIdealEffect() { super(Outcome.Benefit); - staticText = "Search your library for an enchantment card and put it onto the battlefield. Then shuffle your library"; + staticText = "Search your library for an enchantment card, put it onto the battlefield, then shuffle"; } public EnduringIdealEffect(final EnduringIdealEffect effect) { diff --git a/Mage.Sets/src/mage/cards/e/EnduringRenewal.java b/Mage.Sets/src/mage/cards/e/EnduringRenewal.java index 82689d481855..a9eb672aed8b 100644 --- a/Mage.Sets/src/mage/cards/e/EnduringRenewal.java +++ b/Mage.Sets/src/mage/cards/e/EnduringRenewal.java @@ -1,25 +1,21 @@ package mage.cards.e; -import java.util.UUID; - -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.PutIntoGraveFromBattlefieldAllTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ContinuousEffectImpl; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.effects.common.continuous.PlayWithHandRevealedEffect; import mage.cards.*; import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** * @author anonymous */ @@ -31,15 +27,16 @@ public EnduringRenewal(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}{W}"); // Play with your hand revealed. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayWithHandRevealedEffect())); + this.addAbility(new SimpleStaticAbility(new PlayWithHandRevealedEffect(TargetController.YOU))); // If you would draw a card, reveal the top card of your library instead. If it's a creature card, put it into your graveyard. Otherwise, draw a card. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new EnduringRenewalReplacementEffect())); + this.addAbility(new SimpleStaticAbility(new EnduringRenewalReplacementEffect())); // Whenever a creature is put into your graveyard from the battlefield, return it to your hand. - Effect effect = new ReturnFromGraveyardToHandTargetEffect(); - effect.setText("return it to your hand"); - this.addAbility(new PutIntoGraveFromBattlefieldAllTriggeredAbility(effect, false, filter, true, true)); + this.addAbility(new PutIntoGraveFromBattlefieldAllTriggeredAbility( + new ReturnFromGraveyardToHandTargetEffect().setText("return it to your hand"), + false, filter, true, true + )); } private EnduringRenewal(final EnduringRenewal card) { @@ -54,12 +51,13 @@ public EnduringRenewal copy() { class EnduringRenewalReplacementEffect extends ReplacementEffectImpl { - public EnduringRenewalReplacementEffect() { + EnduringRenewalReplacementEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "If you would draw a card, reveal the top card of your library instead. If it's a creature card, put it into your graveyard. Otherwise, draw a card"; + staticText = "If you would draw a card, reveal the top card of your library instead. " + + "If it's a creature card, put it into your graveyard. Otherwise, draw a card"; } - public EnduringRenewalReplacementEffect(final EnduringRenewalReplacementEffect effect) { + private EnduringRenewalReplacementEffect(final EnduringRenewalReplacementEffect effect) { super(effect); } @@ -80,18 +78,18 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { return false; } Card card = controller.getLibrary().getFromTop(game); - if (card != null) { - Cards cards = new CardsImpl(card); - controller.revealCards("Top card of " + controller.getName() + "'s library", cards, game); - if (card.isCreature()) { - controller.moveCards(card, Zone.GRAVEYARD, source, game); - } else { - // This is still replacing the draw, so we still return true - controller.drawCards(1, source, game, event); - } - return true; + if (card == null) { + return false; } - return false; + Cards cards = new CardsImpl(card); + controller.revealCards("Top card of " + controller.getName() + "'s library", cards, game); + if (card.isCreature()) { + controller.moveCards(card, Zone.GRAVEYARD, source, game); + } else { + // This is still replacing the draw, so we still return true + controller.drawCards(1, source, game, event); + } + return true; } @Override @@ -104,62 +102,3 @@ public boolean applies(GameEvent event, Ability source, Game game) { return event.getPlayerId().equals(source.getControllerId()); } } - -class PlayWithHandRevealedEffect extends ContinuousEffectImpl { - - public PlayWithHandRevealedEffect() { - super(Duration.WhileOnBattlefield, Layer.PlayerEffects, SubLayer.NA, Outcome.Detriment); - staticText = "Play with your hand revealed"; - } - - public PlayWithHandRevealedEffect(final PlayWithHandRevealedEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (controller != null && sourceObject != null) { - controller.revealCards(controller.getName(), controller.getHand(), game, false); - return true; - } - return false; - } - - @Override - public PlayWithHandRevealedEffect copy() { - return new PlayWithHandRevealedEffect(this); - } -} - -class EnduringRenewalEffect extends OneShotEffect { - - public EnduringRenewalEffect() { - super(Outcome.ReturnToHand); - staticText = "return it to your hand"; - } - - public EnduringRenewalEffect(final EnduringRenewalEffect effect) { - super(effect); - } - - @Override - public EnduringRenewalEffect copy() { - return new EnduringRenewalEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - UUID creatureId = (UUID) getValue("returningCreature"); - Permanent creature = game.getPermanent(creatureId); - if (creature != null) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - creature.moveToZone(Zone.HAND, source, game, false); - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/e/EnergyTap.java b/Mage.Sets/src/mage/cards/e/EnergyTap.java index 661f41229f1a..45bba2076fc9 100644 --- a/Mage.Sets/src/mage/cards/e/EnergyTap.java +++ b/Mage.Sets/src/mage/cards/e/EnergyTap.java @@ -50,7 +50,7 @@ class EnergyTapEffect extends OneShotEffect { EnergyTapEffect() { super(Outcome.PutManaInPool); - this.staticText = "Tap target untapped creature you control. If you do, add an amount of {C} equal to that creature's converted mana cost"; + this.staticText = "Tap target untapped creature you control. If you do, add an amount of {C} equal to that creature's mana value"; } EnergyTapEffect(final EnergyTapEffect effect) { @@ -74,7 +74,7 @@ public boolean apply(Game game, Ability source) { if (targetCreature != null) { applied = targetCreature.tap(source, game); if (applied) { - player.getManaPool().addMana(new Mana(0, 0, 0, 0, 0, 0, 0, targetCreature.getConvertedManaCost()), game, source); + player.getManaPool().addMana(new Mana(0, 0, 0, 0, 0, 0, 0, targetCreature.getManaValue()), game, source); } } return applied; diff --git a/Mage.Sets/src/mage/cards/e/EnergyVortex.java b/Mage.Sets/src/mage/cards/e/EnergyVortex.java index 986f4a45ac37..a6564329959c 100644 --- a/Mage.Sets/src/mage/cards/e/EnergyVortex.java +++ b/Mage.Sets/src/mage/cards/e/EnergyVortex.java @@ -38,7 +38,9 @@ public EnergyVortex(UUID ownerId, CardSetInfo setInfo) { // At the beginning of your upkeep, remove all vortex counters from Energy Vortex. this.addAbility(new BeginningOfUpkeepTriggeredAbility( - new RemoveAllCountersSourceEffect(CounterType.VORTEX), TargetController.YOU, false + new RemoveAllCountersSourceEffect(CounterType.VORTEX) + .setText("remove all vortex counters from {this}"), + TargetController.YOU, false )); // At the beginning of the chosen player's upkeep, Energy Vortex deals 3 damage to that player unless they pay {1} for each vortex counter on Energy Vortex. diff --git a/Mage.Sets/src/mage/cards/e/EngineeredExplosives.java b/Mage.Sets/src/mage/cards/e/EngineeredExplosives.java index 7cb4f6f765c3..1318712c0ae0 100644 --- a/Mage.Sets/src/mage/cards/e/EngineeredExplosives.java +++ b/Mage.Sets/src/mage/cards/e/EngineeredExplosives.java @@ -54,7 +54,7 @@ class EngineeredExplosivesEffect extends OneShotEffect { public EngineeredExplosivesEffect() { super(Outcome.DestroyPermanent); - staticText = "Destroy each nonland permanent with converted mana cost equal to the number of charge counters on Engineered Explosives"; + staticText = "Destroy each nonland permanent with mana value equal to the number of charge counters on Engineered Explosives"; } @@ -73,7 +73,7 @@ public boolean apply(Game game, Ability source) { if(engineeredExplosives instanceof Permanent){ int count = ((Permanent)engineeredExplosives).getCounters(game).getCount(CounterType.CHARGE); for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - if(permanent.getConvertedManaCost() == count){ + if(permanent.getManaValue() == count){ permanent.destroy(source, game, false); } } diff --git a/Mage.Sets/src/mage/cards/e/EnigmaSphinx.java b/Mage.Sets/src/mage/cards/e/EnigmaSphinx.java index 3c9cc367f714..36d326b3748c 100644 --- a/Mage.Sets/src/mage/cards/e/EnigmaSphinx.java +++ b/Mage.Sets/src/mage/cards/e/EnigmaSphinx.java @@ -84,9 +84,7 @@ public boolean checkEventType(GameEvent event, Game game) { public boolean checkTrigger(GameEvent event, Game game) { ZoneChangeEvent zEvent = (ZoneChangeEvent) event; Permanent permanent = zEvent.getTarget(); - if (permanent != null - && zEvent.getToZone() == Zone.GRAVEYARD - && zEvent.getFromZone() == Zone.BATTLEFIELD + if (permanent != null && zEvent.isDiesEvent() && permanent.getId().equals(this.getSourceId()) && // 5/1/2009 If you control an Enigma Sphinx that's owned by another player, it's put into that player's // graveyard from the battlefield, so Enigma Sphinx's middle ability won't trigger. diff --git a/Mage.Sets/src/mage/cards/e/EnigmaticIncarnation.java b/Mage.Sets/src/mage/cards/e/EnigmaticIncarnation.java index 04ef30ed867e..2c796bd05563 100644 --- a/Mage.Sets/src/mage/cards/e/EnigmaticIncarnation.java +++ b/Mage.Sets/src/mage/cards/e/EnigmaticIncarnation.java @@ -15,8 +15,8 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledEnchantmentPermanent; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -59,9 +59,9 @@ class EnigmaticIncarnationEffect extends OneShotEffect { EnigmaticIncarnationEffect() { super(Outcome.Benefit); staticText = "you may sacrifice another enchantment. If you do, " - + "search your library for a creature card with converted mana cost " - + "equal to 1 plus the sacrificed enchantment's converted mana cost, " - + "put that card onto the battlefield, then shuffle your library."; + + "search your library for a creature card with mana value " + + "equal to 1 plus the sacrificed enchantment's mana value, " + + "put that card onto the battlefield, then shuffle."; } private EnigmaticIncarnationEffect(final EnigmaticIncarnationEffect effect) { @@ -88,12 +88,12 @@ public boolean apply(Game game, Ability source) { return false; } game.getState().processAction(game); - int cmc = permanent.getConvertedManaCost(); + int cmc = permanent.getManaValue(); if (!permanent.sacrifice(source, game)) { return false; } - FilterCard filterCard = new FilterCreatureCard("creature card with converted mana cost " + (cmc + 1)); - filterCard.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, cmc + 1)); + FilterCard filterCard = new FilterCreatureCard("creature card with mana value " + (cmc + 1)); + filterCard.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, cmc + 1)); return new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filterCard)).apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/e/EnslavedHorror.java b/Mage.Sets/src/mage/cards/e/EnslavedHorror.java index 189014f231a4..1048f609aafe 100644 --- a/Mage.Sets/src/mage/cards/e/EnslavedHorror.java +++ b/Mage.Sets/src/mage/cards/e/EnslavedHorror.java @@ -15,7 +15,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInGraveyard; diff --git a/Mage.Sets/src/mage/cards/e/EntanglingTrap.java b/Mage.Sets/src/mage/cards/e/EntanglingTrap.java index 6a8ef4e7b2be..cad90ab7d73e 100644 --- a/Mage.Sets/src/mage/cards/e/EntanglingTrap.java +++ b/Mage.Sets/src/mage/cards/e/EntanglingTrap.java @@ -1,44 +1,28 @@ - package mage.cards.e; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; import mage.abilities.effects.common.TapTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class EntanglingTrap extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public EntanglingTrap(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); // Whenever you clash, tap target creature an opponent controls. If you won, that creature doesn't untap during its controller's next untap step. - Ability ability = new EntanglingTrapTriggeredAbility(); - ability.addTarget(new TargetCreaturePermanent(filter)); - this.addAbility(ability); + this.addAbility(new EntanglingTrapTriggeredAbility()); } private EntanglingTrap(final EntanglingTrap card) { @@ -53,11 +37,12 @@ public EntanglingTrap copy() { class EntanglingTrapTriggeredAbility extends TriggeredAbilityImpl { - public EntanglingTrapTriggeredAbility() { - super(Zone.BATTLEFIELD, new TapTargetEffect()); + EntanglingTrapTriggeredAbility() { + super(Zone.BATTLEFIELD, null); + this.addTarget(new TargetOpponentsCreaturePermanent()); } - public EntanglingTrapTriggeredAbility(final EntanglingTrapTriggeredAbility ability) { + private EntanglingTrapTriggeredAbility(final EntanglingTrapTriggeredAbility ability) { super(ability); } @@ -73,28 +58,20 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { - //remove effects from previous triggers - List effects = getEffects(); - List effectsToRemove = new ArrayList<>(); - for (Effect effect : effects) { - if (effect instanceof DontUntapInControllersNextUntapStepTargetEffect) { - effectsToRemove.add(effect); - } + if (!isControlledBy(event.getPlayerId())) { + return false; } - for (Effect effect : effectsToRemove) { - effects.remove(effect); + this.getEffects().clear(); + this.addEffect(new TapTargetEffect()); + if (event.getFlag()) { + this.addEffect(new DontUntapInControllersNextUntapStepTargetEffect()); } - - if (event.getData().equals("controller") && event.getPlayerId().equals(getControllerId()) - || event.getData().equals("opponent") && event.getTargetId().equals(getControllerId())) { - addEffect(new DontUntapInControllersNextUntapStepTargetEffect()); - } - return true; } @Override public String getRule() { - return "Whenever you clash, tap target creature an opponent controls. If you won, that creature doesn't untap during its controller's next untap step."; + return "Whenever you clash, tap target creature an opponent controls. " + + "If you won, that creature doesn't untap during its controller's next untap step."; } } diff --git a/Mage.Sets/src/mage/cards/e/EnthusiasticStudy.java b/Mage.Sets/src/mage/cards/e/EnthusiasticStudy.java new file mode 100644 index 000000000000..6a283b1a70da --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EnthusiasticStudy.java @@ -0,0 +1,44 @@ +package mage.cards.e; + +import mage.abilities.effects.common.LearnEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EnthusiasticStudy extends CardImpl { + + public EnthusiasticStudy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); + + // Target creature gets +3/+1 and gains trample until end of turn. + this.getSpellAbility().addEffect(new BoostTargetEffect( + 3, 1 + ).setText("target creature gets +3/+1")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn + ).setText("and gains trample until end of turn")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + + // Learn. + this.getSpellAbility().addEffect(new LearnEffect().concatBy("
")); + } + + private EnthusiasticStudy(final EnthusiasticStudy card) { + super(card); + } + + @Override + public EnthusiasticStudy copy() { + return new EnthusiasticStudy(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/Entomb.java b/Mage.Sets/src/mage/cards/e/Entomb.java index 398885d2c7bb..e9bbd6ed856c 100644 --- a/Mage.Sets/src/mage/cards/e/Entomb.java +++ b/Mage.Sets/src/mage/cards/e/Entomb.java @@ -42,7 +42,7 @@ class SearchLibraryPutInGraveyard extends SearchEffect { public SearchLibraryPutInGraveyard() { super(new TargetCardInLibrary(new FilterCard()), Outcome.Neutral); - staticText = "Search your library for a card and put that card into your graveyard. Then shuffle your library"; + staticText = "Search your library for a card and put that card into your graveyard, then shuffle"; } public SearchLibraryPutInGraveyard(final SearchLibraryPutInGraveyard effect) { diff --git a/Mage.Sets/src/mage/cards/e/EntrancingMelody.java b/Mage.Sets/src/mage/cards/e/EntrancingMelody.java index 8f93beeae723..63c8c80eebd6 100644 --- a/Mage.Sets/src/mage/cards/e/EntrancingMelody.java +++ b/Mage.Sets/src/mage/cards/e/EntrancingMelody.java @@ -9,7 +9,7 @@ import mage.constants.ComparisonType; import mage.constants.Duration; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.target.common.TargetCreaturePermanent; import mage.target.targetadjustment.TargetAdjuster; @@ -21,7 +21,7 @@ */ public final class EntrancingMelody extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with converted mana cost X"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with mana value X"); public EntrancingMelody(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{U}{U}"); @@ -49,8 +49,8 @@ enum EntrancingMelodyAdjuster implements TargetAdjuster { public void adjustTargets(Ability ability, Game game) { ability.getTargets().clear(); int xValue = ability.getManaCostsToPay().getX(); - FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with converted mana cost " + xValue); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, xValue)); + FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with mana value " + xValue); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, xValue)); ability.addTarget(new TargetCreaturePermanent(filter)); } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/e/EntreatTheDead.java b/Mage.Sets/src/mage/cards/e/EntreatTheDead.java index 44c51c62339e..0781e016ba61 100644 --- a/Mage.Sets/src/mage/cards/e/EntreatTheDead.java +++ b/Mage.Sets/src/mage/cards/e/EntreatTheDead.java @@ -25,12 +25,11 @@ public EntreatTheDead(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{X}{B}{B}{B}"); // Return X target creature cards from your graveyard to the battlefield. - this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(1, StaticFilters.FILTER_CARD_CREATURE)); + this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect().setText("return X target creature cards from your graveyard to the battlefield")); this.getSpellAbility().setTargetAdjuster(EntreatTheDeadAdjuster.instance); // Miracle {X}{B}{B} - this.addAbility(new MiracleAbility(this, new ManaCostsImpl("{X}{B}{B}"))); + this.addAbility(new MiracleAbility(this, new ManaCostsImpl<>("{X}{B}{B}"))); } private EntreatTheDead(final EntreatTheDead card) { diff --git a/Mage.Sets/src/mage/cards/e/EnvironmentalSciences.java b/Mage.Sets/src/mage/cards/e/EnvironmentalSciences.java new file mode 100644 index 000000000000..b51b3be6863d --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EnvironmentalSciences.java @@ -0,0 +1,39 @@ +package mage.cards.e; + +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EnvironmentalSciences extends CardImpl { + + public EnvironmentalSciences(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}"); + + this.subtype.add(SubType.LESSON); + + // Search your library for a basic land card, reveal it, put it into your hand, then shuffle. You gain 2 life. + this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect( + new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true + )); + this.getSpellAbility().addEffect(new GainLifeEffect(2).setText("You gain 2 life")); + } + + private EnvironmentalSciences(final EnvironmentalSciences card) { + super(card); + } + + @Override + public EnvironmentalSciences copy() { + return new EnvironmentalSciences(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EonHub.java b/Mage.Sets/src/mage/cards/e/EonHub.java index a52d89404773..d17564ada165 100644 --- a/Mage.Sets/src/mage/cards/e/EonHub.java +++ b/Mage.Sets/src/mage/cards/e/EonHub.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.cards.e; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/e/Ephemerate.java b/Mage.Sets/src/mage/cards/e/Ephemerate.java index 871ae3e16908..c90add03300e 100644 --- a/Mage.Sets/src/mage/cards/e/Ephemerate.java +++ b/Mage.Sets/src/mage/cards/e/Ephemerate.java @@ -21,7 +21,7 @@ public Ephemerate(UUID ownerId, CardSetInfo setInfo) { // Exile target creature you control, then return it to the battlefield under its owner's control. this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); this.getSpellAbility().addEffect(new ExileTargetForSourceEffect()); - this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false)); + this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false).setText(", then return it to the battlefield under its owner's control")); // Rebound this.addAbility(new ReboundAbility()); diff --git a/Mage.Sets/src/mage/cards/e/EpicDownfall.java b/Mage.Sets/src/mage/cards/e/EpicDownfall.java index 79e307b008b4..af5409c8606b 100644 --- a/Mage.Sets/src/mage/cards/e/EpicDownfall.java +++ b/Mage.Sets/src/mage/cards/e/EpicDownfall.java @@ -7,7 +7,7 @@ import mage.constants.ComparisonType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetPermanent; import java.util.UUID; @@ -18,10 +18,10 @@ public final class EpicDownfall extends CardImpl { private static final FilterPermanent filter - = new FilterCreaturePermanent("creature with converted mana cost 3 or greater"); + = new FilterCreaturePermanent("creature with mana value 3 or greater"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 2)); + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 2)); } public EpicDownfall(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/e/EpicExperiment.java b/Mage.Sets/src/mage/cards/e/EpicExperiment.java index 870ed34baa32..c0fe068149b5 100644 --- a/Mage.Sets/src/mage/cards/e/EpicExperiment.java +++ b/Mage.Sets/src/mage/cards/e/EpicExperiment.java @@ -10,7 +10,7 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.common.FilterInstantOrSorceryCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.ExileZone; import mage.game.Game; import mage.players.Player; @@ -48,7 +48,7 @@ class EpicExperimentEffect extends OneShotEffect { public EpicExperimentEffect() { super(Outcome.PlayForFree); staticText = "Exile the top X cards of your library. For each instant and " - + "sorcery card with converted mana cost X or less among them, " + + "sorcery card with mana value X or less among them, " + "you may cast that card without paying its mana cost. Then put all " + "cards exiled this way that weren't cast into your graveyard"; } @@ -69,9 +69,9 @@ public boolean apply(Game game, Ability source) { // cast the possible cards without paying the mana ExileZone epicExperimentExileZone = game.getExile().getExileZone(source.getSourceId()); FilterCard filter = new FilterInstantOrSorceryCard(); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, source.getManaCostsToPay().getX() + 1)); - filter.setMessage("instant and sorcery cards with converted mana cost " + filter.setMessage("instant and sorcery cards with mana value " + source.getManaCostsToPay().getX() + " or less"); Cards cardsToCast = new CardsImpl(); if (epicExperimentExileZone == null) { diff --git a/Mage.Sets/src/mage/cards/e/EpiphanyAtTheDrownyard.java b/Mage.Sets/src/mage/cards/e/EpiphanyAtTheDrownyard.java index 6fcd20af104b..c40996a8311c 100644 --- a/Mage.Sets/src/mage/cards/e/EpiphanyAtTheDrownyard.java +++ b/Mage.Sets/src/mage/cards/e/EpiphanyAtTheDrownyard.java @@ -1,10 +1,5 @@ - package mage.cards.e; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; @@ -20,14 +15,18 @@ import mage.target.common.TargetOpponent; import mage.util.GameLog; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class EpiphanyAtTheDrownyard extends CardImpl { public EpiphanyAtTheDrownyard(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{X}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{U}"); // Reveal the top X plus one cards of your library and separate them into two piles. An opponent chooses one of those piles. Put that pile into your hand and the other into your graveyard. this.getSpellAbility().addEffect(new EpiphanyAtTheDrownyardEffect()); @@ -46,12 +45,12 @@ public EpiphanyAtTheDrownyard copy() { class EpiphanyAtTheDrownyardEffect extends OneShotEffect { - public EpiphanyAtTheDrownyardEffect() { + EpiphanyAtTheDrownyardEffect() { super(Outcome.DrawCard); this.staticText = "Reveal the top X plus one cards of your library and separate them into two piles. An opponent chooses one of those piles. Put that pile into your hand and the other into your graveyard"; } - public EpiphanyAtTheDrownyardEffect(final EpiphanyAtTheDrownyardEffect effect) { + private EpiphanyAtTheDrownyardEffect(final EpiphanyAtTheDrownyardEffect effect) { super(effect); } @@ -124,9 +123,9 @@ public boolean apply(Game game, Ability source) { if (i < pile1CardsIds.size()) { sb.append(", "); } - card.moveToZone(pile1Zone, source, game, false); } } + controller.moveCards(new CardsImpl(pile1CardsIds), pile1Zone, source, game); game.informPlayers(sb.toString()); sb = new StringBuilder(sourceObject.getLogName() + ": Pile 2, going to ").append(pile2Zone == Zone.HAND ? "Hand" : "Graveyard").append(':'); @@ -139,9 +138,9 @@ public boolean apply(Game game, Ability source) { if (i < pile2CardsIds.size()) { sb.append(", "); } - card.moveToZone(pile2Zone, source, game, false); } } + controller.moveCards(new CardsImpl(pile2CardsIds), pile2Zone, source, game); game.informPlayers(sb.toString()); } diff --git a/Mage.Sets/src/mage/cards/e/EqualTreatment.java b/Mage.Sets/src/mage/cards/e/EqualTreatment.java index 5bb803e3f707..9cac57fc9548 100644 --- a/Mage.Sets/src/mage/cards/e/EqualTreatment.java +++ b/Mage.Sets/src/mage/cards/e/EqualTreatment.java @@ -57,9 +57,8 @@ public EqualTreatmentEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE - || event.getType() == GameEvent.EventType.DAMAGE_PLAYER - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT + || event.getType() == GameEvent.EventType.DAMAGE_PLAYER; } @Override diff --git a/Mage.Sets/src/mage/cards/e/ErebosBleakHearted.java b/Mage.Sets/src/mage/cards/e/ErebosBleakHearted.java index 8be207540324..abf4a2fa75a3 100644 --- a/Mage.Sets/src/mage/cards/e/ErebosBleakHearted.java +++ b/Mage.Sets/src/mage/cards/e/ErebosBleakHearted.java @@ -22,7 +22,7 @@ import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/e/ErraticCyclops.java b/Mage.Sets/src/mage/cards/e/ErraticCyclops.java index 08755be44882..80b8efce2017 100644 --- a/Mage.Sets/src/mage/cards/e/ErraticCyclops.java +++ b/Mage.Sets/src/mage/cards/e/ErraticCyclops.java @@ -68,7 +68,7 @@ public boolean checkTrigger(GameEvent event, Game game) { && spell.isInstantOrSorcery()) { this.getEffects().clear(); this.addEffect(new BoostSourceEffect( - spell.getConvertedManaCost(), 0, Duration.EndOfTurn + spell.getManaValue(), 0, Duration.EndOfTurn )); return true; } @@ -79,7 +79,7 @@ public boolean checkTrigger(GameEvent event, Game game) { public String getRule() { return "Whenever you cast an instant or sorcery spell, " + "{this} gets +X/+0 until end of turn, " - + "where X is that spell's converted mana cost"; + + "where X is that spell's mana value."; } @Override diff --git a/Mage.Sets/src/mage/cards/e/ErraticExplosion.java b/Mage.Sets/src/mage/cards/e/ErraticExplosion.java index d31a7c10164b..96e5f05795a8 100644 --- a/Mage.Sets/src/mage/cards/e/ErraticExplosion.java +++ b/Mage.Sets/src/mage/cards/e/ErraticExplosion.java @@ -42,7 +42,7 @@ class ErraticExplosionEffect extends OneShotEffect { public ErraticExplosionEffect() { super(Outcome.Damage); - this.staticText = "Choose any target. Reveal cards from the top of your library until you reveal a nonland card. {this} deals damage equal to that card's converted mana cost to that permanent or player. Put the revealed cards on the bottom of your library in any order"; + this.staticText = "Choose any target. Reveal cards from the top of your library until you reveal a nonland card. {this} deals damage equal to that card's mana value to that permanent or player. Put the revealed cards on the bottom of your library in any order"; } public ErraticExplosionEffect(ErraticExplosionEffect effect) { @@ -72,11 +72,11 @@ public boolean apply(Game game, Ability source) { if (nonLandCard != null) { Permanent targetCreature = game.getPermanent(this.getTargetPointer().getFirst(game, source)); if (targetCreature != null) { - targetCreature.damage(nonLandCard.getConvertedManaCost(), source.getSourceId(), source, game, false, true); + targetCreature.damage(nonLandCard.getManaValue(), source.getSourceId(), source, game, false, true); } else { Player targetPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source)); if (targetPlayer != null) { - targetPlayer.damage(nonLandCard.getConvertedManaCost(), source.getSourceId(), source, game); + targetPlayer.damage(nonLandCard.getManaValue(), source.getSourceId(), source, game); } } } diff --git a/Mage.Sets/src/mage/cards/e/ErraticMutation.java b/Mage.Sets/src/mage/cards/e/ErraticMutation.java index 4e7f70ac3bb0..57e8637505f6 100644 --- a/Mage.Sets/src/mage/cards/e/ErraticMutation.java +++ b/Mage.Sets/src/mage/cards/e/ErraticMutation.java @@ -48,7 +48,7 @@ class ErraticMutationEffect extends OneShotEffect { public ErraticMutationEffect() { super(Outcome.UnboostCreature); - this.staticText = "Choose target creature. Reveal cards from the top of your library until you reveal a nonland card. That creature gets +X/-X until end of turn, where X is that card's converted mana cost. Put all cards revealed this way on the bottom of your library in any order"; + this.staticText = "Choose target creature. Reveal cards from the top of your library until you reveal a nonland card. That creature gets +X/-X until end of turn, where X is that card's mana value. Put all cards revealed this way on the bottom of your library in any order"; } public ErraticMutationEffect(final ErraticMutationEffect effect) { @@ -79,7 +79,7 @@ public boolean apply(Game game, Ability source) { // the nonland card if (nonLandCard != null) { - int boostValue = nonLandCard.getConvertedManaCost(); + int boostValue = nonLandCard.getManaValue(); // unboost target ContinuousEffect effect = new BoostTargetEffect(boostValue, -boostValue, Duration.EndOfTurn); effect.setTargetPointer(new FixedTarget(this.getTargetPointer().getFirst(game, source))); diff --git a/Mage.Sets/src/mage/cards/e/EscapePod.java b/Mage.Sets/src/mage/cards/e/EscapePod.java index e8e6cac66ab3..326850022ac6 100644 --- a/Mage.Sets/src/mage/cards/e/EscapePod.java +++ b/Mage.Sets/src/mage/cards/e/EscapePod.java @@ -1,24 +1,24 @@ - package mage.cards.e; -import java.util.UUID; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnToBattlefieldUnderYourControlTargetEffect; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author Styxo */ public final class EscapePod extends CardImpl { @@ -29,7 +29,6 @@ public EscapePod(UUID ownerId, CardSetInfo setInfo) { // Exile target creature you control. Return that card to the battlefield under your control at the beginning of the next end step. this.getSpellAbility().addEffect(new EscapePodEffect()); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - } private EscapePod(final EscapePod card) { @@ -44,34 +43,35 @@ public EscapePod copy() { class EscapePodEffect extends OneShotEffect { - public EscapePodEffect() { + EscapePodEffect() { super(Outcome.Detriment); - staticText = "Exile target creature you control. Return that card to the battlefield under your control at the beginning of the next end step"; + staticText = "Exile target creature you control. Return that card to the battlefield " + + "under your control at the beginning of the next end step"; } - public EscapePodEffect(final EscapePodEffect effect) { + private EscapePodEffect(final EscapePodEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanent(source.getFirstTarget()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (permanent != null && sourceObject != null) { - if (permanent.moveToExile(source.getSourceId(), sourceObject.getIdName(), source, game)) { - Effect effect = new ReturnToBattlefieldUnderYourControlTargetEffect(); - effect.setText("Return that card to the battlefield under your control at the beginning of the next end step"); - effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); - game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source); - return true; - } + if (player == null || permanent == null) { + return false; } - return false; + Card card = permanent.getMainCard(); + player.moveCards(permanent, Zone.EXILED, source, game); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility( + new ReturnToBattlefieldUnderYourControlTargetEffect().setText( + "Return that card to the battlefield under your control at the beginning of the next end step" + ).setTargetPointer(new FixedTarget(card, game)) + ), source); + return true; } @Override public EscapePodEffect copy() { return new EscapePodEffect(this); } - } diff --git a/Mage.Sets/src/mage/cards/e/EsiorWardwingFamiliar.java b/Mage.Sets/src/mage/cards/e/EsiorWardwingFamiliar.java index 501a516976eb..5e0188120352 100644 --- a/Mage.Sets/src/mage/cards/e/EsiorWardwingFamiliar.java +++ b/Mage.Sets/src/mage/cards/e/EsiorWardwingFamiliar.java @@ -12,7 +12,7 @@ import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.CommanderPredicate; +import mage.filter.predicate.mageobject.CommanderPredicate; import mage.game.Game; import mage.util.CardUtil; diff --git a/Mage.Sets/src/mage/cards/e/EsixFractalBloom.java b/Mage.Sets/src/mage/cards/e/EsixFractalBloom.java new file mode 100644 index 000000000000..6f3325356065 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EsixFractalBloom.java @@ -0,0 +1,175 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.CopyEffect; +import mage.abilities.keyword.FlyingAbility; +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.AnotherPredicate; +import mage.game.Game; +import mage.game.events.CreateTokenEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.EmptyToken; +import mage.game.permanent.token.Token; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.util.CardUtil; +import mage.util.functions.CopyApplier; +import mage.util.functions.EmptyCopyApplier; +import mage.watchers.Watcher; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EsixFractalBloom extends CardImpl { + + public EsixFractalBloom(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.FRACTAL); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // The first time you would create one or more tokens during each of your turns, you may instead choose a creature other than Esix, Fractal Bloom and create that many tokens that are copies of that creature. + this.addAbility(new SimpleStaticAbility(new EsixFractalBloomEffect()), new EsixFractalBloomWatcher()); + } + + private EsixFractalBloom(final EsixFractalBloom card) { + super(card); + } + + @Override + public EsixFractalBloom copy() { + return new EsixFractalBloom(this); + } +} + +class EsixFractalBloomEffect extends ReplacementEffectImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("another creature"); + + static { + filter.add(AnotherPredicate.instance); + } + + EsixFractalBloomEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit, false); + staticText = "the first time you would create one or more tokens during each of your turns, " + + "you may instead choose a creature other than {this} " + + "and create that many tokens that are copies of that creature"; + } + + private EsixFractalBloomEffect(EsixFractalBloomEffect effect) { + super(effect); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.CREATE_TOKEN; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.isControlledBy(event.getPlayerId()) + && game.isActivePlayer(source.getControllerId()) + && !EsixFractalBloomWatcher.checkPlayer(source.getControllerId(), game) + && game.getBattlefield().count( + filter, source.getSourceId(), source.getControllerId(), game + ) > 0; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + TargetPermanent target = new TargetPermanent(0, 1, filter, true); + player.choose(outcome, target, source.getSourceId(), game); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent == null) { + return false; + } + ((CreateTokenEvent) event).setToken(copyPermanentToToken(permanent, game, source)); + return false; + } + + @Override + public EsixFractalBloomEffect copy() { + return new EsixFractalBloomEffect(this); + } + + private static Token copyPermanentToToken(Permanent permanent, Game game, Ability source) { + CopyApplier applier = new EmptyCopyApplier(); + // handle copies of copies + Permanent copyFromPermanent = permanent; + for (ContinuousEffect effect : game.getState().getContinuousEffects().getLayeredEffects(game)) { + if (!(effect instanceof CopyEffect)) { + continue; + } + CopyEffect copyEffect = (CopyEffect) effect; + // there is another copy effect that our targetPermanent copies stats from + if (!copyEffect.getSourceId().equals(permanent.getId())) { + continue; + } + MageObject object = ((CopyEffect) effect).getTarget(); + if (!(object instanceof Permanent)) { + continue; + } + copyFromPermanent = (Permanent) object; + if (copyEffect.getApplier() != null) { + applier = copyEffect.getApplier(); + } + } + + // create token and modify all attributes permanently (without game usage) + EmptyToken token = new EmptyToken(); + CardUtil.copyTo(token).from(copyFromPermanent, game); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer) + applier.apply(game, token, source, permanent.getId()); + return token; + } +} + +class EsixFractalBloomWatcher extends Watcher { + + private final Set createdThisTurn = new HashSet<>(); + + EsixFractalBloomWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.CREATED_TOKEN) { + createdThisTurn.add(event.getPlayerId()); + } + } + + @Override + public void reset() { + super.reset(); + createdThisTurn.clear(); + } + + static boolean checkPlayer(UUID playerId, Game game) { + EsixFractalBloomWatcher watcher = game.getState().getWatcher(EsixFractalBloomWatcher.class); + return watcher != null && watcher.createdThisTurn.contains(playerId); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EsperPanorama.java b/Mage.Sets/src/mage/cards/e/EsperPanorama.java index 055b82ccbf78..979a18654e01 100644 --- a/Mage.Sets/src/mage/cards/e/EsperPanorama.java +++ b/Mage.Sets/src/mage/cards/e/EsperPanorama.java @@ -22,7 +22,7 @@ */ public final class EsperPanorama extends CardImpl { - private static final FilterCard filter = new FilterCard("a basic Plains, Island, or Swamp"); + private static final FilterCard filter = new FilterCard("a basic Plains, Island, or Swamp card"); static { filter.add(CardType.LAND.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/e/EsperStormblade.java b/Mage.Sets/src/mage/cards/e/EsperStormblade.java index f7ddaea9936c..c9ae008bc62d 100644 --- a/Mage.Sets/src/mage/cards/e/EsperStormblade.java +++ b/Mage.Sets/src/mage/cards/e/EsperStormblade.java @@ -18,7 +18,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.MulticoloredPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/e/EssenceInfusion.java b/Mage.Sets/src/mage/cards/e/EssenceInfusion.java new file mode 100644 index 000000000000..82f67fa07e68 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EssenceInfusion.java @@ -0,0 +1,39 @@ +package mage.cards.e; + +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.counters.CounterType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EssenceInfusion extends CardImpl { + + public EssenceInfusion(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}"); + + // Put two +1/+1 counters on target creature. It gains lifelink until end of turn. + this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance(2))); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + LifelinkAbility.getInstance(), Duration.EndOfTurn + ).setText("It gains lifelink until end of turn")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private EssenceInfusion(final EssenceInfusion card) { + super(card); + } + + @Override + public EssenceInfusion copy() { + return new EssenceInfusion(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EssencePulse.java b/Mage.Sets/src/mage/cards/e/EssencePulse.java new file mode 100644 index 000000000000..e683a2a77fff --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EssencePulse.java @@ -0,0 +1,41 @@ +package mage.cards.e; + +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.MultipliedValue; +import mage.abilities.dynamicvalue.common.ControllerGotLifeCount; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EssencePulse extends CardImpl { + + private static final DynamicValue xValue = new MultipliedValue(ControllerGotLifeCount.instance, -1); + + public EssencePulse(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}"); + + // You gain 2 life. Each creature gets -X/-X until end of turn, where X is the amount of life you gained this turn. + this.getSpellAbility().addEffect(new GainLifeEffect(2)); + this.getSpellAbility().addEffect(new BoostAllEffect( + xValue, xValue, Duration.EndOfTurn + ).setText("Each creature gets -X/-X until end of turn, where X is the amount of life you gained this turn")); + this.getSpellAbility().addHint(ControllerGotLifeCount.getHint()); + } + + private EssencePulse(final EssencePulse card) { + super(card); + } + + @Override + public EssencePulse copy() { + return new EssencePulse(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EssenceSliver.java b/Mage.Sets/src/mage/cards/e/EssenceSliver.java index c8a1de1edbfc..4a619fa25332 100644 --- a/Mage.Sets/src/mage/cards/e/EssenceSliver.java +++ b/Mage.Sets/src/mage/cards/e/EssenceSliver.java @@ -65,9 +65,8 @@ public DealsDamageAllTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE - || event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT + || event.getType() == GameEvent.EventType.DAMAGED_PLAYER; } @Override diff --git a/Mage.Sets/src/mage/cards/e/EssenceWarden.java b/Mage.Sets/src/mage/cards/e/EssenceWarden.java index e92881580df8..4695f8373255 100644 --- a/Mage.Sets/src/mage/cards/e/EssenceWarden.java +++ b/Mage.Sets/src/mage/cards/e/EssenceWarden.java @@ -11,7 +11,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/e/EstridTheMasked.java b/Mage.Sets/src/mage/cards/e/EstridTheMasked.java index 4a0585574df8..06d4ff560162 100644 --- a/Mage.Sets/src/mage/cards/e/EstridTheMasked.java +++ b/Mage.Sets/src/mage/cards/e/EstridTheMasked.java @@ -19,7 +19,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterEnchantmentCard; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.EnchantedPredicate; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/e/EstridsInvocation.java b/Mage.Sets/src/mage/cards/e/EstridsInvocation.java index a3d6575b6713..59702fc8a5e9 100644 --- a/Mage.Sets/src/mage/cards/e/EstridsInvocation.java +++ b/Mage.Sets/src/mage/cards/e/EstridsInvocation.java @@ -17,6 +17,7 @@ import mage.filter.common.FilterControlledEnchantmentPermanent; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.util.functions.CopyApplier; import java.util.UUID; @@ -68,8 +69,7 @@ class EstridsInvocationEffect extends OneShotEffect { EstridsInvocationEffect() { super(Outcome.Neutral); - this.staticText = "you may exile this enchantment. " - + "If you do, return it to the battlefield under its owner's control"; + this.staticText = "exile this enchantment. If you do, return it to the battlefield under its owner's control"; } private EstridsInvocationEffect(final EstridsInvocationEffect effect) { @@ -83,15 +83,14 @@ public EstridsInvocationEffect copy() { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - if (permanent.moveToExile(source.getSourceId(), "Estrid's Invocation", source, game)) { - Card card = game.getExile().getCard(source.getSourceId(), game); - if (card != null) { - return card.moveToZone(Zone.BATTLEFIELD, source, game, false); - } - } + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent == null || player == null) { + return false; } - return false; + Card card = permanent.getMainCard(); + player.moveCards(card, Zone.EXILED, source, game); + player.moveCards(card, Zone.BATTLEFIELD, source, game, false, false, true, null); + return true; } } diff --git a/Mage.Sets/src/mage/cards/e/EtaliPrimalStorm.java b/Mage.Sets/src/mage/cards/e/EtaliPrimalStorm.java index 1731ff02b781..de743e3de923 100644 --- a/Mage.Sets/src/mage/cards/e/EtaliPrimalStorm.java +++ b/Mage.Sets/src/mage/cards/e/EtaliPrimalStorm.java @@ -54,7 +54,7 @@ class EtaliPrimalStormEffect extends OneShotEffect { public EtaliPrimalStormEffect() { super(Outcome.PlayForFree); this.staticText = "exile the top card of each player's library, then you may cast " - + "any number of nonland cards exiled this way without paying their mana costs"; + + "any number of spells from among those cards without paying their mana costs"; } public EtaliPrimalStormEffect(final EtaliPrimalStormEffect effect) { diff --git a/Mage.Sets/src/mage/cards/e/EternalDominion.java b/Mage.Sets/src/mage/cards/e/EternalDominion.java index f9b0c9c00171..b4bc4ecb0e75 100644 --- a/Mage.Sets/src/mage/cards/e/EternalDominion.java +++ b/Mage.Sets/src/mage/cards/e/EternalDominion.java @@ -62,7 +62,7 @@ class EternalDominionEffect extends OneShotEffect { public EternalDominionEffect() { super(Outcome.Benefit); - staticText = "Search target opponent's library for an artifact, creature, enchantment, or land card. Put that card onto the battlefield under your control. Then that player shuffles their library"; + staticText = "Search target opponent's library for an artifact, creature, enchantment, or land card. Put that card onto the battlefield under your control. Then that player shuffles"; } public EternalDominionEffect(final EternalDominionEffect effect) { diff --git a/Mage.Sets/src/mage/cards/e/EternalDragon.java b/Mage.Sets/src/mage/cards/e/EternalDragon.java index da46b16e53ae..a2d68b9d610b 100644 --- a/Mage.Sets/src/mage/cards/e/EternalDragon.java +++ b/Mage.Sets/src/mage/cards/e/EternalDragon.java @@ -7,6 +7,7 @@ import mage.abilities.condition.common.IsStepCondition; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalActivatedAbility; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToHandEffect; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.PlainscyclingAbility; @@ -36,7 +37,7 @@ public EternalDragon(UUID ownerId, CardSetInfo setInfo) { // {3}{W}{W}: Return Eternal Dragon from your graveyard to your hand. Activate this ability only during your upkeep. Ability ability = new ConditionalActivatedAbility(Zone.GRAVEYARD, - new ReturnToHandSourceEffect(), + new ReturnSourceFromGraveyardToHandEffect(), new ManaCostsImpl("{3}{W}{W}"), new IsStepCondition(PhaseStep.UPKEEP), null diff --git a/Mage.Sets/src/mage/cards/e/EtherealArmor.java b/Mage.Sets/src/mage/cards/e/EtherealArmor.java index 9e79307d1788..c72bf2cac9ff 100644 --- a/Mage.Sets/src/mage/cards/e/EtherealArmor.java +++ b/Mage.Sets/src/mage/cards/e/EtherealArmor.java @@ -1,10 +1,8 @@ - - package mage.cards.e; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; @@ -14,27 +12,27 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterEnchantmentPermanent; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledEnchantmentPermanent; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class EtherealArmor extends CardImpl { - private static final FilterEnchantmentPermanent filter = new FilterEnchantmentPermanent("enchantment you control"); + private static final FilterPermanent filter + = new FilterControlledEnchantmentPermanent("enchantment you control"); - static { - filter.add(TargetController.YOU.getControllerPredicate()); - } + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); - public EtherealArmor (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{W}"); + public EtherealArmor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); this.subtype.add(SubType.AURA); - // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); @@ -43,13 +41,16 @@ public EtherealArmor (UUID ownerId, CardSetInfo setInfo) { this.addAbility(ability); // Enchanted creature gets +1/+1 for each enchantment you control and has first strike. - PermanentsOnBattlefieldCount countEnchantments = new PermanentsOnBattlefieldCount(new FilterEnchantmentPermanent(filter)); - SimpleStaticAbility ability2 = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(countEnchantments, countEnchantments, Duration.WhileOnBattlefield)); - ability2.addEffect(new GainAbilityAttachedEffect(FirstStrikeAbility.getInstance(), AttachmentType.AURA)); - this.addAbility(ability2); + ability = new SimpleStaticAbility(new BoostEnchantedEffect( + xValue, xValue, Duration.WhileOnBattlefield + )); + ability.addEffect(new GainAbilityAttachedEffect( + FirstStrikeAbility.getInstance(), AttachmentType.AURA + ).setText("and has first strike")); + this.addAbility(ability); } - public EtherealArmor (final EtherealArmor card) { + private EtherealArmor(final EtherealArmor card) { super(card); } @@ -57,5 +58,4 @@ public EtherealArmor (final EtherealArmor card) { public EtherealArmor copy() { return new EtherealArmor(this); } - } diff --git a/Mage.Sets/src/mage/cards/e/EtherwroughtPage.java b/Mage.Sets/src/mage/cards/e/EtherwroughtPage.java index cf24984ba4e5..d30cf50f6d25 100644 --- a/Mage.Sets/src/mage/cards/e/EtherwroughtPage.java +++ b/Mage.Sets/src/mage/cards/e/EtherwroughtPage.java @@ -84,7 +84,7 @@ public boolean apply(Game game, Ability source) { CardsImpl cards = new CardsImpl(); cards.add(card); controller.lookAtCards("Etherwrought Page", cards, game); - if (controller.chooseUse(Outcome.Neutral, "Do you wish to put the card into your graveyard?", source, game)) { + if (controller.chooseUse(Outcome.Neutral, "Put that card into your graveyard?", source, game)) { return controller.moveCards(card, Zone.GRAVEYARD, source, game); } return true; diff --git a/Mage.Sets/src/mage/cards/e/EurekaMoment.java b/Mage.Sets/src/mage/cards/e/EurekaMoment.java new file mode 100644 index 000000000000..7dc92072d633 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EurekaMoment.java @@ -0,0 +1,33 @@ +package mage.cards.e; + +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.PutCardFromHandOntoBattlefieldEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EurekaMoment extends CardImpl { + + public EurekaMoment(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}{U}"); + + // Draw two cards. You may put a land card from your hand onto the battlefield. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2)); + this.getSpellAbility().addEffect(new PutCardFromHandOntoBattlefieldEffect(StaticFilters.FILTER_CARD_LAND_A)); + } + + private EurekaMoment(final EurekaMoment card) { + super(card); + } + + @Override + public EurekaMoment copy() { + return new EurekaMoment(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EverAfter.java b/Mage.Sets/src/mage/cards/e/EverAfter.java index dd2297a21e23..f27e9391d4d6 100644 --- a/Mage.Sets/src/mage/cards/e/EverAfter.java +++ b/Mage.Sets/src/mage/cards/e/EverAfter.java @@ -23,7 +23,7 @@ public EverAfter(UUID ownerId, CardSetInfo setInfo) { // Return up to two target creature cards from your graveyard to the battlefield. Each of those creatures is a black Zombie in addition // to its other colors and types. Put Ever After on the bottom of its owner's library. - this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect(false, false)); + this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, new FilterCreatureCard("creature cards from your graveyard"))); Effect effect = new BecomesBlackZombieAdditionEffect(); effect.setText("Each of those creatures is a black Zombie in addition to its other colors and types"); diff --git a/Mage.Sets/src/mage/cards/e/EverWatchingThreshold.java b/Mage.Sets/src/mage/cards/e/EverWatchingThreshold.java index 6498c817f128..df9fbf7b73be 100644 --- a/Mage.Sets/src/mage/cards/e/EverWatchingThreshold.java +++ b/Mage.Sets/src/mage/cards/e/EverWatchingThreshold.java @@ -1,6 +1,5 @@ package mage.cards.e; -import java.util.UUID; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; @@ -9,11 +8,10 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class EverWatchingThreshold extends CardImpl { @@ -57,25 +55,16 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { - Player player = game.getPlayer(this.getControllerId()); - if (player == null) { - return false; - } - for (UUID attacker : game.getCombat().getAttackers()) { - Permanent creature = game.getPermanent(attacker); - if (creature != null - && player.hasOpponent(creature.getControllerId(), game) - && player.getId().equals(game.getCombat().getDefendingPlayerId(attacker, game))) { - return true; - } - } - return false; + return game.getCombat() + .getAttackers() + .stream() + .filter(attacker -> isControlledBy(game.getCombat().getDefendingPlayerId(attacker, game))) + .map(game::getControllerId) + .anyMatch(game.getOpponents(getControllerId())::contains); } @Override public String getRule() { - return "Whenever an opponent attacks you " - + "and/or a planeswalker you control " - + "with one or more creatures, draw a card."; + return "Whenever an opponent attacks, if they attacked you and/or a planeswalker you control, draw a card."; } } diff --git a/Mage.Sets/src/mage/cards/e/EverbarkShaman.java b/Mage.Sets/src/mage/cards/e/EverbarkShaman.java index 7760992bc71d..8a1533f8cf53 100644 --- a/Mage.Sets/src/mage/cards/e/EverbarkShaman.java +++ b/Mage.Sets/src/mage/cards/e/EverbarkShaman.java @@ -25,7 +25,7 @@ public final class EverbarkShaman extends CardImpl { private static final FilterCard filterForest = new FilterCard("Forest"); - private static final FilterCard filterTreefolk = new FilterCard("Treefolk from your graveyard"); + private static final FilterCard filterTreefolk = new FilterCard("Treefolk card from your graveyard"); static { filterForest.add(SubType.FOREST.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/e/EverlastingTorment.java b/Mage.Sets/src/mage/cards/e/EverlastingTorment.java index 5feddd1fd7e8..7090898426ce 100644 --- a/Mage.Sets/src/mage/cards/e/EverlastingTorment.java +++ b/Mage.Sets/src/mage/cards/e/EverlastingTorment.java @@ -1,7 +1,5 @@ - package mage.cards.e; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; @@ -12,32 +10,31 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; -import mage.counters.Counter; -import mage.counters.CounterType; import mage.game.Game; +import mage.game.events.DamageEvent; import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; + +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class EverlastingTorment extends CardImpl { public EverlastingTorment(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{B/R}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B/R}"); // Players can't gain life. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantGainLifeAllEffect())); + this.addAbility(new SimpleStaticAbility(new CantGainLifeAllEffect())); // Damage can't be prevented. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new DamageCantBePreventedEffect(Duration.WhileOnBattlefield, "Damage can't be prevented", true, false))); + this.addAbility(new SimpleStaticAbility(new DamageCantBePreventedEffect( + Duration.WhileOnBattlefield, "Damage can't be prevented", + true, false + ))); // All damage is dealt as though its source had wither. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DamageDealtAsIfSourceHadWitherEffect())); - + this.addAbility(new SimpleStaticAbility(new DamageDealtAsIfSourceHadWitherEffect())); } private EverlastingTorment(final EverlastingTorment card) { @@ -52,12 +49,12 @@ public EverlastingTorment copy() { class DamageDealtAsIfSourceHadWitherEffect extends ReplacementEffectImpl { - public DamageDealtAsIfSourceHadWitherEffect() { + DamageDealtAsIfSourceHadWitherEffect() { super(Duration.WhileOnBattlefield, Outcome.Neutral); staticText = "All damage is dealt as though its source had wither"; } - public DamageDealtAsIfSourceHadWitherEffect(final DamageDealtAsIfSourceHadWitherEffect effect) { + private DamageDealtAsIfSourceHadWitherEffect(final DamageDealtAsIfSourceHadWitherEffect effect) { super(effect); } @@ -73,23 +70,15 @@ public boolean apply(Game game, Ability source) { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - int damageAmount = event.getAmount(); - if (damageAmount > 0) { - Counter counter = CounterType.M1M1.createInstance(damageAmount); - Permanent creatureDamaged = game.getPermanent(event.getTargetId()); - if (creatureDamaged != null) { - creatureDamaged.addCounters(counter, source.getControllerId(), source, game); - } - } - return true; + ((DamageEvent) event).setAsThoughWither(true); + return false; } @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT; } - @Override public boolean applies(GameEvent event, Ability source, Game game) { return true; diff --git a/Mage.Sets/src/mage/cards/e/Evershrike.java b/Mage.Sets/src/mage/cards/e/Evershrike.java index 77541a6a944e..2040992da913 100644 --- a/Mage.Sets/src/mage/cards/e/Evershrike.java +++ b/Mage.Sets/src/mage/cards/e/Evershrike.java @@ -1,12 +1,11 @@ - package mage.cards.e; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.AuraAttachedCount; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; @@ -16,18 +15,22 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; -import mage.filter.predicate.other.AuraCardCanAttachToPermanentId; +import mage.filter.predicate.card.AuraCardCanAttachToPermanentId; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetCard; +import java.util.UUID; + /** * @author jeffwadsworth */ public final class Evershrike extends CardImpl { + private static final DynamicValue amount = new AuraAttachedCount(2); + public Evershrike(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W/B}{W/B}"); this.subtype.add(SubType.ELEMENTAL); @@ -40,12 +43,10 @@ public Evershrike(UUID ownerId, CardSetInfo setInfo) { this.addAbility(FlyingAbility.getInstance()); // Evershrike gets +2/+2 for each Aura attached to it. - AuraAttachedCount amount = new AuraAttachedCount(2); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostSourceEffect(amount, amount, Duration.WhileOnBattlefield))); + this.addAbility(new SimpleStaticAbility(new BoostSourceEffect(amount, amount, Duration.WhileOnBattlefield))); // {X}{WB}{WB}: Return Evershrike from your graveyard to the battlefield. You may put an Aura card with converted mana cost X or less from your hand onto the battlefield attached to it. If you don't, exile Evershrike. this.addAbility(new SimpleActivatedAbility(Zone.GRAVEYARD, new EvershrikeEffect(), new ManaCostsImpl("{X}{W/B}{W/B}"))); - } private Evershrike(final Evershrike card) { @@ -60,12 +61,14 @@ public Evershrike copy() { class EvershrikeEffect extends OneShotEffect { - public EvershrikeEffect() { + EvershrikeEffect() { super(Outcome.Benefit); - staticText = "Return {this} from your graveyard to the battlefield. You may put an Aura card with converted mana cost X or less from your hand onto the battlefield attached to it. If you don't, exile {this}"; + staticText = "Return {this} from your graveyard to the battlefield. " + + "You may put an Aura card with mana value X or less from your hand " + + "onto the battlefield attached to it. If you don't, exile {this}"; } - public EvershrikeEffect(final EvershrikeEffect effect) { + private EvershrikeEffect(final EvershrikeEffect effect) { super(effect); } @@ -73,40 +76,42 @@ public EvershrikeEffect(final EvershrikeEffect effect) { public boolean apply(Game game, Ability source) { Card evershrikeCard = game.getCard(source.getSourceId()); Player controller = game.getPlayer(source.getControllerId()); - if (controller != null && evershrikeCard != null) { - if (evershrikeCard.moveToZone(Zone.BATTLEFIELD, source, game, false)) { - int xAmount = source.getManaCostsToPay().getX() + 1; - Permanent evershrikePermanent = game.getPermanent(source.getSourceId()); - if (evershrikePermanent == null) { - return false; - } - boolean exileSource = true; - FilterCard filterAuraCard = new FilterCard("Aura card with converted mana cost X or less from your hand"); - filterAuraCard.add(CardType.ENCHANTMENT.getPredicate()); - filterAuraCard.add(SubType.AURA.getPredicate()); - filterAuraCard.add(new AuraCardCanAttachToPermanentId(evershrikePermanent.getId())); - filterAuraCard.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, xAmount)); - int count = controller.getHand().count(filterAuraCard, game); - if (count > 0 && controller.chooseUse(Outcome.Benefit, "Do you wish to put an Aura card from your hand onto " + evershrikeCard.getIdName() + "?", source, game)) { - TargetCard targetAura = new TargetCard(Zone.HAND, filterAuraCard); - if (controller.choose(Outcome.Benefit, controller.getHand(), targetAura, game)) { - Card aura = game.getCard(targetAura.getFirstTarget()); - if (aura != null) { - game.getState().setValue("attachTo:" + aura.getId(), evershrikePermanent); - if (controller.moveCards(aura, Zone.BATTLEFIELD, source, game)) { - evershrikePermanent.addAttachment(aura.getId(), source, game); - } - exileSource = false; - } + if (controller == null || evershrikeCard == null) { + return false; + } + int xAmount = source.getManaCostsToPay().getX(); + controller.moveCards(evershrikeCard, Zone.BATTLEFIELD, source, game); + Permanent evershrikePermanent = game.getPermanent(evershrikeCard.getId()); + if (evershrikePermanent == null) { + if (game.getState().getZone(evershrikeCard.getId()) != Zone.EXILED) { + controller.moveCards(evershrikeCard, Zone.EXILED, source, game); + } + return false; + } + boolean exileSource = true; + FilterCard filterAuraCard = new FilterCard("Aura card with mana value X or less from your hand"); + filterAuraCard.add(CardType.ENCHANTMENT.getPredicate()); + filterAuraCard.add(SubType.AURA.getPredicate()); + filterAuraCard.add(new AuraCardCanAttachToPermanentId(evershrikePermanent.getId())); + filterAuraCard.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xAmount + 1)); + int count = controller.getHand().count(filterAuraCard, game); + if (count > 0 && controller.chooseUse(Outcome.Benefit, "Put an Aura card from your hand onto the battlefield attached to " + evershrikeCard.getIdName() + "?", source, game)) { + TargetCard targetAura = new TargetCard(Zone.HAND, filterAuraCard); + if (controller.choose(Outcome.Benefit, controller.getHand(), targetAura, game)) { + Card aura = game.getCard(targetAura.getFirstTarget()); + if (aura != null) { + game.getState().setValue("attachTo:" + aura.getId(), evershrikePermanent); + if (controller.moveCards(aura, Zone.BATTLEFIELD, source, game)) { + evershrikePermanent.addAttachment(aura.getId(), source, game); } + exileSource = false; } - if (exileSource) { - controller.moveCards(evershrikeCard, Zone.EXILED, source, game); - } - return true; } } - return false; + if (exileSource) { + controller.moveCards(evershrikeCard, Zone.EXILED, source, game); + } + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/e/EverythingamajigB.java b/Mage.Sets/src/mage/cards/e/EverythingamajigB.java index 35cfe5358bb7..ed21767e6a74 100644 --- a/Mage.Sets/src/mage/cards/e/EverythingamajigB.java +++ b/Mage.Sets/src/mage/cards/e/EverythingamajigB.java @@ -21,7 +21,7 @@ import mage.filter.common.FilterPermanentCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.NamePredicate; -import mage.filter.predicate.other.ExpansionSetPredicate; +import mage.filter.predicate.card.ExpansionSetPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/e/EverythingamajigC.java b/Mage.Sets/src/mage/cards/e/EverythingamajigC.java index 477d2896fed5..bb42334f4a37 100644 --- a/Mage.Sets/src/mage/cards/e/EverythingamajigC.java +++ b/Mage.Sets/src/mage/cards/e/EverythingamajigC.java @@ -86,7 +86,7 @@ public ManaScrewAbility copy() { @Override public String getRule() { - return super.getRule() + " Activate this ability only any time you could cast an instant."; + return super.getRule() + " Activate only as an instant."; } } diff --git a/Mage.Sets/src/mage/cards/e/EverythingamajigE.java b/Mage.Sets/src/mage/cards/e/EverythingamajigE.java index 5328e536ab51..a45f3cfdc797 100644 --- a/Mage.Sets/src/mage/cards/e/EverythingamajigE.java +++ b/Mage.Sets/src/mage/cards/e/EverythingamajigE.java @@ -71,7 +71,7 @@ class UrzasHotTubEffect extends OneShotEffect { public UrzasHotTubEffect() { super(Outcome.ReturnToHand); - this.staticText = "Search your library for a card that shares a complete word in its name with the discarded card, reveal it, put it into your hand, then shuffle your library"; + this.staticText = "Search your library for a card that shares a complete word in its name with the discarded card, reveal it, put it into your hand, then shuffle"; } public UrzasHotTubEffect(final UrzasHotTubEffect effect) { diff --git a/Mage.Sets/src/mage/cards/e/EvolutionCharm.java b/Mage.Sets/src/mage/cards/e/EvolutionCharm.java index daf6c438f635..b148f091c7f3 100644 --- a/Mage.Sets/src/mage/cards/e/EvolutionCharm.java +++ b/Mage.Sets/src/mage/cards/e/EvolutionCharm.java @@ -25,7 +25,7 @@ public EvolutionCharm(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); // Choose one - Search your library for a basic land card, reveal it, put it into your hand, then shuffle your library; - this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 1, StaticFilters.FILTER_CARD_BASIC_LAND), true, true).setText("Search your library for a basic land card, reveal it, put it into your hand, then shuffle your library")); + this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 1, StaticFilters.FILTER_CARD_BASIC_LAND), true, true).setText("Search your library for a basic land card, reveal it, put it into your hand, then shuffle")); // or return target creature card from your graveyard to your hand; Mode mode = new Mode(); diff --git a/Mage.Sets/src/mage/cards/e/ExavaRakdosBloodWitch.java b/Mage.Sets/src/mage/cards/e/ExavaRakdosBloodWitch.java index 937fe1f51c96..b32e9dab48ab 100644 --- a/Mage.Sets/src/mage/cards/e/ExavaRakdosBloodWitch.java +++ b/Mage.Sets/src/mage/cards/e/ExavaRakdosBloodWitch.java @@ -13,7 +13,7 @@ import mage.constants.*; import mage.counters.CounterType; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/e/ExcavatedWall.java b/Mage.Sets/src/mage/cards/e/ExcavatedWall.java new file mode 100644 index 000000000000..ff05a89d8bbc --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ExcavatedWall.java @@ -0,0 +1,48 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.MillCardsControllerEffect; +import mage.abilities.keyword.DefenderAbility; +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 ExcavatedWall extends CardImpl { + + public ExcavatedWall(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}"); + + this.subtype.add(SubType.WALL); + this.power = new MageInt(0); + this.toughness = new MageInt(4); + + // Defender + this.addAbility(DefenderAbility.getInstance()); + + // {1}, {T}: Mill a card. + Ability ability = new SimpleActivatedAbility( + new MillCardsControllerEffect(1), new GenericManaCost(1) + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private ExcavatedWall(final ExcavatedWall card) { + super(card); + } + + @Override + public ExcavatedWall copy() { + return new ExcavatedWall(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/ExcavationTechnique.java b/Mage.Sets/src/mage/cards/e/ExcavationTechnique.java new file mode 100644 index 000000000000..151d00b289d1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ExcavationTechnique.java @@ -0,0 +1,69 @@ +package mage.cards.e; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.DemonstrateAbility; +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.target.common.TargetNonlandPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ExcavationTechnique extends CardImpl { + + public ExcavationTechnique(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{W}"); + + // Demonstrate + this.addAbility(new DemonstrateAbility()); + + // Destroy target nonland permanent. Its controller creates two Treasure tokens. + this.getSpellAbility().addEffect(new ExcavationTechniqueEffect()); + this.getSpellAbility().addTarget(new TargetNonlandPermanent()); + } + + private ExcavationTechnique(final ExcavationTechnique card) { + super(card); + } + + @Override + public ExcavationTechnique copy() { + return new ExcavationTechnique(this); + } +} + +class ExcavationTechniqueEffect extends OneShotEffect { + + ExcavationTechniqueEffect() { + super(Outcome.Benefit); + staticText = "destroy target nonland permanent. Its controller creates two Treasure tokens"; + } + + private ExcavationTechniqueEffect(final ExcavationTechniqueEffect effect) { + super(effect); + } + + @Override + public ExcavationTechniqueEffect copy() { + return new ExcavationTechniqueEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + permanent.destroy(source, game, false); + new TreasureToken().putOntoBattlefield(2, game, source, permanent.getControllerId()); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/e/ExecutionersSwing.java b/Mage.Sets/src/mage/cards/e/ExecutionersSwing.java index d2c400be81ca..48a2482ea531 100644 --- a/Mage.Sets/src/mage/cards/e/ExecutionersSwing.java +++ b/Mage.Sets/src/mage/cards/e/ExecutionersSwing.java @@ -1,4 +1,3 @@ - package mage.cards.e; import java.util.HashSet; @@ -11,7 +10,10 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; +import mage.filter.FilterPermanent; import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; @@ -23,12 +25,13 @@ */ public final class ExecutionersSwing extends CardImpl { + private static final FilterPermanent filter=new FilterCreaturePermanent("creature that dealt damage this turn");static {filter.add(ExecutionersSwingPredicate.instance);} public ExecutionersSwing(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}{B}"); // Target creature that dealt damage this turn gets -5/-5 until end of turn. this.getSpellAbility().addEffect(new BoostTargetEffect(-5, -5, Duration.EndOfTurn)); - this.getSpellAbility().addTarget(new TargetCreaturePermanentThatDealtDamageThisTurn()); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); this.getSpellAbility().addWatcher(new SourceDidDamageWatcher()); } @@ -42,71 +45,12 @@ public ExecutionersSwing copy() { return new ExecutionersSwing(this); } } - -class TargetCreaturePermanentThatDealtDamageThisTurn extends TargetPermanent { - - public TargetCreaturePermanentThatDealtDamageThisTurn() { - super(1, 1, StaticFilters.FILTER_PERMANENT_CREATURE, false); - targetName = "creature that dealt damage this turn"; - } - - public TargetCreaturePermanentThatDealtDamageThisTurn(final TargetCreaturePermanentThatDealtDamageThisTurn target) { - super(target); - } - - @Override - public boolean canTarget(UUID id, Ability source, Game game) { - SourceDidDamageWatcher watcher = game.getState().getWatcher(SourceDidDamageWatcher.class); - if (watcher != null) { - if (watcher.damageSources.contains(id)) { - return super.canTarget(id, source, game); - } - } - return false; - } - - @Override - public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { - int remainingTargets = this.minNumberOfTargets - targets.size(); - if (remainingTargets <= 0) { - return true; - } - int count = 0; - MageObject targetSource = game.getObject(sourceId); - SourceDidDamageWatcher watcher = game.getState().getWatcher(SourceDidDamageWatcher.class); - if (watcher != null && targetSource != null) { - for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, sourceId, game)) { - if (!targets.containsKey(permanent.getId()) && watcher.damageSources.contains(permanent.getId())) { - if (!notTarget || permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) { - count++; - if (count >= remainingTargets) { - return true; - } - } - } - } - } - return false; - } +enum ExecutionersSwingPredicate implements Predicate{ + instance ; @Override - public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { - Set availablePossibleTargets = super.possibleTargets(sourceId, sourceControllerId, game); - Set possibleTargets = new HashSet<>(); - SourceDidDamageWatcher watcher = game.getState().getWatcher(SourceDidDamageWatcher.class); - if (watcher != null) { - for (UUID targetId : availablePossibleTargets) { - Permanent permanent = game.getPermanent(targetId); - if (permanent != null && !targets.containsKey(permanent.getId()) && watcher.damageSources.contains(targetId)) { - possibleTargets.add(targetId); - } - } - } - return possibleTargets; + public boolean apply(Permanent input, Game game) { + SourceDidDamageWatcher watcher=game.getState().getWatcher(SourceDidDamageWatcher.class); + return watcher!=null&&watcher.checkSource(input,game); } - - @Override - public TargetCreaturePermanentThatDealtDamageThisTurn copy() { - return new TargetCreaturePermanentThatDealtDamageThisTurn(this); - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/e/ExhilaratingElocution.java b/Mage.Sets/src/mage/cards/e/ExhilaratingElocution.java new file mode 100644 index 000000000000..3574c5d0e008 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ExhilaratingElocution.java @@ -0,0 +1,74 @@ +package mage.cards.e; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.MageObjectReferencePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ExhilaratingElocution extends CardImpl { + + public ExhilaratingElocution(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{W}{B}"); + + // Put two +1/+1 counters on target creature you control. Other creatures you control get +1/+1 until end of turn. + this.getSpellAbility().addEffect(new ExhilaratingElocutionEffect()); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + } + + private ExhilaratingElocution(final ExhilaratingElocution card) { + super(card); + } + + @Override + public ExhilaratingElocution copy() { + return new ExhilaratingElocution(this); + } +} + +class ExhilaratingElocutionEffect extends OneShotEffect { + + ExhilaratingElocutionEffect() { + super(Outcome.Benefit); + staticText = "put two +1/+1 counters on target creature you control. " + + "Other creatures you control get +1/+1 until end of turn"; + } + + private ExhilaratingElocutionEffect(final ExhilaratingElocutionEffect effect) { + super(effect); + } + + @Override + public ExhilaratingElocutionEffect copy() { + return new ExhilaratingElocutionEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + permanent.addCounters(CounterType.P1P1.createInstance(2), source.getControllerId(), source, game); + FilterCreaturePermanent filterPermanent = new FilterCreaturePermanent(); + filterPermanent.add(Predicates.not(new MageObjectReferencePredicate(new MageObjectReference(permanent, game)))); + game.addEffect(new BoostControlledEffect(1, 1, Duration.EndOfTurn, filterPermanent), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/e/Exhume.java b/Mage.Sets/src/mage/cards/e/Exhume.java index f2456266ad91..e1d5724ca383 100644 --- a/Mage.Sets/src/mage/cards/e/Exhume.java +++ b/Mage.Sets/src/mage/cards/e/Exhume.java @@ -12,7 +12,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInGraveyard; diff --git a/Mage.Sets/src/mage/cards/e/ExileIntoDarkness.java b/Mage.Sets/src/mage/cards/e/ExileIntoDarkness.java index c9d5faf0bd35..8067eb46fa01 100644 --- a/Mage.Sets/src/mage/cards/e/ExileIntoDarkness.java +++ b/Mage.Sets/src/mage/cards/e/ExileIntoDarkness.java @@ -15,7 +15,7 @@ import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetPlayer; /** @@ -24,10 +24,10 @@ */ public final class ExileIntoDarkness extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with converted mana cost 3 or less"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with mana value 3 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public ExileIntoDarkness(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/e/ExpandedAnatomy.java b/Mage.Sets/src/mage/cards/e/ExpandedAnatomy.java new file mode 100644 index 000000000000..7ccbd04ab477 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ExpandedAnatomy.java @@ -0,0 +1,42 @@ +package mage.cards.e; + +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ExpandedAnatomy extends CardImpl { + + public ExpandedAnatomy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}"); + + this.subtype.add(SubType.LESSON); + + // Put two +1/+1 counters on target creature. It gains vigilance until end of turn. + this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance(2))); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + VigilanceAbility.getInstance(), Duration.EndOfTurn + ).setText("It gains vigilance until end of turn")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private ExpandedAnatomy(final ExpandedAnatomy card) { + super(card); + } + + @Override + public ExpandedAnatomy copy() { + return new ExpandedAnatomy(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/ExpansionExplosion.java b/Mage.Sets/src/mage/cards/e/ExpansionExplosion.java index ea59e7fb4f29..0ced362fc264 100644 --- a/Mage.Sets/src/mage/cards/e/ExpansionExplosion.java +++ b/Mage.Sets/src/mage/cards/e/ExpansionExplosion.java @@ -14,7 +14,7 @@ import mage.constants.SpellAbilityType; import mage.filter.FilterSpell; import mage.filter.common.FilterInstantOrSorcerySpell; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; @@ -30,10 +30,10 @@ public final class ExpansionExplosion extends SplitCard { private static final FilterSpell filter - = new FilterInstantOrSorcerySpell("instant or sorcery spell with converted mana cost 4 or less"); + = new FilterInstantOrSorcerySpell("instant or sorcery spell with mana value 4 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 5)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 5)); } public ExpansionExplosion(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/e/ExpeditionChampion.java b/Mage.Sets/src/mage/cards/e/ExpeditionChampion.java index 8ab529cbf3bd..bb2b692ad9ba 100644 --- a/Mage.Sets/src/mage/cards/e/ExpeditionChampion.java +++ b/Mage.Sets/src/mage/cards/e/ExpeditionChampion.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/e/ExpeditionDiviner.java b/Mage.Sets/src/mage/cards/e/ExpeditionDiviner.java index bc6c96b825d8..1ded2293510f 100644 --- a/Mage.Sets/src/mage/cards/e/ExpeditionDiviner.java +++ b/Mage.Sets/src/mage/cards/e/ExpeditionDiviner.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; import mage.abilities.common.DiesSourceTriggeredAbility; diff --git a/Mage.Sets/src/mage/cards/e/ExpeditionHealer.java b/Mage.Sets/src/mage/cards/e/ExpeditionHealer.java index 9c27b3012263..58d1edc6e904 100644 --- a/Mage.Sets/src/mage/cards/e/ExpeditionHealer.java +++ b/Mage.Sets/src/mage/cards/e/ExpeditionHealer.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/e/ExpeditionSkulker.java b/Mage.Sets/src/mage/cards/e/ExpeditionSkulker.java index 839959e69635..6053d63cab7e 100644 --- a/Mage.Sets/src/mage/cards/e/ExpeditionSkulker.java +++ b/Mage.Sets/src/mage/cards/e/ExpeditionSkulker.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/e/Expel.java b/Mage.Sets/src/mage/cards/e/Expel.java new file mode 100644 index 000000000000..2170007b3c69 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/Expel.java @@ -0,0 +1,41 @@ +package mage.cards.e; + +import mage.abilities.effects.common.ExileTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Expel extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("tapped creature"); + + static { + filter.add(TappedPredicate.instance); + } + + public Expel(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}"); + + // Exile target tapped creature. + this.getSpellAbility().addEffect(new ExileTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + } + + private Expel(final Expel card) { + super(card); + } + + @Override + public Expel copy() { + return new Expel(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/ExperimentKraj.java b/Mage.Sets/src/mage/cards/e/ExperimentKraj.java index f53df9584857..b6df994db9e9 100644 --- a/Mage.Sets/src/mage/cards/e/ExperimentKraj.java +++ b/Mage.Sets/src/mage/cards/e/ExperimentKraj.java @@ -15,7 +15,7 @@ import mage.constants.*; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/e/ExperimentalFrenzy.java b/Mage.Sets/src/mage/cards/e/ExperimentalFrenzy.java index e4d6d07d2599..c376398c33f4 100644 --- a/Mage.Sets/src/mage/cards/e/ExperimentalFrenzy.java +++ b/Mage.Sets/src/mage/cards/e/ExperimentalFrenzy.java @@ -30,7 +30,7 @@ public ExperimentalFrenzy(UUID ownerId, CardSetInfo setInfo) { // You may look at the top card of your library any time. this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect())); - // You may play the top card of your library. + // You may play lands and cast spells from the top of your library. this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect())); // You can't play cards from your hand. diff --git a/Mage.Sets/src/mage/cards/e/ExperimentalOverload.java b/Mage.Sets/src/mage/cards/e/ExperimentalOverload.java index 76321ab38a82..b9e69b9f42b5 100644 --- a/Mage.Sets/src/mage/cards/e/ExperimentalOverload.java +++ b/Mage.Sets/src/mage/cards/e/ExperimentalOverload.java @@ -27,7 +27,7 @@ public ExperimentalOverload(UUID ownerId, CardSetInfo setInfo) { // Create an X/X blue and red Weird creature token, where X is the number of instant and sorcery cards in your graveyard. Then you may return an instant or sorcery card from your graveyard to your hand. Exile Experimental Overload. this.getSpellAbility().addEffect(new ExperimentalOverloadEffect()); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private ExperimentalOverload(final ExperimentalOverload card) { diff --git a/Mage.Sets/src/mage/cards/e/ExplosiveRevelation.java b/Mage.Sets/src/mage/cards/e/ExplosiveRevelation.java index 98172b43fd19..5957cac229cf 100644 --- a/Mage.Sets/src/mage/cards/e/ExplosiveRevelation.java +++ b/Mage.Sets/src/mage/cards/e/ExplosiveRevelation.java @@ -45,7 +45,7 @@ class ExplosiveRevelationEffect extends OneShotEffect { public ExplosiveRevelationEffect() { super(Outcome.DrawCard); - this.staticText = "Choose any target. Reveal cards from the top of your library until you reveal a nonland card, {this} deals damage equal to that card's converted mana cost to that permanent or player. Put the nonland card into your hand and the rest on the bottom of your library in any order"; + this.staticText = "Choose any target. Reveal cards from the top of your library until you reveal a nonland card, {this} deals damage equal to that card's mana value to that permanent or player. Put the nonland card into your hand and the rest on the bottom of your library in any order"; } public ExplosiveRevelationEffect(final ExplosiveRevelationEffect effect) { @@ -76,7 +76,7 @@ public boolean apply(Game game, Ability source) { // reveal cards controller.revealCards(sourceObject.getIdName(), toReveal, game); // the nonland card - int damage = nonLandCard == null ? 0 : nonLandCard.getConvertedManaCost(); + int damage = nonLandCard == null ? 0 : nonLandCard.getManaValue(); // assign damage to target for (UUID targetId : targetPointer.getTargets(game, source)) { Permanent targetedCreature = game.getPermanent(targetId); diff --git a/Mage.Sets/src/mage/cards/e/ExplosiveWelcome.java b/Mage.Sets/src/mage/cards/e/ExplosiveWelcome.java new file mode 100644 index 000000000000..f84c93da93ec --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ExplosiveWelcome.java @@ -0,0 +1,56 @@ +package mage.cards.e; + +import mage.Mana; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.mana.BasicManaEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ManaType; +import mage.filter.common.FilterCreaturePlayerOrPlaneswalker; +import mage.filter.predicate.other.AnotherTargetPredicate; +import mage.target.Target; +import mage.target.common.TargetAnyTarget; +import mage.target.targetpointer.SecondTargetPointer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ExplosiveWelcome extends CardImpl { + + private static final FilterCreaturePlayerOrPlaneswalker filter = new FilterCreaturePlayerOrPlaneswalker(); + + static { + filter.add(new AnotherTargetPredicate(2)); + } + + public ExplosiveWelcome(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{7}{R}"); + + // Explosive Welcome deals 5 damage to any target and 3 damage to any other target. Add {R}{R}{R}. + this.getSpellAbility().addEffect(new DamageTargetEffect(5)); + this.getSpellAbility().addEffect( + new DamageTargetEffect(3) + .setTargetPointer(new SecondTargetPointer()) + .setText("and 3 damage to any other target.") + ); + this.getSpellAbility().addEffect(new BasicManaEffect(new Mana(ManaType.RED, 3))); + Target target = new TargetAnyTarget(); + target.setTargetTag(1); + this.getSpellAbility().addTarget(target); + target = new TargetAnyTarget(filter); + target.setTargetTag(2); + this.getSpellAbility().addTarget(target); + } + + private ExplosiveWelcome(final ExplosiveWelcome card) { + super(card); + } + + @Override + public ExplosiveWelcome copy() { + return new ExplosiveWelcome(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/ExponentialGrowth.java b/Mage.Sets/src/mage/cards/e/ExponentialGrowth.java new file mode 100644 index 000000000000..7702eef86faa --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ExponentialGrowth.java @@ -0,0 +1,74 @@ +package mage.cards.e; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ExponentialGrowth extends CardImpl { + + public ExponentialGrowth(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{X}{G}{G}"); + + // Until end of turn, double target creature's power X times. + this.getSpellAbility().addEffect(new ExponentialGrowthEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private ExponentialGrowth(final ExponentialGrowth card) { + super(card); + } + + @Override + public ExponentialGrowth copy() { + return new ExponentialGrowth(this); + } +} + +class ExponentialGrowthEffect extends OneShotEffect { + + ExponentialGrowthEffect() { + super(Outcome.Benefit); + staticText = "until end of turn, double target creature's power X times"; + } + + private ExponentialGrowthEffect(final ExponentialGrowthEffect effect) { + super(effect); + } + + @Override + public ExponentialGrowthEffect copy() { + return new ExponentialGrowthEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + int xValue = source.getManaCostsToPay().getX(); + int multiplier = 1; + for (int i = 0; i < xValue; i++) { + multiplier = CardUtil.overflowMultiply(multiplier, 2); + } + multiplier = CardUtil.overflowDec(multiplier, 1); + game.addEffect(new BoostTargetEffect(CardUtil.overflowMultiply( + multiplier, permanent.getPower().getValue() + ), 0, Duration.EndOfTurn), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/e/ExpressiveIteration.java b/Mage.Sets/src/mage/cards/e/ExpressiveIteration.java new file mode 100644 index 000000000000..58ce134c353f --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ExpressiveIteration.java @@ -0,0 +1,110 @@ +package mage.cards.e; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInLibrary; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ExpressiveIteration extends CardImpl { + + public ExpressiveIteration(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{U}{R}"); + + // Look at the top three cards of your library. Put one of them into your hand, put one of them on the bottom of your library, and exile one of them. You may play the exiled card this turn. + this.getSpellAbility().addEffect(new ExpressiveIterationEffect()); + } + + private ExpressiveIteration(final ExpressiveIteration card) { + super(card); + } + + @Override + public ExpressiveIteration copy() { + return new ExpressiveIteration(this); + } +} + +class ExpressiveIterationEffect extends OneShotEffect { + + ExpressiveIterationEffect() { + super(Outcome.Benefit); + staticText = "look at the top three cards of your library. Put one of them into your hand, put one of them " + + "on the bottom of your library, and exile one of them. You may play the exiled card this turn"; + } + + private ExpressiveIterationEffect(final ExpressiveIterationEffect effect) { + super(effect); + } + + @Override + public ExpressiveIterationEffect copy() { + return new ExpressiveIterationEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObject(game); + if (player == null || sourceObject == null) { + return false; + } + Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 3)); + if (cards.size() < 1) { + return false; + } + TargetCard target = new TargetCardInLibrary( + cards.size() == 3 ? 1 : 0, 1, StaticFilters.FILTER_CARD + ); + target.withChooseHint("To put into your hand"); + player.choose(outcome, cards, target, game); + Card card = game.getCard(target.getFirstTarget()); + if (card != null) { + player.moveCards(card, Zone.HAND, source, game); + cards.remove(card); + } + if (cards.isEmpty()) { + return true; + } + target = new TargetCardInLibrary( + cards.size() == 2 ? 1 : 0, 1, StaticFilters.FILTER_CARD + ); + target.withChooseHint("To put on the bottom of your library"); + player.choose(outcome, cards, target, game); + card = game.getCard(target.getFirstTarget()); + if (card != null) { + player.putCardsOnBottomOfLibrary(card, game, source, false); + cards.remove(card); + } + if (cards.isEmpty()) { + return true; + } + target = new TargetCardInLibrary(); + target.withChooseHint("To exile (you may play it this turn)"); + player.choose(outcome, cards, target, game); + card = game.getCard(target.getFirstTarget()); + if (card == null) { + return true; + } + player.moveCardsToExile(card, source, game, true, source.getSourceId(), sourceObject.getIdName()); + game.addEffect(new PlayFromNotOwnHandZoneTargetEffect( + Zone.EXILED, Duration.EndOfTurn + ).setTargetPointer(new FixedTarget(card, game)), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/e/Expropriate.java b/Mage.Sets/src/mage/cards/e/Expropriate.java index 34c153dd279e..04aa42b4f37a 100644 --- a/Mage.Sets/src/mage/cards/e/Expropriate.java +++ b/Mage.Sets/src/mage/cards/e/Expropriate.java @@ -1,28 +1,31 @@ package mage.cards.e; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.ContinuousEffectImpl; -import mage.abilities.effects.common.CouncilsDilemmaVoteEffect; +import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileSpellEffect; +import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.choices.TwoChoiceVote; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; import mage.filter.FilterPermanent; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.turn.TurnMod; import mage.players.Player; -import mage.target.Target; import mage.target.TargetPermanent; -import mage.target.targetpointer.FixedTarget; +import mage.target.targetpointer.FixedTargets; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.UUID; /** - * @author JRHerlehy + * @author JRHerlehy, TheElk801 */ public final class Expropriate extends CardImpl { @@ -31,8 +34,8 @@ public Expropriate(UUID ownerId, CardSetInfo setInfo) { // Council's dilemma — Starting with you, each player votes for time or money. For each time vote, // take an extra turn after this one. For each money vote, choose a permanent owned by the voter and gain control of it. Exile Expropriate - this.getSpellAbility().addEffect(new ExpropriateDilemmaEffect()); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExpropriateEffect()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private Expropriate(final Expropriate card) { @@ -45,138 +48,69 @@ public Expropriate copy() { } } -class ExpropriateDilemmaEffect extends CouncilsDilemmaVoteEffect { +class ExpropriateEffect extends OneShotEffect { - private ArrayList choiceTwoVoters = new ArrayList<>(); - - public ExpropriateDilemmaEffect() { + ExpropriateEffect() { super(Outcome.Benefit); - this.staticText = "Council's dilemma — Starting with you, each player votes for time or money. For each time vote, take an extra turn after this one. For each money vote, choose a permanent owned by the voter and gain control of it"; + staticText = "Council's dilemma — Starting with you, each player votes for time or money. " + + "For each time vote, take an extra turn after this one. For each money vote, " + + "choose a permanent owned by the voter and gain control of it"; } - public ExpropriateDilemmaEffect(final ExpropriateDilemmaEffect effect) { + private ExpropriateEffect(final ExpropriateEffect effect) { super(effect); - this.choiceTwoVoters.addAll(effect.choiceTwoVoters); } @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); + public ExpropriateEffect copy() { + return new ExpropriateEffect(this); + } - //If not controller, exit out here and do not vote. - if (controller == null) { + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { return false; } - this.vote("time", "money", controller, game, source); - - //Time Votes - if (voteOneCount > 0) { - this.turnsForTimeVote(voteOneCount, controller, game, source); - } + // Outcome.Detriment - AI will gain control all the time (Money choice) + // TODO: add AI hint logic in the choice method, see Tyrant's Choice as example + TwoChoiceVote vote = new TwoChoiceVote("Time (extra turn)", "Money (gain control)", Outcome.Detriment); + vote.doVotes(source, game); - //Money Votes - if (voteTwoCount > 0) { - this.controlForMoneyVote(controller, game, source); - } - - return true; - } - - private void turnsForTimeVote(int timeCount, Player controller, Game game, Ability source) { - if (timeCount == 1) { - game.informPlayers(controller.getLogName() + " will take an extra turn"); - } else { - game.informPlayers(controller.getLogName() + " will take " + timeCount + " extra turns"); - } - do { + // extra turn + int timeCount = vote.getVoteCount(true); + for (int i = 0; i < timeCount; i++) { game.getState().getTurnMods().add(new TurnMod(source.getControllerId(), false)); - timeCount--; - } while (timeCount > 0); - } - - private void controlForMoneyVote(Player controller, Game game, Ability source) { - List chosenCards = new ArrayList<>(); - - for (UUID playerId : choiceTwoVoters) { - FilterPermanent filter = new FilterPermanent("permanent owned by " + game.getPlayer(playerId).getName()); - filter.add(new OwnerIdPredicate(playerId)); - - Target target = new TargetPermanent(filter); - target.setNotTarget(true); - - if (controller != null - && controller.chooseTarget(Outcome.GainControl, target, source, game)) { - Permanent targetPermanent = game.getPermanent(target.getFirstTarget()); - - if (targetPermanent != null) { - chosenCards.add(targetPermanent); - } - } - } - if (controller != null) { - for (Permanent permanent : chosenCards) { - ContinuousEffect effect = new ExpropriateControlEffect(controller.getId()); - effect.setTargetPointer(new FixedTarget(permanent, game)); - game.addEffect(effect, source); - game.informPlayers(controller.getLogName() + " gained control of " + permanent.getIdName() + " owned by " + game.getPlayer(permanent.getOwnerId()).getName()); - } } - } - @Override - protected void vote(String choiceOne, String choiceTwo, Player controller, Game game, Ability source) { - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - if (player.chooseUse(Outcome.Vote, - "Choose " + choiceOne + " or " + choiceTwo + "?", - source.getRule(), choiceOne, choiceTwo, source, game)) { - voteOneCount++; - game.informPlayers(player.getLogName() + " has voted for " + choiceOne); - } else { - voteTwoCount++; - game.informPlayers(player.getLogName() + " has voted for " + choiceTwo); - choiceTwoVoters.add(player.getId()); - } - } + // gain control + if (vote.getVoteCount(false) < 1) { + return true; } - } - - @Override - public ExpropriateDilemmaEffect copy() { - return new ExpropriateDilemmaEffect(this); - } - -} - -class ExpropriateControlEffect extends ContinuousEffectImpl { - - private final UUID controllerId; - - public ExpropriateControlEffect(UUID controllerId) { - super(Duration.EndOfGame, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); - this.controllerId = controllerId; - } - - public ExpropriateControlEffect(final ExpropriateControlEffect effect) { - super(effect); - this.controllerId = effect.controllerId; - } - - @Override - public ExpropriateControlEffect copy() { - return new ExpropriateControlEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source)); - if (permanent == null || controllerId == null) { - this.discard(); - } else { - permanent.changeControllerId(controllerId, game, source); + List toSteal = new ArrayList<>(); + for (UUID playerId : vote.getVotedFor(false)) { + int moneyCount = vote.getVotes(playerId).stream().mapToInt(x -> x ? 0 : 1).sum(); + FilterPermanent filter = new FilterPermanent(); + filter.add(new ControllerIdPredicate(playerId)); + moneyCount = Math.min(game.getBattlefield().count( + filter, source.getSourceId(), source.getControllerId(), game + ), moneyCount); + if (moneyCount == 0) { + continue; + } + TargetPermanent target = new TargetPermanent(moneyCount, filter); + target.setNotTarget(true); + player.choose(Outcome.GainControl, target, source.getSourceId(), game); + target.getTargets() + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .forEach(toSteal::add); } + game.addEffect(new GainControlTargetEffect( + Duration.Custom, true, source.getControllerId() + ).setTargetPointer(new FixedTargets(toSteal, game)), source); return true; } } diff --git a/Mage.Sets/src/mage/cards/e/ExquisiteFirecraft.java b/Mage.Sets/src/mage/cards/e/ExquisiteFirecraft.java index d67f2dc52aa5..19ca998c2945 100644 --- a/Mage.Sets/src/mage/cards/e/ExquisiteFirecraft.java +++ b/Mage.Sets/src/mage/cards/e/ExquisiteFirecraft.java @@ -31,7 +31,7 @@ public ExquisiteFirecraft(UUID ownerId, CardSetInfo setInfo) { // Spell mastery — If there are two or more instant and/or sorcery cards in your graveyard, Exquisite Firecraft can't be countered. ContinuousRuleModifyingEffect cantBeCountered = new CantBeCounteredSourceEffect(); ConditionalContinuousRuleModifyingEffect conditionalCantBeCountered = new ConditionalContinuousRuleModifyingEffect(cantBeCountered, SpellMasteryCondition.instance); - conditionalCantBeCountered.setText("
If there are two or more instant and/or sorcery cards in your graveyard, this spell can't be countered"); + conditionalCantBeCountered.setText("
Spell mastery — If there are two or more instant and/or sorcery cards in your graveyard, this spell can't be countered"); Ability ability = new SimpleStaticAbility(Zone.STACK, conditionalCantBeCountered); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/e/ExtinctionEvent.java b/Mage.Sets/src/mage/cards/e/ExtinctionEvent.java index ac43a229a194..875a24b820d4 100644 --- a/Mage.Sets/src/mage/cards/e/ExtinctionEvent.java +++ b/Mage.Sets/src/mage/cards/e/ExtinctionEvent.java @@ -9,7 +9,7 @@ import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostParityPredicate; +import mage.filter.predicate.mageobject.ManaValueParityPredicate; import mage.game.Game; import mage.players.Player; @@ -44,13 +44,13 @@ class ExtinctionEventEffect extends OneShotEffect { private static final FilterPermanent oddFilter = new FilterCreaturePermanent(); static { - evenFilter.add(ConvertedManaCostParityPredicate.EVEN); - oddFilter.add(ConvertedManaCostParityPredicate.ODD); + evenFilter.add(ManaValueParityPredicate.EVEN); + oddFilter.add(ManaValueParityPredicate.ODD); } ExtinctionEventEffect() { super(Outcome.Benefit); - staticText = "Choose odd or even. Exile each creature with converted mana cost of the chosen value. (Zero is even.)"; + staticText = "Choose odd or even. Exile each creature with mana value of the chosen quality. (Zero is even.)"; } private ExtinctionEventEffect(final ExtinctionEventEffect effect) { diff --git a/Mage.Sets/src/mage/cards/e/Extirpate.java b/Mage.Sets/src/mage/cards/e/Extirpate.java index 34c41914cd69..511bc3784965 100644 --- a/Mage.Sets/src/mage/cards/e/Extirpate.java +++ b/Mage.Sets/src/mage/cards/e/Extirpate.java @@ -64,7 +64,7 @@ public ExtirpateEffect() { this.staticText = "Choose target card in a graveyard other than " + "a basic land card. Search its owner's graveyard, hand, " + "and library for any number of cards with the same name " - + "as that card and exile them. Then that player shuffles their library"; + + "as that card and exile them. Then that player shuffles"; } public ExtirpateEffect(final ExtirpateEffect effect) { diff --git a/Mage.Sets/src/mage/cards/e/Extract.java b/Mage.Sets/src/mage/cards/e/Extract.java index acf4c0e77c07..ee436168bb5b 100644 --- a/Mage.Sets/src/mage/cards/e/Extract.java +++ b/Mage.Sets/src/mage/cards/e/Extract.java @@ -48,7 +48,7 @@ class ExtractEffect extends OneShotEffect { public ExtractEffect() { super(Outcome.Exile); - staticText = "Search target player's library for a card and exile it. Then that player shuffles their library."; + staticText = "Search target player's library for a card and exile it. Then that player shuffles."; } public ExtractEffect(final ExtractEffect effect) { diff --git a/Mage.Sets/src/mage/cards/e/ExtractorDemon.java b/Mage.Sets/src/mage/cards/e/ExtractorDemon.java index 298ec1f30a27..bf23831df86b 100644 --- a/Mage.Sets/src/mage/cards/e/ExtractorDemon.java +++ b/Mage.Sets/src/mage/cards/e/ExtractorDemon.java @@ -14,7 +14,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPlayer; /** diff --git a/Mage.Sets/src/mage/cards/e/ExtricatorOfSin.java b/Mage.Sets/src/mage/cards/e/ExtricatorOfSin.java index baef68c05c3a..57876a106a7d 100644 --- a/Mage.Sets/src/mage/cards/e/ExtricatorOfSin.java +++ b/Mage.Sets/src/mage/cards/e/ExtricatorOfSin.java @@ -20,7 +20,7 @@ import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.EldraziHorrorToken; import mage.target.common.TargetControlledPermanent; diff --git a/Mage.Sets/src/mage/cards/e/ExtusOriqOverlord.java b/Mage.Sets/src/mage/cards/e/ExtusOriqOverlord.java new file mode 100644 index 000000000000..72c64a97f29b --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ExtusOriqOverlord.java @@ -0,0 +1,132 @@ +package mage.cards.e; + +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.SacrificeXTargetCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.effects.common.SacrificeOpponentsEffect; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.permanent.token.BloodAvatarToken; +import mage.target.common.TargetCardInYourGraveyard; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ExtusOriqOverlord extends ModalDoubleFacesCard { + + private static final FilterCard filter + = new FilterCreatureCard("nonlegendary creature card from your graveyard"); + + static { + filter.add(Predicates.not(SuperType.LEGENDARY.getPredicate())); + } + + public ExtusOriqOverlord(UUID ownerId, CardSetInfo setInfo) { + super( + ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.HUMAN, SubType.WARLOCK}, "{1}{W}{B}{B}", + "Awaken the Blood Avatar", + new CardType[]{CardType.SORCERY}, new SubType[]{}, "{6}{B}{R}" + ); + + // 1. + // Extus, Oriq Overlord + // Legendary Creature - Human Warlock + this.getLeftHalfCard().addSuperType(SuperType.LEGENDARY); + this.getLeftHalfCard().setPT(2, 4); + + // Double strike + this.getLeftHalfCard().addAbility(DoubleStrikeAbility.getInstance()); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, return target nonlegendary creature card from your graveyard to your hand. + Ability ability = new MagecraftAbility(new ReturnFromGraveyardToHandTargetEffect()); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.getLeftHalfCard().addAbility(ability); + + // 2. + // Awaken the Blood Avatar + // Sorcery + // As an additional cost to cast this spell, you may sacrifice any number of creatures. This spell costs {2} less to cast for each creature sacrificed this way. + Cost cost = new SacrificeXTargetCost(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT); + cost.setText("As an additional cost to cast this spell, you may sacrifice any number of creatures. " + + "This spell costs {2} less to cast for each creature sacrificed this way"); + this.getRightHalfCard().getSpellAbility().addCost(cost); + ability = new SimpleStaticAbility(Zone.ALL, new AwakenTheBloodAvatarCostReductionEffect()); + ability.setRuleVisible(false); + this.getRightHalfCard().addAbility(ability); + + // Each opponent sacrifices a creature. Create a 3/6 black and red Avatar creature token with haste and "Whenever this creature attacks, it deals 3 damage to each opponent." + this.getRightHalfCard().getSpellAbility().addEffect(new SacrificeOpponentsEffect( + StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT + )); + this.getRightHalfCard().getSpellAbility().addEffect(new CreateTokenEffect(new BloodAvatarToken())); + } + + private ExtusOriqOverlord(final ExtusOriqOverlord card) { + super(card); + } + + @Override + public ExtusOriqOverlord copy() { + return new ExtusOriqOverlord(this); + } +} + +class AwakenTheBloodAvatarCostReductionEffect extends CostModificationEffectImpl { + + AwakenTheBloodAvatarCostReductionEffect() { + super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST); + } + + private AwakenTheBloodAvatarCostReductionEffect(final AwakenTheBloodAvatarCostReductionEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + SpellAbility spellAbility = (SpellAbility) abilityToModify; + for (Cost cost : spellAbility.getCosts()) { + if (!(cost instanceof SacrificeXTargetCost)) { + continue; + } + if (game.inCheckPlayableState()) { + // allows to cast in getPlayable + int reduction = ((SacrificeXTargetCost) cost).getMaxValue(spellAbility, game); + CardUtil.adjustCost(spellAbility, reduction * 2); + } else { + // real cast + int reduction = ((SacrificeXTargetCost) cost).getAmount(); + CardUtil.adjustCost(spellAbility, reduction * 2); + } + + break; + } + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + return abilityToModify instanceof SpellAbility && abilityToModify.getSourceId().equals(source.getSourceId()); + } + + @Override + public AwakenTheBloodAvatarCostReductionEffect copy() { + return new AwakenTheBloodAvatarCostReductionEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EyeOfTheStorm.java b/Mage.Sets/src/mage/cards/e/EyeOfTheStorm.java index 668381f96c8f..164823125ea9 100644 --- a/Mage.Sets/src/mage/cards/e/EyeOfTheStorm.java +++ b/Mage.Sets/src/mage/cards/e/EyeOfTheStorm.java @@ -72,7 +72,7 @@ public boolean checkTrigger(GameEvent event, Game game) { && !spell.isCopy() && spell.getCard() != null && !spell.getCard().isCopy() - && (spell.isInstant() || spell.isSorcery())) { + && spell.isInstantOrSorcery()) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getTargetId())); } diff --git a/Mage.Sets/src/mage/cards/e/Eyetwitch.java b/Mage.Sets/src/mage/cards/e/Eyetwitch.java new file mode 100644 index 000000000000..335438d0d14d --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/Eyetwitch.java @@ -0,0 +1,42 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.common.LearnEffect; +import mage.abilities.keyword.FlyingAbility; +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 Eyetwitch extends CardImpl { + + public Eyetwitch(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}"); + + this.subtype.add(SubType.EYE); + this.subtype.add(SubType.BAT); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Eyetwitch dies, learn. + this.addAbility(new DiesSourceTriggeredAbility(new LearnEffect())); + } + + private Eyetwitch(final Eyetwitch card) { + super(card); + } + + @Override + public Eyetwitch copy() { + return new Eyetwitch(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EzuriClawOfProgress.java b/Mage.Sets/src/mage/cards/e/EzuriClawOfProgress.java index a1a39573d5c9..ca7fcb1ec3b9 100644 --- a/Mage.Sets/src/mage/cards/e/EzuriClawOfProgress.java +++ b/Mage.Sets/src/mage/cards/e/EzuriClawOfProgress.java @@ -14,7 +14,7 @@ import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/e/EzzarootChanneler.java b/Mage.Sets/src/mage/cards/e/EzzarootChanneler.java new file mode 100644 index 000000000000..9ab86356b56d --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EzzarootChanneler.java @@ -0,0 +1,90 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.dynamicvalue.common.ControllerGotLifeCount; +import mage.abilities.dynamicvalue.common.ControllerLifeCount; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.util.CardUtil; +import mage.watchers.common.PlayerGainedLifeWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EzzarootChanneler extends CardImpl { + + public EzzarootChanneler(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{G}"); + + this.subtype.add(SubType.TREEFOLK); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(4); + this.toughness = new MageInt(6); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Creature spells you cast cost {X} less to cast, where X is the amount of life you gained this turn. + this.addAbility( + new SimpleStaticAbility(new EzzarootChannelerEffect()) + .addHint(ControllerGotLifeCount.getHint()), + new PlayerGainedLifeWatcher() + ); + + // {T}: You gain 2 life. + this.addAbility(new SimpleActivatedAbility(new GainLifeEffect(2), new TapSourceCost())); + } + + private EzzarootChanneler(final EzzarootChanneler card) { + super(card); + } + + @Override + public EzzarootChanneler copy() { + return new EzzarootChanneler(this); + } +} + +class EzzarootChannelerEffect extends CostModificationEffectImpl { + + EzzarootChannelerEffect() { + super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST); + staticText = "creature spells you cast cost {X} less to cast, " + + "where X is the amount of life you gained this turn"; + } + + private EzzarootChannelerEffect(final EzzarootChannelerEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + CardUtil.reduceCost(abilityToModify, Math.max(0, ControllerLifeCount.instance.calculate(game, source, this))); + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + return abilityToModify instanceof SpellAbility + && abilityToModify.isControlledBy(source.getControllerId()) + && ((SpellAbility) abilityToModify).getCharacteristics(game).isCreature() + && game.getCard(abilityToModify.getSourceId()) != null; + } + + @Override + public EzzarootChannelerEffect copy() { + return new EzzarootChannelerEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FabledPassage.java b/Mage.Sets/src/mage/cards/f/FabledPassage.java index 7897263e3aa1..237de41a8212 100644 --- a/Mage.Sets/src/mage/cards/f/FabledPassage.java +++ b/Mage.Sets/src/mage/cards/f/FabledPassage.java @@ -48,7 +48,7 @@ class FabledPassageEffect extends OneShotEffect { FabledPassageEffect() { super(Outcome.Benefit); staticText = "Search your library for a basic land card, put it onto the battlefield tapped, " + - "then shuffle your library. Then if you control four or more lands, untap that land."; + "then shuffle. Then if you control four or more lands, untap that land."; } private FabledPassageEffect(final FabledPassageEffect effect) { diff --git a/Mage.Sets/src/mage/cards/f/Fabricate.java b/Mage.Sets/src/mage/cards/f/Fabricate.java index ca18d95f0cd9..b3daae45b9f9 100644 --- a/Mage.Sets/src/mage/cards/f/Fabricate.java +++ b/Mage.Sets/src/mage/cards/f/Fabricate.java @@ -1,32 +1,26 @@ - package mage.cards.f; -import java.util.UUID; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInLibrary; +import java.util.UUID; + /** - * * @author Loki */ public final class Fabricate extends CardImpl { - private static final FilterCard filter = new FilterCard("artifact"); - - static { - filter.add(CardType.ARTIFACT.getPredicate()); - } - public Fabricate(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}"); // Search your library for an artifact card, reveal it, and put it into your hand. Then shuffle your library. - this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(1, filter), true)); + this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect( + new TargetCardInLibrary(1, StaticFilters.FILTER_CARD_ARTIFACT), true + )); } private Fabricate(final Fabricate card) { diff --git a/Mage.Sets/src/mage/cards/f/FacelessButcher.java b/Mage.Sets/src/mage/cards/f/FacelessButcher.java index 05a2a4718167..de40203235ed 100644 --- a/Mage.Sets/src/mage/cards/f/FacelessButcher.java +++ b/Mage.Sets/src/mage/cards/f/FacelessButcher.java @@ -4,7 +4,6 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.ExileTargetForSourceEffect; import mage.abilities.effects.common.ReturnFromExileForSourceEffect; import mage.cards.CardImpl; @@ -13,8 +12,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; -import mage.target.Target; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/f/FacelessDevourer.java b/Mage.Sets/src/mage/cards/f/FacelessDevourer.java index 8ae88aa07e19..3cc83f2c458d 100644 --- a/Mage.Sets/src/mage/cards/f/FacelessDevourer.java +++ b/Mage.Sets/src/mage/cards/f/FacelessDevourer.java @@ -4,7 +4,6 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.ExileTargetForSourceEffect; import mage.abilities.effects.common.ReturnFromExileForSourceEffect; import mage.abilities.keyword.ShadowAbility; @@ -15,8 +14,7 @@ import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; -import mage.target.Target; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/f/FactOrFiction.java b/Mage.Sets/src/mage/cards/f/FactOrFiction.java index 05494640aaf8..28241804d67b 100644 --- a/Mage.Sets/src/mage/cards/f/FactOrFiction.java +++ b/Mage.Sets/src/mage/cards/f/FactOrFiction.java @@ -1,10 +1,5 @@ - package mage.cards.f; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; @@ -19,6 +14,11 @@ import mage.target.TargetCard; import mage.target.common.TargetOpponent; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.UUID; + /** * @author North */ @@ -43,12 +43,12 @@ public FactOrFiction copy() { class FactOrFictionEffect extends OneShotEffect { - public FactOrFictionEffect() { + FactOrFictionEffect() { super(Outcome.DrawCard); this.staticText = "Reveal the top five cards of your library. An opponent separates those cards into two piles. Put one pile into your hand and the other into your graveyard"; } - public FactOrFictionEffect(final FactOrFictionEffect effect) { + private FactOrFictionEffect(final FactOrFictionEffect effect) { super(effect); } @@ -110,8 +110,10 @@ public boolean apply(Game game, Ability source) { if (i < pile1.size()) { sb.append(", "); } - card.moveToZone(pile1Zone, source, game, false); } + cards.clear(); + cards.addAll(pile1); + controller.moveCards(cards, pile1Zone, source, game); game.informPlayers(sb.toString()); sb = new StringBuilder("Pile 2, going to ").append(pile2Zone == Zone.HAND ? "Hand" : "Graveyard").append(':'); @@ -122,8 +124,10 @@ public boolean apply(Game game, Ability source) { if (i < pile2.size()) { sb.append(", "); } - card.moveToZone(pile2Zone, source, game, false); } + cards.clear(); + cards.addAll(pile2); + controller.moveCards(cards, pile2Zone, source, game); game.informPlayers(sb.toString()); } diff --git a/Mage.Sets/src/mage/cards/f/FaerieImpostor.java b/Mage.Sets/src/mage/cards/f/FaerieImpostor.java index 9918cca5ca18..94dc8006f78b 100644 --- a/Mage.Sets/src/mage/cards/f/FaerieImpostor.java +++ b/Mage.Sets/src/mage/cards/f/FaerieImpostor.java @@ -1,34 +1,32 @@ - package mage.cards.f; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class FaerieImpostor extends CardImpl { public FaerieImpostor(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}"); this.subtype.add(SubType.FAERIE); this.subtype.add(SubType.ROGUE); @@ -66,36 +64,32 @@ class FaerieImpostorEffect extends OneShotEffect { staticText = effectText; } - FaerieImpostorEffect(FaerieImpostorEffect effect) { + private FaerieImpostorEffect(FaerieImpostorEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - boolean targetChosen = false; - TargetPermanent target = new TargetPermanent(1, 1, filter, true); - if (target.canChoose(source.getSourceId(), controller.getId(), game) && controller.chooseUse(outcome, "Return another creature you control to its owner's hand?", source, game)) { - controller.chooseTarget(Outcome.ReturnToHand, target, source, game); - Permanent permanent = game.getPermanent(target.getFirstTarget()); - if (permanent != null) { - targetChosen = true; - permanent.moveToZone(Zone.HAND, source, game, false); - } - } - - if (!targetChosen) { - new SacrificeSourceEffect().apply(game, source); + if (controller == null) { + return false; + } + TargetPermanent target = new TargetPermanent(1, 1, filter, true); + if (target.canChoose(source.getSourceId(), controller.getId(), game) + && controller.chooseUse(outcome, "Return another creature you control to its owner's hand?", source, game)) { + controller.chooseTarget(Outcome.ReturnToHand, target, source, game); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent != null) { + controller.moveCards(permanent, Zone.HAND, source, game); + return true; } - return true; } - return false; + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + return permanent != null && permanent.sacrifice(source, game); } @Override public FaerieImpostorEffect copy() { return new FaerieImpostorEffect(this); } - } diff --git a/Mage.Sets/src/mage/cards/f/FaerieMiscreant.java b/Mage.Sets/src/mage/cards/f/FaerieMiscreant.java index 859c866bb11c..1a20458ff853 100644 --- a/Mage.Sets/src/mage/cards/f/FaerieMiscreant.java +++ b/Mage.Sets/src/mage/cards/f/FaerieMiscreant.java @@ -14,7 +14,7 @@ import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.NamePredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/f/FailureComply.java b/Mage.Sets/src/mage/cards/f/FailureComply.java index 22f348f4ad32..cd6066ff319a 100644 --- a/Mage.Sets/src/mage/cards/f/FailureComply.java +++ b/Mage.Sets/src/mage/cards/f/FailureComply.java @@ -1,23 +1,15 @@ package mage.cards.f; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.Effect; import mage.abilities.effects.common.ChooseACardNameEffect; +import mage.abilities.effects.common.OpponentsCantCastChosenUntilNextTurnEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.keyword.AftermathAbility; import mage.cards.CardSetInfo; import mage.cards.SplitCard; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.SpellAbilityType; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.target.TargetSpell; -import mage.util.CardUtil; import java.util.UUID; @@ -41,7 +33,7 @@ public FailureComply(UUID ownerId, CardSetInfo setInfo) { Effect effect = new ChooseACardNameEffect(ChooseACardNameEffect.TypeOfName.ALL); effect.setText("Choose a card name"); getRightHalfCard().getSpellAbility().addEffect(effect); - getRightHalfCard().getSpellAbility().addEffect(new ComplyCantCastEffect()); + getRightHalfCard().getSpellAbility().addEffect(new OpponentsCantCastChosenUntilNextTurnEffect()); } private FailureComply(final FailureComply card) { @@ -53,45 +45,3 @@ public FailureComply copy() { return new FailureComply(this); } } - -class ComplyCantCastEffect extends ContinuousRuleModifyingEffectImpl { - - public ComplyCantCastEffect() { - super(Duration.UntilYourNextTurn, Outcome.Benefit); - staticText = "Until your next turn, your opponents can't cast spells with the chosen name"; - } - - public ComplyCantCastEffect(final ComplyCantCastEffect effect) { - super(effect); - } - - @Override - public ComplyCantCastEffect copy() { - return new ComplyCantCastEffect(this); - } - - @Override - public String getInfoMessage(Ability source, GameEvent event, Game game) { - MageObject mageObject = game.getObject(source.getSourceId()); - String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); - if (mageObject != null && cardName != null) { - return "You can't cast a card named " + cardName + " (" + mageObject.getIdName() + ")."; - } - return null; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.CAST_SPELL_LATE; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); - if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { - MageObject object = game.getObject(event.getSourceId()); - return object != null && CardUtil.haveSameNames(object, cardName, game); - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/f/FainTheBroker.java b/Mage.Sets/src/mage/cards/f/FainTheBroker.java new file mode 100644 index 000000000000..c395ab321898 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FainTheBroker.java @@ -0,0 +1,76 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.RemoveCounterCost; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.UntapSourceEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +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.game.permanent.token.SilverquillToken; +import mage.game.permanent.token.TreasureToken; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetControlledPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FainTheBroker extends CardImpl { + + public FainTheBroker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // {T}, Sacrifice a creature: Put two +1/+1 counters on target creature. + Ability ability = new SimpleActivatedAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance(2)), new TapSourceCost() + ); + ability.addCost(new SacrificeTargetCost( + new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT) + )); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + + // {T}, Remove a counter from a creature you control: Create a Treasure token. + ability = new SimpleActivatedAbility(new CreateTokenEffect(new TreasureToken()), new TapSourceCost()); + ability.addCost(new RemoveCounterCost(new TargetControlledCreaturePermanent())); + this.addAbility(ability); + + // {T}, Sacrifice an artifact: Create a 2/1 white and black Inkling creature token with flying. + ability = new SimpleActivatedAbility(new CreateTokenEffect(new SilverquillToken()), new TapSourceCost()); + ability.addCost(new SacrificeTargetCost( + new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT_AN) + )); + this.addAbility(ability); + + // {3}{B}: Untap Fain, the Broker. + this.addAbility(new SimpleActivatedAbility(new UntapSourceEffect(), new ManaCostsImpl("{3}{B}"))); + } + + private FainTheBroker(final FainTheBroker card) { + super(card); + } + + @Override + public FainTheBroker copy() { + return new FainTheBroker(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FaithsReward.java b/Mage.Sets/src/mage/cards/f/FaithsReward.java index 54f70f68dc20..7c643ba5a9be 100644 --- a/Mage.Sets/src/mage/cards/f/FaithsReward.java +++ b/Mage.Sets/src/mage/cards/f/FaithsReward.java @@ -1,37 +1,32 @@ - package mage.cards.f; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.WatcherScope; import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.common.FilterPermanentCard; +import mage.filter.predicate.card.PutIntoGraveFromBattlefieldThisTurnPredicate; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; -import mage.watchers.Watcher; +import mage.players.Player; +import mage.watchers.common.CardsPutIntoGraveyardWatcher; -import java.util.ArrayList; -import java.util.List; import java.util.UUID; /** - * * @author Loki */ public final class FaithsReward extends CardImpl { public FaithsReward(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{W}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{W}"); // Return to the battlefield all permanent cards in your graveyard that were put there from the battlefield this turn. this.getSpellAbility().addEffect(new FaithsRewardEffect()); - this.getSpellAbility().addWatcher(new FaithsRewardWatcher()); + this.getSpellAbility().addWatcher(new CardsPutIntoGraveyardWatcher()); } private FaithsReward(final FaithsReward card) { @@ -46,28 +41,30 @@ public FaithsReward copy() { class FaithsRewardEffect extends OneShotEffect { + private static final FilterCard filter = new FilterPermanentCard(); + + static { + filter.add(PutIntoGraveFromBattlefieldThisTurnPredicate.instance); + } + FaithsRewardEffect() { super(Outcome.PutCardInPlay); staticText = "Return to the battlefield all permanent cards in your graveyard that were put there from the battlefield this turn"; } - FaithsRewardEffect(final FaithsRewardEffect effect) { + private FaithsRewardEffect(final FaithsRewardEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { - FaithsRewardWatcher watcher = game.getState().getWatcher(FaithsRewardWatcher.class); - if (watcher != null) { - for (UUID id : watcher.getCards()) { - Card c = game.getCard(id); - if (c != null && c.isOwnedBy(source.getControllerId()) && game.getState().getZone(id) == Zone.GRAVEYARD) { - c.moveToZone(Zone.BATTLEFIELD, source, game, false); - } - } - return true; + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; } - return false; + return player.moveCards(player.getGraveyard().getCards( + filter, source.getSourceId(), source.getControllerId(), game + ), Zone.BATTLEFIELD, source, game); } @Override @@ -75,28 +72,3 @@ public FaithsRewardEffect copy() { return new FaithsRewardEffect(this); } } - -class FaithsRewardWatcher extends Watcher { - private List cards = new ArrayList<>(); - - public FaithsRewardWatcher() { - super(WatcherScope.GAME); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.ZONE_CHANGE && ((ZoneChangeEvent)event).isDiesEvent()) { - cards.add(event.getTargetId()); - } - } - - public List getCards(){ - return cards; - } - - @Override - public void reset() { - super.reset(); - cards.clear(); - } -} diff --git a/Mage.Sets/src/mage/cards/f/FallOfTheHammer.java b/Mage.Sets/src/mage/cards/f/FallOfTheHammer.java index dbd17cbffd42..f03918c7c05f 100644 --- a/Mage.Sets/src/mage/cards/f/FallOfTheHammer.java +++ b/Mage.Sets/src/mage/cards/f/FallOfTheHammer.java @@ -10,7 +10,7 @@ import mage.constants.Outcome; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/f/FallingTimber.java b/Mage.Sets/src/mage/cards/f/FallingTimber.java index 397ccdac7deb..4d9f85f3c075 100644 --- a/Mage.Sets/src/mage/cards/f/FallingTimber.java +++ b/Mage.Sets/src/mage/cards/f/FallingTimber.java @@ -33,7 +33,7 @@ public FallingTimber(UUID ownerId, CardSetInfo setInfo) { // Prevent all combat damage target creature would deal this turn. If Falling Timber was kicked, // prevent all combat damage another target creature would deal this turn. Effect effect = new PreventDamageByTargetEffect(Duration.EndOfTurn, true); - effect.setText("Prevent all combat damage target creature would deal this turn. if this spell was kicked, " + + effect.setText("Prevent all combat damage target creature would deal this turn. If this spell was kicked, " + "prevent all combat damage another target creature would deal this turn."); this.getSpellAbility().addEffect(effect); this.getSpellAbility().setTargetAdjuster(FallingTimberAdjuster.instance); diff --git a/Mage.Sets/src/mage/cards/f/FalthisShadowcatFamiliar.java b/Mage.Sets/src/mage/cards/f/FalthisShadowcatFamiliar.java index e2c730b8336b..5c6b4b83e527 100644 --- a/Mage.Sets/src/mage/cards/f/FalthisShadowcatFamiliar.java +++ b/Mage.Sets/src/mage/cards/f/FalthisShadowcatFamiliar.java @@ -15,7 +15,7 @@ import mage.constants.SuperType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.CommanderPredicate; +import mage.filter.predicate.mageobject.CommanderPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/f/FangOfThePack.java b/Mage.Sets/src/mage/cards/f/FangOfThePack.java index f6f07ea42837..64af6c36b828 100644 --- a/Mage.Sets/src/mage/cards/f/FangOfThePack.java +++ b/Mage.Sets/src/mage/cards/f/FangOfThePack.java @@ -14,7 +14,7 @@ import mage.constants.Duration; import mage.constants.TargetController; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/f/FarWanderings.java b/Mage.Sets/src/mage/cards/f/FarWanderings.java index 99a15b938367..3b1c709a817f 100644 --- a/Mage.Sets/src/mage/cards/f/FarWanderings.java +++ b/Mage.Sets/src/mage/cards/f/FarWanderings.java @@ -29,7 +29,7 @@ public FarWanderings(UUID ownerId, CardSetInfo setInfo) { new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 3, StaticFilters.FILTER_CARD_BASIC_LAND), true, true), new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 1, StaticFilters.FILTER_CARD_BASIC_LAND), true, true), new CardsInControllerGraveyardCondition(7), - "Search your library for a basic land card, put that card onto the battlefield tapped, then shuffle your library.

Threshold — If seven or more cards are in your graveyard, instead search your library for up to three basic land cards, put them onto the battlefield tapped, then shuffle your library."); + "Search your library for a basic land card, put that card onto the battlefield tapped, then shuffle.

Threshold — If seven or more cards are in your graveyard, instead search your library for up to three basic land cards, put them onto the battlefield tapped, then shuffle."); this.getSpellAbility().addEffect(effect); } diff --git a/Mage.Sets/src/mage/cards/f/FarrelsMantle.java b/Mage.Sets/src/mage/cards/f/FarrelsMantle.java index 918e9d7f5ae6..cfb64e43bb63 100644 --- a/Mage.Sets/src/mage/cards/f/FarrelsMantle.java +++ b/Mage.Sets/src/mage/cards/f/FarrelsMantle.java @@ -1,38 +1,36 @@ - package mage.cards.f; -import java.util.UUID; +import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility; -import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.DamageTargetEffect; -import mage.abilities.effects.common.continuous.AssignNoCombatDamageSourceEffect; -import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicate; import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.MageObjectReferencePredicate; import mage.game.Game; +import mage.game.events.DamageEvent; +import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; -import mage.util.CardUtil; + +import java.util.UUID; /** - * - * @author MarcoMarin + * @author TheElk801 */ public final class FarrelsMantle extends CardImpl { - public FarrelsMantle(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); this.subtype.add(SubType.AURA); // Enchant creature @@ -41,17 +39,9 @@ public FarrelsMantle(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new AttachEffect(Outcome.AddAbility)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); - + // Whenever enchanted creature attacks and isn't blocked, its controller may have it deal damage equal to its power plus 2 to another target creature. If that player does, the attacking creature assigns no combat damage this turn. - FilterPermanent filter = new FilterCreaturePermanent(); - filter.add(Predicates.not(new AttachmentByUUIDPredicate(this.getId()))); - - Ability ability2 = new AttacksAndIsNotBlockedTriggeredAbility(new FarrelsMantleEffect(), true); - ability2.addTarget(new TargetPermanent(filter)); - ability2.addEffect(new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn)); - - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(ability2, AttachmentType.AURA))); - + this.addAbility(new FarrelsMantleTriggeredAbility()); } private FarrelsMantle(final FarrelsMantle card) { @@ -63,36 +53,69 @@ public FarrelsMantle copy() { return new FarrelsMantle(this); } } -class AttachmentByUUIDPredicate implements Predicate { - private final UUID id; +class FarrelsMantleTriggeredAbility extends TriggeredAbilityImpl { + + FarrelsMantleTriggeredAbility() { + super(Zone.BATTLEFIELD, null, false); + } + + private FarrelsMantleTriggeredAbility(final FarrelsMantleTriggeredAbility ability) { + super(ability); + } + + @Override + public FarrelsMantleTriggeredAbility copy() { + return new FarrelsMantleTriggeredAbility(this); + } - public AttachmentByUUIDPredicate(UUID id) { - this.id = id; + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.UNBLOCKED_ATTACKER; } @Override - public boolean apply(Permanent input, Game game) { - return input.getAttachments().contains(id); + public boolean checkTrigger(GameEvent event, Game game) { + Permanent aura = getSourcePermanentOrLKI(game); + if (aura == null + || !event.getTargetId().equals(aura.getAttachedTo()) + || game.getPermanent(aura.getAttachedTo()) == null) { + return false; + } + MageObjectReference mor = new MageObjectReference(event.getTargetId(), game); + FilterPermanent filter = new FilterCreaturePermanent(); + filter.add(Predicates.not(new MageObjectReferencePredicate(mor))); + TargetPermanent target = new TargetPermanent(filter); + target.setTargetController(game.getControllerId(event.getTargetId())); + this.getTargets().clear(); + this.addTarget(target); + this.getEffects().clear(); + this.addEffect(new FarrelsMantleEffect(mor)); + return true; } @Override - public String toString() { - return "AttachmentUUID(" + id + ')'; + public String getRule() { + return "Whenever enchanted creature attacks and isn't blocked, its controller may have it deal damage " + + "equal to its power plus 2 to another target creature. If that player does, " + + "the attacking creature assigns no combat damage this turn."; } } -class FarrelsMantleEffect extends OneShotEffect{ - - public FarrelsMantleEffect() { - super(Outcome.Damage); - this.setText("its controller may have it deal damage equal to its power plus 2 to another target creature."); +class FarrelsMantleEffect extends OneShotEffect { + + private final MageObjectReference mor; + + FarrelsMantleEffect(MageObjectReference mor) { + super(Outcome.Benefit); + this.mor = mor; } - - public FarrelsMantleEffect(final FarrelsMantleEffect effect) { + + private FarrelsMantleEffect(final FarrelsMantleEffect effect) { super(effect); + this.mor = effect.mor; } - + @Override public FarrelsMantleEffect copy() { return new FarrelsMantleEffect(this); @@ -100,9 +123,66 @@ public FarrelsMantleEffect copy() { @Override public boolean apply(Game game, Ability source) { - Permanent perm = game.getPermanent(source.getSourceId()); - int damage = CardUtil.overflowInc(perm.getPower().getValue(), 2); - DamageTargetEffect dmgEffect = new DamageTargetEffect(damage); - return dmgEffect.apply(game, source); + Permanent permanent = mor.getPermanent(game); + Permanent targeted = game.getPermanent(source.getFirstTarget()); + if (permanent == null || targeted == null) { + return false; + } + int damage = permanent.getPower().getValue() + 2; + Player player = game.getPlayer(permanent.getControllerId()); + if (damage > 0 && player != null && player.chooseUse( + outcome, "Have " + permanent.getIdName() + " deal " + + damage + " to " + targeted.getIdName() + '?', source, game + ) && targeted.damage(damage, permanent.getId(), source, game) > 0) { + game.addEffect(new FarrelsMantleDamageEffect(mor), source); + return true; + } + return false; + } +} + +class FarrelsMantleDamageEffect extends ReplacementEffectImpl { + + private final MageObjectReference mor; + + FarrelsMantleDamageEffect(MageObjectReference mor) { + super(Duration.EndOfTurn, Outcome.Neutral); + this.mor = mor; + } + + private FarrelsMantleDamageEffect(final FarrelsMantleDamageEffect effect) { + super(effect); + this.mor = effect.mor; + } + + @Override + public FarrelsMantleDamageEffect copy() { + return new FarrelsMantleDamageEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + switch (event.getType()) { + case DAMAGE_PERMANENT: + case DAMAGE_PLAYER: + return true; + default: + return false; + } + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return ((DamageEvent) event).isCombatDamage() && mor.refersTo(event.getSourceId(), game); } } diff --git a/Mage.Sets/src/mage/cards/f/Fasting.java b/Mage.Sets/src/mage/cards/f/Fasting.java index e537226a173c..1fa6e0af561c 100644 --- a/Mage.Sets/src/mage/cards/f/Fasting.java +++ b/Mage.Sets/src/mage/cards/f/Fasting.java @@ -91,7 +91,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { Player controller = game.getPlayer(source.getControllerId()); if (event.getPlayerId().equals(source.getControllerId()) && controller != null - && controller.chooseUse(outcome, "Would you like to skip your draw step to gain 2 life?", source, game)) { + && controller.chooseUse(outcome, "Skip your draw step to gain 2 life?", source, game)) { controller.gainLife(2, game, source); return true; } diff --git a/Mage.Sets/src/mage/cards/f/FatalPush.java b/Mage.Sets/src/mage/cards/f/FatalPush.java index 347cf70a47c6..7425407d33f6 100644 --- a/Mage.Sets/src/mage/cards/f/FatalPush.java +++ b/Mage.Sets/src/mage/cards/f/FatalPush.java @@ -45,7 +45,7 @@ class FatalPushEffect extends OneShotEffect { FatalPushEffect() { super(Outcome.DestroyPermanent); - this.staticText = "Destroy target creature if it has converted mana cost 2 or less.
Revolt — Destroy that creature if it has converted mana cost 4 or less instead if a permanent you controlled left the battlefield this turn"; + this.staticText = "Destroy target creature if it has mana value 2 or less.
Revolt — Destroy that creature if it has mana value 4 or less instead if a permanent you controlled left the battlefield this turn"; } FatalPushEffect(final FatalPushEffect effect) { @@ -63,7 +63,7 @@ public boolean apply(Game game, Ability source) { if (controller != null) { Permanent targetCreature = game.getPermanent(this.getTargetPointer().getFirst(game, source)); if (targetCreature != null) { - int cmc = targetCreature.getConvertedManaCost(); + int cmc = targetCreature.getManaValue(); if (cmc <= 2 || (RevoltCondition.instance.apply(game, source) && cmc <= 4)) { targetCreature.destroy(source, game, false); diff --git a/Mage.Sets/src/mage/cards/f/FateTransfer.java b/Mage.Sets/src/mage/cards/f/FateTransfer.java index 2e202ea5a1dc..071c306639d6 100644 --- a/Mage.Sets/src/mage/cards/f/FateTransfer.java +++ b/Mage.Sets/src/mage/cards/f/FateTransfer.java @@ -10,7 +10,7 @@ import mage.constants.Outcome; import mage.counters.Counter; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/f/FatedReturn.java b/Mage.Sets/src/mage/cards/f/FatedReturn.java index 4546ee620cfe..17ef52dd54e4 100644 --- a/Mage.Sets/src/mage/cards/f/FatedReturn.java +++ b/Mage.Sets/src/mage/cards/f/FatedReturn.java @@ -31,7 +31,7 @@ public FatedReturn(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new GainAbilityTargetEffect(IndestructibleAbility.getInstance(), Duration.Custom, "It gains indestructible")); this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new ScryEffect(2), MyTurnCondition.instance, - "If it's your turn, scry 2 (Look at the top two cards of your library, then put any number of them on the bottom of your library and the rest on top in any order.)")); + "If it's your turn, scry 2. (Look at the top two cards of your library, then put any number of them on the bottom of your library and the rest on top in any order.)")); this.getSpellAbility().addHint(MyTurnHint.instance); } diff --git a/Mage.Sets/src/mage/cards/f/Fatestitcher.java b/Mage.Sets/src/mage/cards/f/Fatestitcher.java index 971babca6af0..3019602378ef 100644 --- a/Mage.Sets/src/mage/cards/f/Fatestitcher.java +++ b/Mage.Sets/src/mage/cards/f/Fatestitcher.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; /** diff --git a/Mage.Sets/src/mage/cards/f/FathomFleetBoarder.java b/Mage.Sets/src/mage/cards/f/FathomFleetBoarder.java index b35e2e204e13..1574b38afb20 100644 --- a/Mage.Sets/src/mage/cards/f/FathomFleetBoarder.java +++ b/Mage.Sets/src/mage/cards/f/FathomFleetBoarder.java @@ -13,7 +13,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/f/FathomFleetCaptain.java b/Mage.Sets/src/mage/cards/f/FathomFleetCaptain.java index 1a402f351e29..6d309e008947 100644 --- a/Mage.Sets/src/mage/cards/f/FathomFleetCaptain.java +++ b/Mage.Sets/src/mage/cards/f/FathomFleetCaptain.java @@ -16,7 +16,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.permanent.token.PirateToken; diff --git a/Mage.Sets/src/mage/cards/f/FaultRiders.java b/Mage.Sets/src/mage/cards/f/FaultRiders.java index aca1563c68d9..8ec58194fea6 100644 --- a/Mage.Sets/src/mage/cards/f/FaultRiders.java +++ b/Mage.Sets/src/mage/cards/f/FaultRiders.java @@ -39,7 +39,7 @@ public FaultRiders(UUID ownerId, CardSetInfo setInfo) { effect, new SacrificeTargetCost(new TargetControlledPermanent(new FilterControlledLandPermanent("a land")))); effect = new GainAbilitySourceEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn); - effect.setText("and gains first strike"); + effect.setText("and gains first strike until end of turn"); ability.addEffect(effect); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/f/FavorOfTheMighty.java b/Mage.Sets/src/mage/cards/f/FavorOfTheMighty.java index 6c91034a89c7..2efa1ce72a59 100644 --- a/Mage.Sets/src/mage/cards/f/FavorOfTheMighty.java +++ b/Mage.Sets/src/mage/cards/f/FavorOfTheMighty.java @@ -16,7 +16,7 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; @@ -60,7 +60,7 @@ class FavorOfTheMightyEffect extends ContinuousEffectImpl { FavorOfTheMightyEffect() { super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); - this.staticText = "Each creature with the highest converted mana cost has protection from all colors."; + this.staticText = "Each creature with the highest mana value has protection from all colors."; } FavorOfTheMightyEffect(final FavorOfTheMightyEffect effect) { @@ -76,12 +76,12 @@ public FavorOfTheMightyEffect copy() { public boolean apply(Game game, Ability source) { int maxCMC = Integer.MIN_VALUE; for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game)) { - if (permanent != null && permanent.getConvertedManaCost() > maxCMC) { - maxCMC = permanent.getConvertedManaCost(); + if (permanent != null && permanent.getManaValue() > maxCMC) { + maxCMC = permanent.getManaValue(); } } FilterPermanent filterMaxCMC = new FilterCreaturePermanent(); - filterMaxCMC.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, maxCMC)); + filterMaxCMC.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, maxCMC)); for (Permanent permanent : game.getBattlefield().getActivePermanents(filterMaxCMC, source.getControllerId(), game)) { if (permanent != null) { permanent.addAbility(new ProtectionAbility(filter), source.getSourceId(), game); diff --git a/Mage.Sets/src/mage/cards/f/FeastingTrollKing.java b/Mage.Sets/src/mage/cards/f/FeastingTrollKing.java index 5e021705badc..a37e9cf8810e 100644 --- a/Mage.Sets/src/mage/cards/f/FeastingTrollKing.java +++ b/Mage.Sets/src/mage/cards/f/FeastingTrollKing.java @@ -55,7 +55,7 @@ public FeastingTrollKing(UUID ownerId, CardSetInfo setInfo) { // Sacrifice three Foods: Return Feasting Troll King from your graveyard to the battlefield. Activate this ability only during your turn. this.addAbility(new ActivateIfConditionActivatedAbility( Zone.GRAVEYARD, - new ReturnSourceFromGraveyardToBattlefieldEffect(), + new ReturnSourceFromGraveyardToBattlefieldEffect(false, false), new SacrificeTargetCost(new TargetControlledPermanent(3, filter)), MyTurnCondition.instance ).addHint(MyTurnHint.instance)); diff --git a/Mage.Sets/src/mage/cards/f/FeedTheSwarm.java b/Mage.Sets/src/mage/cards/f/FeedTheSwarm.java index a80c755b83a4..4c61ce614e26 100644 --- a/Mage.Sets/src/mage/cards/f/FeedTheSwarm.java +++ b/Mage.Sets/src/mage/cards/f/FeedTheSwarm.java @@ -55,7 +55,7 @@ class FeedTheSwarmEffect extends OneShotEffect { FeedTheSwarmEffect() { super(Outcome.Benefit); staticText = "destroy target creature or enchantment an opponent controls. " + - "You lose life equal to that permanent's converted mana cost"; + "You lose life equal to that permanent's mana value"; } private FeedTheSwarmEffect(final FeedTheSwarmEffect effect) { @@ -74,7 +74,7 @@ public boolean apply(Game game, Ability source) { if (permanent == null || player == null) { return false; } - player.loseLife(permanent.getConvertedManaCost(), game, source, false); + player.loseLife(permanent.getManaValue(), game, source, false); permanent.destroy(source, game, false); return true; } diff --git a/Mage.Sets/src/mage/cards/f/FelhideBrawler.java b/Mage.Sets/src/mage/cards/f/FelhideBrawler.java index 5e3e7a68778b..bc1a772569de 100644 --- a/Mage.Sets/src/mage/cards/f/FelhideBrawler.java +++ b/Mage.Sets/src/mage/cards/f/FelhideBrawler.java @@ -11,7 +11,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/f/FelhideSpiritbinder.java b/Mage.Sets/src/mage/cards/f/FelhideSpiritbinder.java index 54bae1f02312..cfa673999e63 100644 --- a/Mage.Sets/src/mage/cards/f/FelhideSpiritbinder.java +++ b/Mage.Sets/src/mage/cards/f/FelhideSpiritbinder.java @@ -18,7 +18,7 @@ import mage.constants.SubType; import mage.constants.Outcome; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/f/FelidarGuardian.java b/Mage.Sets/src/mage/cards/f/FelidarGuardian.java index 86cabf747262..155d6d53fa7e 100644 --- a/Mage.Sets/src/mage/cards/f/FelidarGuardian.java +++ b/Mage.Sets/src/mage/cards/f/FelidarGuardian.java @@ -3,7 +3,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.ExileTargetForSourceEffect; import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect; import mage.cards.CardImpl; @@ -11,7 +10,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/f/FelisaFangOfSilverquill.java b/Mage.Sets/src/mage/cards/f/FelisaFangOfSilverquill.java new file mode 100644 index 000000000000..88241f01a68a --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FelisaFangOfSilverquill.java @@ -0,0 +1,104 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.MentorAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.Counter; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.CounterAnyPredicate; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.SilverquillToken; + +import java.util.Map; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FelisaFangOfSilverquill extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("a nontoken creature you control"); + + static { + filter.add(Predicates.not(TokenPredicate.instance)); + filter.add(CounterAnyPredicate.instance); + } + + private static final String rule = "if it had counters on it, " + + "create X tapped 2/1 white and black Inkling creature tokens with flying, " + + "where X is the number of counters it had on it"; + + public FelisaFangOfSilverquill(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Mentor + this.addAbility(new MentorAbility()); + + // Whenever a nontoken creature you control dies, if it had counters on it, create X tapped 2/1 white and black Inkling creature tokens with flying, where X is the number of counters it had on it. + this.addAbility(new DiesCreatureTriggeredAbility(new CreateTokenEffect( + new SilverquillToken(), FelisaFangOfSilverquillValue.instance, true, false + ).setText(rule), false, filter)); + } + + private FelisaFangOfSilverquill(final FelisaFangOfSilverquill card) { + super(card); + } + + @Override + public FelisaFangOfSilverquill copy() { + return new FelisaFangOfSilverquill(this); + } +} + +enum FelisaFangOfSilverquillValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + Permanent permanent = (Permanent) effect.getValue("creatureDied"); + if (permanent == null) { + return 0; + } + return permanent + .getCounters(game) + .entrySet() + .stream() + .map(Map.Entry::getValue) + .mapToInt(Counter::getCount) + .sum(); + } + + @Override + public FelisaFangOfSilverquillValue copy() { + return instance; + } + + @Override + public String getMessage() { + return ""; + } +} diff --git a/Mage.Sets/src/mage/cards/f/FellShepherd.java b/Mage.Sets/src/mage/cards/f/FellShepherd.java index 057c3827734b..f0e3e7f0453f 100644 --- a/Mage.Sets/src/mage/cards/f/FellShepherd.java +++ b/Mage.Sets/src/mage/cards/f/FellShepherd.java @@ -1,37 +1,40 @@ - package mage.cards.f; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ReturnToHandFromGraveyardAllEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.FilterCard; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.events.ZoneChangeEvent; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.card.PutIntoGraveFromBattlefieldThisTurnPredicate; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; -import mage.watchers.Watcher; +import mage.watchers.common.CardsPutIntoGraveyardWatcher; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class FellShepherd extends CardImpl { + private static final FilterCard filter = new FilterCreatureCard(); + + static { + filter.add(PutIntoGraveFromBattlefieldThisTurnPredicate.instance); + } + public FellShepherd(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{B}{B}"); this.subtype.add(SubType.AVATAR); @@ -40,14 +43,20 @@ public FellShepherd(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(6); // Whenever Fell Shepherd deals combat damage to a player, you may return to your hand all creature cards that were put into your graveyard from the battlefield this turn. - this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new FellShepherdEffect(), true), new FellShepherdWatcher()); + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( + new ReturnToHandFromGraveyardAllEffect(filter, TargetController.YOU) + .setText("return to your hand all creature cards that were " + + "put into your graveyard from the battlefield this turn"), + true + ), new CardsPutIntoGraveyardWatcher()); // {B}, Sacrifice another creature: Target creature gets -2/-2 until end of turn. - SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(-2, -2, Duration.EndOfTurn), new ManaCostsImpl("{B}")); - ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE, false))); + Ability ability = new SimpleActivatedAbility( + new BoostTargetEffect(-2, -2, Duration.EndOfTurn), new ManaCostsImpl("{B}") + ); + ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE))); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); - } private FellShepherd(final FellShepherd card) { @@ -59,73 +68,3 @@ public FellShepherd copy() { return new FellShepherd(this); } } - -class FellShepherdWatcher extends Watcher { - - private Set creatureIds = new HashSet<>(); - - public FellShepherdWatcher() { - super(WatcherScope.PLAYER); - condition = true; - } - - public Set getCreaturesIds() { - return creatureIds; - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.ZONE_CHANGE && ((ZoneChangeEvent) event).isDiesEvent()) { - MageObject card = game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); - if (card != null && ((Card) card).isOwnedBy(this.controllerId) && card.isCreature()) { - creatureIds.add(card.getId()); - } - } - } - - @Override - public void reset() { - super.reset(); - creatureIds.clear(); - } -} - -class FellShepherdEffect extends OneShotEffect { - - public FellShepherdEffect() { - super(Outcome.ReturnToHand); - this.staticText = "return to your hand all creature cards that were put into your graveyard from the battlefield this turn"; - } - - public FellShepherdEffect(final FellShepherdEffect effect) { - super(effect); - } - - @Override - public FellShepherdEffect copy() { - return new FellShepherdEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - FellShepherdWatcher watcher = game.getState().getWatcher(FellShepherdWatcher.class, source.getControllerId()); - if (watcher != null) { - StringBuilder sb = new StringBuilder(); - for (UUID creatureId : watcher.getCreaturesIds()) { - if (game.getState().getZone(creatureId) == Zone.GRAVEYARD) { - Card card = game.getCard(creatureId); - if (card != null) { - card.moveToZone(Zone.HAND, source, game, false); - sb.append(' ').append(card.getName()); - } - } - } - if (sb.length() > 0) { - sb.insert(0, "Fell Shepherd - returning to hand:"); - game.informPlayers(sb.toString()); - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/f/FeralContest.java b/Mage.Sets/src/mage/cards/f/FeralContest.java index ef74c8acaf0d..bd10f66d3125 100644 --- a/Mage.Sets/src/mage/cards/f/FeralContest.java +++ b/Mage.Sets/src/mage/cards/f/FeralContest.java @@ -11,7 +11,7 @@ import mage.constants.Duration; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/f/FerocityOfTheUnderworld.java b/Mage.Sets/src/mage/cards/f/FerocityOfTheUnderworld.java index 7a32b767861e..259cfad3b3a2 100644 --- a/Mage.Sets/src/mage/cards/f/FerocityOfTheUnderworld.java +++ b/Mage.Sets/src/mage/cards/f/FerocityOfTheUnderworld.java @@ -12,7 +12,7 @@ import mage.constants.ComparisonType; import mage.filter.StaticFilters; import mage.filter.common.FilterNonlandPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetSpell; import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetNonlandPermanent; @@ -23,10 +23,10 @@ */ public final class FerocityOfTheUnderworld extends CardImpl { - private static final FilterNonlandPermanent filterMode1 = new FilterNonlandPermanent("nonland permanent with converted mana cost 3 or less"); + private static final FilterNonlandPermanent filterMode1 = new FilterNonlandPermanent("nonland permanent with mana value 3 or less"); static { - filterMode1.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filterMode1.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public FerocityOfTheUnderworld(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/f/FerventChampion.java b/Mage.Sets/src/mage/cards/f/FerventChampion.java index 3ad2e21d0a45..828b931c7152 100644 --- a/Mage.Sets/src/mage/cards/f/FerventChampion.java +++ b/Mage.Sets/src/mage/cards/f/FerventChampion.java @@ -14,7 +14,7 @@ import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.AttackingPredicate; import mage.game.Game; import mage.target.Target; diff --git a/Mage.Sets/src/mage/cards/f/FerventMastery.java b/Mage.Sets/src/mage/cards/f/FerventMastery.java new file mode 100644 index 000000000000..c8a14cfc02ed --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FerventMastery.java @@ -0,0 +1,127 @@ +package mage.cards.f; + +import mage.abilities.Ability; +import mage.abilities.costs.AlternativeCostSourceAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FerventMastery extends CardImpl { + + public FerventMastery(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}{R}"); + + // You may pay {2}{R}{R} rather than pay this spell's mana cost. + Ability costAbility = new AlternativeCostSourceAbility(new ManaCostsImpl<>("{2}{R}{R}")); + this.addAbility(costAbility); + + // If the {2}{R}{R} cost was paid, an opponent discards any number of cards, then draws that many cards. + this.getSpellAbility().addEffect(new FerventMasteryAlternativeCostEffect(costAbility.getOriginalId())); + + // Search your library for up to three cards, put them into your hand, shuffle, then discard three cards at random. + this.getSpellAbility().addEffect(new FerventMasteryEffect()); + } + + private FerventMastery(final FerventMastery card) { + super(card); + } + + @Override + public FerventMastery copy() { + return new FerventMastery(this); + } +} + +class FerventMasteryAlternativeCostEffect extends OneShotEffect { + + private final UUID alternativeCostOriginalID; + + FerventMasteryAlternativeCostEffect(UUID alternativeCostOriginalID) { + super(Outcome.Detriment); + staticText = "if the {2}{R}{R} cost was paid, an opponent discards any number of cards, " + + "then draws that many cards.
"; + this.alternativeCostOriginalID = alternativeCostOriginalID; + } + + private FerventMasteryAlternativeCostEffect(FerventMasteryAlternativeCostEffect effect) { + super(effect); + this.alternativeCostOriginalID = effect.alternativeCostOriginalID; + } + + @Override + public FerventMasteryAlternativeCostEffect copy() { + return new FerventMasteryAlternativeCostEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (!AlternativeCostSourceAbility.getActivatedStatus( + game, source, this.alternativeCostOriginalID, false + )) { + return false; + } + + Player player = game.getPlayer(source.getControllerId()); + TargetOpponent targetOpponent = new TargetOpponent(true); + if (!player.chooseTarget(Outcome.DrawCard, targetOpponent, source, game)) { + return false; + } + Player opponent = game.getPlayer(targetOpponent.getFirstTarget()); + if (opponent == null) { + return false; + } + int discarded = opponent.discard(0, Integer.MAX_VALUE, false, source, game).size(); + opponent.drawCards(discarded, source, game); + return true; + } +} + +class FerventMasteryEffect extends OneShotEffect { + + FerventMasteryEffect() { + super(Outcome.Benefit); + staticText = "Search your library for up to three cards, put them into your hand, " + + "shuffle, then discard three cards at random."; + } + + private FerventMasteryEffect(final FerventMasteryEffect effect) { + super(effect); + } + + @Override + public FerventMasteryEffect copy() { + return new FerventMasteryEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + TargetCardInLibrary target = new TargetCardInLibrary(3, StaticFilters.FILTER_CARD); + player.searchLibrary(target, source, game); + Cards cards = new CardsImpl(target.getTargets()); + cards.removeIf(uuid -> game.getState().getZone(uuid) != Zone.LIBRARY); + player.moveCards(cards, Zone.HAND, source, game); + player.shuffleLibrary(source, game); + player.discard(3, true, false, source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/f/FesteringMarch.java b/Mage.Sets/src/mage/cards/f/FesteringMarch.java index f758dc6cc652..ccf4b0cb3195 100644 --- a/Mage.Sets/src/mage/cards/f/FesteringMarch.java +++ b/Mage.Sets/src/mage/cards/f/FesteringMarch.java @@ -27,7 +27,7 @@ public FesteringMarch(UUID ownerId, CardSetInfo setInfo) { // Creatures your opponents control get -1/-1 until end of turn. this.getSpellAbility().addEffect(new BoostOpponentsEffect(-1, -1, Duration.EndOfTurn)); // Exile Festering March - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); // with three time counters on it. Effect effect = new AddCountersSourceEffect(CounterType.TIME.createInstance(), StaticValue.get(3), false, true); effect.setText("with 3 time counters on it"); diff --git a/Mage.Sets/src/mage/cards/f/Fettergeist.java b/Mage.Sets/src/mage/cards/f/Fettergeist.java index aa316aeafdff..e75e2d007f74 100644 --- a/Mage.Sets/src/mage/cards/f/Fettergeist.java +++ b/Mage.Sets/src/mage/cards/f/Fettergeist.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/f/FieldOfRuin.java b/Mage.Sets/src/mage/cards/f/FieldOfRuin.java index 2a7680b06f86..4ae3757f4d3f 100644 --- a/Mage.Sets/src/mage/cards/f/FieldOfRuin.java +++ b/Mage.Sets/src/mage/cards/f/FieldOfRuin.java @@ -68,7 +68,7 @@ class FieldOfRuinEffect extends OneShotEffect { FieldOfRuinEffect() { super(Outcome.Benefit); - this.staticText = "Each player searches their library for a basic land card, puts it onto the battlefield, then shuffles their library"; + this.staticText = "Each player searches their library for a basic land card, puts it onto the battlefield, then shuffles"; } FieldOfRuinEffect(final FieldOfRuinEffect effect) { diff --git a/Mage.Sets/src/mage/cards/f/FieldTrip.java b/Mage.Sets/src/mage/cards/f/FieldTrip.java new file mode 100644 index 000000000000..a76ce68b19de --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FieldTrip.java @@ -0,0 +1,45 @@ +package mage.cards.f; + +import mage.abilities.effects.common.LearnEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterCard; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FieldTrip extends CardImpl { + + private static final FilterCard filter = new FilterCard("basic Forest card"); + + static { + filter.add(SuperType.BASIC.getPredicate()); + filter.add(SubType.FOREST.getPredicate()); + } + + public FieldTrip(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{G}"); + + // Search your library for a basic Forest card, put that card onto the battlefield tapped, then shuffle. + this.getSpellAbility().addEffect(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter), true)); + + // Learn. + this.getSpellAbility().addEffect(new LearnEffect().concatBy("
")); + } + + private FieldTrip(final FieldTrip card) { + super(card); + } + + @Override + public FieldTrip copy() { + return new FieldTrip(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FiendArtisan.java b/Mage.Sets/src/mage/cards/f/FiendArtisan.java index fed489f0ac22..d2100c768579 100644 --- a/Mage.Sets/src/mage/cards/f/FiendArtisan.java +++ b/Mage.Sets/src/mage/cards/f/FiendArtisan.java @@ -20,8 +20,8 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetControlledPermanent; @@ -77,8 +77,8 @@ class FiendArtisanEffect extends OneShotEffect { FiendArtisanEffect() { super(Outcome.Benefit); - staticText = "search your library for a creature card with converted mana cost X or less, " + - "put it onto the battlefield, then shuffle your library"; + staticText = "search your library for a creature card with mana value X or less, " + + "put it onto the battlefield, then shuffle"; } private FiendArtisanEffect(final FiendArtisanEffect effect) { @@ -93,8 +93,8 @@ public FiendArtisanEffect copy() { @Override public boolean apply(Game game, Ability source) { int xValue = source.getManaCostsToPay().getX(); - FilterCard filter = new FilterCreatureCard("creature card with converted mana cost " + xValue + " or less"); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, xValue + 1)); + FilterCard filter = new FilterCreatureCard("creature card with mana value " + xValue + " or less"); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); return new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter)).apply(game, source); } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/f/FiendHunter.java b/Mage.Sets/src/mage/cards/f/FiendHunter.java index a393ea0c30ba..dd2a7f11108a 100644 --- a/Mage.Sets/src/mage/cards/f/FiendHunter.java +++ b/Mage.Sets/src/mage/cards/f/FiendHunter.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.Target; import mage.target.TargetPermanent; diff --git a/Mage.Sets/src/mage/cards/f/FiendOfTheShadows.java b/Mage.Sets/src/mage/cards/f/FiendOfTheShadows.java index c5f478ca0767..145bca509a98 100644 --- a/Mage.Sets/src/mage/cards/f/FiendOfTheShadows.java +++ b/Mage.Sets/src/mage/cards/f/FiendOfTheShadows.java @@ -1,42 +1,37 @@ - package mage.cards.f; -import java.util.UUID; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.SacrificeTargetCost; -import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.common.ExileFromZoneTargetEffect; +import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.RegenerateSourceEffect; import mage.abilities.keyword.FlyingAbility; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.FilterCard; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.game.ExileZone; +import mage.filter.common.FilterControlledPermanent; import mage.game.Game; -import mage.target.common.TargetControlledCreaturePermanent; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetControlledPermanent; +import mage.target.common.TargetDiscard; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author BetaSteward */ public final class FiendOfTheShadows extends CardImpl { - private UUID exileId = UUID.randomUUID(); - - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("a Human"); - - static { - filter.add(SubType.HUMAN.getPredicate()); - } + private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.HUMAN, "a Human"); public FiendOfTheShadows(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); this.subtype.add(SubType.VAMPIRE); this.subtype.add(SubType.WIZARD); @@ -44,12 +39,12 @@ public FiendOfTheShadows(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(3); this.addAbility(FlyingAbility.getInstance()); + // Whenever Fiend of the Shadows deals combat damage to a player, that player exiles a card from their hand. You may play that card for as long as it remains exiled. - this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new ExileFromZoneTargetEffect(Zone.HAND, exileId, "Fiend of the Shadows", new FilterCard()), false, true)); - this.addAbility(new SimpleStaticAbility(Zone.ALL, new FiendOfTheShadowsEffect(exileId))); + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new FiendOfTheShadowsEffect(), false, true)); // Sacrifice a Human: Regenerate Fiend of the Shadows. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, false)))); + this.addAbility(new SimpleActivatedAbility(new RegenerateSourceEffect(), new SacrificeTargetCost(new TargetControlledPermanent(filter)))); } private FiendOfTheShadows(final FiendOfTheShadows card) { @@ -62,24 +57,16 @@ public FiendOfTheShadows copy() { } } -class FiendOfTheShadowsEffect extends AsThoughEffectImpl { - - private final UUID exileId; +class FiendOfTheShadowsEffect extends OneShotEffect { - public FiendOfTheShadowsEffect(UUID exileId) { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfGame, Outcome.Benefit); - this.exileId = exileId; - staticText = "You may play that card for as long as it remains exiled"; + FiendOfTheShadowsEffect() { + super(Outcome.Discard); + staticText = "that player exiles a card from their hand. " + + "You may play that card for as long as it remains exiled"; } - public FiendOfTheShadowsEffect(final FiendOfTheShadowsEffect effect) { + private FiendOfTheShadowsEffect(final FiendOfTheShadowsEffect effect) { super(effect); - this.exileId = effect.exileId; - } - - @Override - public boolean apply(Game game, Ability source) { - return true; } @Override @@ -88,14 +75,23 @@ public FiendOfTheShadowsEffect copy() { } @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (affectedControllerId.equals(source.getControllerId())) { - ExileZone zone = game.getExile().getExileZone(exileId); - if (zone != null && zone.contains(objectId)) { - return true; - } + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(targetPointer.getFirst(game, source)); + MageObject sourceObject = source.getSourceObject(game); + if (player == null || sourceObject == null || player.getHand().isEmpty()) { + return false; + } + TargetCard targetCard = new TargetDiscard(player.getId()); + player.choose(outcome, targetCard, source.getSourceId(), game); + Card card = game.getCard(targetCard.getFirstTarget()); + if (card == null) { + return false; } - return false; + player.moveCardToExileWithInfo( + card, CardUtil.getExileZoneId(game, source), sourceObject.getName(), + source, game, Zone.HAND, true + ); + CardUtil.makeCardPlayable(game, source, card, Duration.Custom, false); + return true; } - } diff --git a/Mage.Sets/src/mage/cards/f/FierceEmpath.java b/Mage.Sets/src/mage/cards/f/FierceEmpath.java index f8589adab403..d1d7307c7bab 100644 --- a/Mage.Sets/src/mage/cards/f/FierceEmpath.java +++ b/Mage.Sets/src/mage/cards/f/FierceEmpath.java @@ -10,7 +10,7 @@ import mage.constants.ComparisonType; import mage.constants.SubType; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; import java.util.UUID; @@ -21,9 +21,9 @@ */ public final class FierceEmpath extends CardImpl { - private static final FilterCreatureCard filter = new FilterCreatureCard("creature card with converted mana cost 6 or greater"); + private static final FilterCreatureCard filter = new FilterCreatureCard("creature card with mana value 6 or greater"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 5)); + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 5)); } public FierceEmpath(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}"); @@ -35,7 +35,7 @@ public FierceEmpath(UUID ownerId, CardSetInfo setInfo) { // When Fierce Empath enters the battlefield, you may search your library for a creature card with converted mana cost 6 or greater, reveal it, put it into your hand, then shuffle your library. this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandEffect( new TargetCardInLibrary(filter), true, true) - .setText("search your library for a creature card with converted mana cost 6 or greater, reveal it, put it into your hand, then shuffle your library"), + .setText("search your library for a creature card with mana value 6 or greater, reveal it, put it into your hand, then shuffle"), true)); } diff --git a/Mage.Sets/src/mage/cards/f/FieryEmancipation.java b/Mage.Sets/src/mage/cards/f/FieryEmancipation.java index 0a9a0e94e6b4..35ac9354423b 100644 --- a/Mage.Sets/src/mage/cards/f/FieryEmancipation.java +++ b/Mage.Sets/src/mage/cards/f/FieryEmancipation.java @@ -56,8 +56,7 @@ public FieryEmancipationEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType().equals(GameEvent.EventType.DAMAGE_PLAYER) - || event.getType().equals(GameEvent.EventType.DAMAGE_CREATURE) - || event.getType().equals(GameEvent.EventType.DAMAGE_PLANESWALKER); + || event.getType().equals(GameEvent.EventType.DAMAGE_PERMANENT); } @Override diff --git a/Mage.Sets/src/mage/cards/f/FieryEncore.java b/Mage.Sets/src/mage/cards/f/FieryEncore.java new file mode 100644 index 000000000000..99a4e7579b79 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FieryEncore.java @@ -0,0 +1,80 @@ +package mage.cards.f; + +import mage.abilities.Ability; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.keyword.StormAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCreatureOrPlaneswalker; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FieryEncore extends CardImpl { + + public FieryEncore(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{R}"); + + // Discard a card, then draw a card. When you discard a nonland card this way, Fiery Encore deals damage equal to that card's mana value to target creature or planeswalker. + this.getSpellAbility().addEffect(new FieryEncoreEffect()); + + // Storm + this.addAbility(new StormAbility()); + } + + private FieryEncore(final FieryEncore card) { + super(card); + } + + @Override + public FieryEncore copy() { + return new FieryEncore(this); + } +} + +class FieryEncoreEffect extends OneShotEffect { + + FieryEncoreEffect() { + super(Outcome.Benefit); + staticText = "discard a card, then draw a card. When you discard a nonland card this way, " + + "{this} deals damage equal to that card's mana value to target creature or planeswalker"; + } + + private FieryEncoreEffect(final FieryEncoreEffect effect) { + super(effect); + } + + @Override + public FieryEncoreEffect copy() { + return new FieryEncoreEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Card card = player.discardOne(false, false, source, game); + player.drawCards(1, source, game); + if (card == null || card.isLand()) { + return true; + } + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new DamageTargetEffect(card.getManaValue()), false, "when you discard a nonland " + + "card this way, {this} deals damage equal to that card's mana value to target creature or planeswalker" + ); + ability.addTarget(new TargetCreatureOrPlaneswalker()); + game.fireReflexiveTriggeredAbility(ability, source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/f/FieryGambit.java b/Mage.Sets/src/mage/cards/f/FieryGambit.java index 5a9a4e6d47f3..849b83dab104 100644 --- a/Mage.Sets/src/mage/cards/f/FieryGambit.java +++ b/Mage.Sets/src/mage/cards/f/FieryGambit.java @@ -72,7 +72,7 @@ public boolean apply(Game game, Ability source) { } // AI workaround to stop flips on good result - if (!controller.isHuman() && !controller.isTestMode() && flipsWon >= 3) { + if (controller.isComputer() && flipsWon >= 3) { controllerStopped = true; break; } diff --git a/Mage.Sets/src/mage/cards/f/FilthyCur.java b/Mage.Sets/src/mage/cards/f/FilthyCur.java index de1ba0f1e6b5..b8193617372c 100644 --- a/Mage.Sets/src/mage/cards/f/FilthyCur.java +++ b/Mage.Sets/src/mage/cards/f/FilthyCur.java @@ -60,7 +60,7 @@ public DealtDamageLoseLifeTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/f/FinalParting.java b/Mage.Sets/src/mage/cards/f/FinalParting.java index 536aa4fe2b4f..3310134b2fea 100644 --- a/Mage.Sets/src/mage/cards/f/FinalParting.java +++ b/Mage.Sets/src/mage/cards/f/FinalParting.java @@ -43,7 +43,7 @@ class FinalPartingEffect extends OneShotEffect { public FinalPartingEffect() { super(Outcome.PutLandInPlay); - staticText = "Search your library for two cards. Put one into your hand and the other into your graveyard. Then shuffle your library"; + staticText = "Search your library for two cards. Put one into your hand and the other into your graveyard. Then shuffle"; } public FinalPartingEffect(final FinalPartingEffect effect) { diff --git a/Mage.Sets/src/mage/cards/f/FinaleOfDevastation.java b/Mage.Sets/src/mage/cards/f/FinaleOfDevastation.java index 7e49ab9f077b..948a31875783 100644 --- a/Mage.Sets/src/mage/cards/f/FinaleOfDevastation.java +++ b/Mage.Sets/src/mage/cards/f/FinaleOfDevastation.java @@ -10,7 +10,7 @@ import mage.filter.FilterCard; import mage.filter.common.FilterCreaturePermanent; import mage.constants.CardType; -import mage.abilities.effects.common.search.SearchLibraryGraveyardWithLessCMCPutIntoPlay; +import mage.abilities.effects.common.search.SearchLibraryGraveyardWithLessMVPutIntoPlay; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; @@ -34,7 +34,7 @@ public final class FinaleOfDevastation extends CardImpl { public FinaleOfDevastation(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{G}{G}"); // Search your library and/or graveyard for a creature card with converted mana cost X or less and put it onto the battlefield. If you search your library this way, shuffle it. - this.getSpellAbility().addEffect(new SearchLibraryGraveyardWithLessCMCPutIntoPlay(filter)); + this.getSpellAbility().addEffect(new SearchLibraryGraveyardWithLessMVPutIntoPlay(filter)); // If X is 10 or more, creatures you control get +X/+X and gain haste until end of turn. this.getSpellAbility().addEffect(new FinaleOfDevastationEffect()); } diff --git a/Mage.Sets/src/mage/cards/f/FinaleOfPromise.java b/Mage.Sets/src/mage/cards/f/FinaleOfPromise.java index 248f78245dfa..f9372cbfcc58 100644 --- a/Mage.Sets/src/mage/cards/f/FinaleOfPromise.java +++ b/Mage.Sets/src/mage/cards/f/FinaleOfPromise.java @@ -1,5 +1,6 @@ package mage.cards.f; +import mage.ApprovingObject; import mage.abilities.Ability; import mage.abilities.dynamicvalue.common.ManacostVariableValue; import mage.abilities.effects.ContinuousEffect; @@ -10,10 +11,9 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; import mage.game.stack.Spell; import mage.players.Player; @@ -24,7 +24,6 @@ import java.util.*; import java.util.stream.Collectors; -import mage.ApprovingObject; /** * @author JayDi85 @@ -71,13 +70,13 @@ public void adjustTargets(Ability ability, Game game) { // <= must be replaced to <= for html view FilterCard filter1 = FinaleOfPromise.filterInstant.copy(); - filter1.setMessage("up to one INSTANT card from your graveyard with CMC <= " + xValue + " (target 1 of 2)"); - filter1.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, xValue + 1)); + filter1.setMessage("up to one INSTANT card from your graveyard with mana value <= " + xValue + " (target 1 of 2)"); + filter1.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); ability.addTarget(new TargetCardInYourGraveyard(0, 1, filter1)); FilterCard filter2 = FinaleOfPromise.filterSorcery.copy(); - filter2.setMessage("up to one SORCERY card from your graveyard with CMC <=" + xValue + " (target 2 of 2)"); - filter2.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, xValue + 1)); + filter2.setMessage("up to one SORCERY card from your graveyard with mana value <=" + xValue + " (target 2 of 2)"); + filter2.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); ability.addTarget(new TargetCardInYourGraveyard(0, 1, filter2)); } } @@ -87,7 +86,7 @@ class FinaleOfPromiseEffect extends OneShotEffect { public FinaleOfPromiseEffect() { super(Outcome.PlayForFree); this.staticText = "You may cast up to one target instant card and/or up to one target sorcery card from your graveyard " - + "each with converted mana cost X or less without paying their mana costs. If a card cast this way would " + + "each with mana value X or less without paying their mana costs. If a card cast this way would " + "be put into your graveyard this turn, exile it instead. If X is 10 or more, copy each of those spells " + "twice. You may choose new targets for the copies."; } @@ -155,7 +154,6 @@ public boolean apply(Game game, Ability source) { Spell spell = game.getStack().getSpell(card.getId()); if (spell != null) { spell.createCopyOnStack(game, source, controller.getId(), true, 2); - game.informPlayers(controller.getLogName() + " copies " + spell.getName() + " twice."); } } } @@ -192,8 +190,7 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { if (controller != null) { Card card = game.getCard(getTargetPointer().getFirst(game, source)); if (card != null) { - card.moveToExile(null, "", source, game); - return true; + return controller.moveCards(card, Zone.EXILED, source, game); } } return false; diff --git a/Mage.Sets/src/mage/cards/f/FinaleOfRevelation.java b/Mage.Sets/src/mage/cards/f/FinaleOfRevelation.java index 30fb5dc41e9e..2c4da25a1586 100644 --- a/Mage.Sets/src/mage/cards/f/FinaleOfRevelation.java +++ b/Mage.Sets/src/mage/cards/f/FinaleOfRevelation.java @@ -27,7 +27,7 @@ public FinaleOfRevelation(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new FinaleOfRevelationEffect()); // Exile Finale of Revelation. - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private FinaleOfRevelation(final FinaleOfRevelation card) { diff --git a/Mage.Sets/src/mage/cards/f/FireAnts.java b/Mage.Sets/src/mage/cards/f/FireAnts.java index f048643060ab..eba7657b4581 100644 --- a/Mage.Sets/src/mage/cards/f/FireAnts.java +++ b/Mage.Sets/src/mage/cards/f/FireAnts.java @@ -15,7 +15,7 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/f/FireBowman.java b/Mage.Sets/src/mage/cards/f/FireBowman.java index 18712393fef7..7b98f5f4d38e 100644 --- a/Mage.Sets/src/mage/cards/f/FireBowman.java +++ b/Mage.Sets/src/mage/cards/f/FireBowman.java @@ -1,7 +1,5 @@ - package mage.cards.f; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.ActivateIfConditionActivatedAbility; @@ -15,14 +13,15 @@ import mage.constants.Zone; import mage.target.common.TargetAnyTarget; +import java.util.UUID; + /** - * * @author fireshoes */ public final class FireBowman extends CardImpl { public FireBowman(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SOLDIER); this.subtype.add(SubType.ARCHER); @@ -30,8 +29,10 @@ public FireBowman(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(1); // Sacrifice Fire Bowman: Fire Bowman deals 1 damage to any target. Activate this ability only during your turn, before attackers are declared. - Ability ability = new ActivateIfConditionActivatedAbility(Zone.BATTLEFIELD, - new DamageTargetEffect(1), new SacrificeSourceCost(), MyTurnBeforeAttackersDeclaredCondition.instance); + Ability ability = new ActivateIfConditionActivatedAbility( + Zone.BATTLEFIELD, new DamageTargetEffect(1, "it"), + new SacrificeSourceCost(), MyTurnBeforeAttackersDeclaredCondition.instance + ); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/f/FireJuggler.java b/Mage.Sets/src/mage/cards/f/FireJuggler.java index e2fdf018e779..e68eb240e1c2 100644 --- a/Mage.Sets/src/mage/cards/f/FireJuggler.java +++ b/Mage.Sets/src/mage/cards/f/FireJuggler.java @@ -28,7 +28,7 @@ public FireJuggler(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(2); // Whenever Fire Juggler becomes blocked, clash with an opponent. If you win, Fire Juggler deals 4 damage to each creature blocking it. - FilterPermanent filter = new FilterPermanent("each creature blocking it"); + FilterPermanent filter = new FilterPermanent("creature blocking it"); filter.add(new BlockingAttackerIdPredicate(this.getId())); this.addAbility(new BecomesBlockedSourceTriggeredAbility(new DoIfClashWonEffect(new DamageAllEffect(4,filter)),false)); } diff --git a/Mage.Sets/src/mage/cards/f/FireServant.java b/Mage.Sets/src/mage/cards/f/FireServant.java index 544923dd53c7..d923e23e7b8c 100644 --- a/Mage.Sets/src/mage/cards/f/FireServant.java +++ b/Mage.Sets/src/mage/cards/f/FireServant.java @@ -65,8 +65,7 @@ public FireServantEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE || - event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER || + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT || event.getType() == GameEvent.EventType.DAMAGE_PLAYER; } @@ -76,7 +75,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { return spell != null && spell.isControlledBy(source.getControllerId()) && spell.getColor(game).isRed() && - (spell.isInstant() || spell.isSorcery()); + spell.isInstantOrSorcery(); } @Override diff --git a/Mage.Sets/src/mage/cards/f/Fireball.java b/Mage.Sets/src/mage/cards/f/Fireball.java index a47a873f2258..69565eda8423 100644 --- a/Mage.Sets/src/mage/cards/f/Fireball.java +++ b/Mage.Sets/src/mage/cards/f/Fireball.java @@ -56,7 +56,8 @@ class FireballEffect extends OneShotEffect { public FireballEffect() { super(Outcome.Damage); - staticText = "{this} deals X damage divided evenly, rounded down, among any number of target creatures and/or players.\n {this} costs {1} more to cast for each target beyond the first"; + staticText = "this spell costs {1} more to cast for each target beyond the first.
{this} deals " + + "X damage divided evenly, rounded down, among any number of target creatures and/or players."; } public FireballEffect(final FireballEffect effect) { diff --git a/Mage.Sets/src/mage/cards/f/FirefluxSquad.java b/Mage.Sets/src/mage/cards/f/FirefluxSquad.java index 85ac79b014a3..90c6ea834a68 100644 --- a/Mage.Sets/src/mage/cards/f/FirefluxSquad.java +++ b/Mage.Sets/src/mage/cards/f/FirefluxSquad.java @@ -12,7 +12,7 @@ import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.AttackingPredicate; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/f/FiremaneAngel.java b/Mage.Sets/src/mage/cards/f/FiremaneAngel.java index bb4822bb3118..5fb3cc2cc29a 100644 --- a/Mage.Sets/src/mage/cards/f/FiremaneAngel.java +++ b/Mage.Sets/src/mage/cards/f/FiremaneAngel.java @@ -44,11 +44,11 @@ public FiremaneAngel(UUID ownerId, CardSetInfo setInfo) { Ability ability = new ConditionalInterveningIfTriggeredAbility( new BeginningOfUpkeepTriggeredAbility(Zone.ALL, new GainLifeEffect(1), TargetController.YOU, true), SourceOnBattlefieldOrGraveyardCondition.instance, - "At the beginning of your upkeep, if {this} is in your graveyard or on the battlefield, you may gain 1 life"); + "At the beginning of your upkeep, if {this} is in your graveyard or on the battlefield, you may gain 1 life."); this.addAbility(ability); // {6}{R}{R}{W}{W}: Return Firemane Angel from your graveyard to the battlefield. Activate this ability only during your upkeep. this.addAbility(new ConditionalActivatedAbility(Zone.GRAVEYARD, - new ReturnSourceFromGraveyardToBattlefieldEffect(), new ManaCostsImpl("{6}{R}{R}{W}{W}"), new IsStepCondition(PhaseStep.UPKEEP), null)); + new ReturnSourceFromGraveyardToBattlefieldEffect(false, false), new ManaCostsImpl("{6}{R}{R}{W}{W}"), new IsStepCondition(PhaseStep.UPKEEP), null)); } private FiremaneAngel(final FiremaneAngel card) { diff --git a/Mage.Sets/src/mage/cards/f/FiremawKavu.java b/Mage.Sets/src/mage/cards/f/FiremawKavu.java index 1ed03c1ef1d9..d56097266918 100644 --- a/Mage.Sets/src/mage/cards/f/FiremawKavu.java +++ b/Mage.Sets/src/mage/cards/f/FiremawKavu.java @@ -35,7 +35,7 @@ public FiremawKavu(UUID ownerId, CardSetInfo setInfo) { this.addAbility(ability); // When Firemaw Kavu leaves the battlefield, it deals 4 damage to target creature. - ability = new LeavesBattlefieldTriggeredAbility(new DamageTargetEffect(4), false); + ability = new LeavesBattlefieldTriggeredAbility(new DamageTargetEffect(4, "it"), false); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/f/FiremindsForesight.java b/Mage.Sets/src/mage/cards/f/FiremindsForesight.java index 1df71aff8f4f..7029380eb55a 100644 --- a/Mage.Sets/src/mage/cards/f/FiremindsForesight.java +++ b/Mage.Sets/src/mage/cards/f/FiremindsForesight.java @@ -1,7 +1,5 @@ - package mage.cards.f; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.*; @@ -10,20 +8,22 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class FiremindsForesight extends CardImpl { public FiremindsForesight(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{5}{U}{R}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{5}{U}{R}"); // Search your library for an instant card with converted mana cost 3, reveal it, // and put it into your hand. Then repeat this process for instant cards with @@ -41,14 +41,27 @@ public FiremindsForesight copy() { } } -class FiremindsForesightSearchEffect extends OneShotEffect { +class FiremindsForesightSearchEffect extends OneShotEffect { + + private static final Map filterMap = new HashMap<>(); + + static { + for (int cmc = 3; cmc > 0; cmc--) { + FilterCard filter = new FilterCard("instant card with mana value " + cmc); + filter.add(CardType.INSTANT.getPredicate()); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, cmc)); + filterMap.put(cmc, filter); + } + } - public FiremindsForesightSearchEffect() { + FiremindsForesightSearchEffect() { super(Outcome.DrawCard); - staticText = "Search your library for an instant card with converted mana cost 3, reveal it, and put it into your hand. Then repeat this process for instant cards with converted mana costs 2 and 1. Then shuffle your library"; + staticText = "Search your library for an instant card with mana value 3, " + + "reveal it, and put it into your hand. Then repeat this process " + + "for instant cards with mana values 2 and 1. Then shuffle"; } - public FiremindsForesightSearchEffect(final FiremindsForesightSearchEffect effect) { + private FiremindsForesightSearchEffect(final FiremindsForesightSearchEffect effect) { super(effect); } @@ -60,42 +73,23 @@ public FiremindsForesightSearchEffect copy() { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - Card sourceCard = game.getCard(source.getSourceId()); - if (player == null || sourceCard == null) { + if (player == null) { return false; } - int cardsCount; Cards cardToReveal = new CardsImpl(); - Cards cardsInLibrary = new CardsImpl(); - cardsInLibrary.addAll(player.getLibrary().getCards(game)); - - for (int cmc=3; cmc > 0; cmc--) { - FilterCard filter = new FilterCard("instant card with converted mana cost " + cmc); - filter.add(CardType.INSTANT.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, cmc)); - - - cardsCount = cardsInLibrary.count(filter, game); - if (cardsCount > 0) { - TargetCardInLibrary target = new TargetCardInLibrary(0, 1, filter); - if (player.searchLibrary(target, source, game)) { - for (UUID cardId: target.getTargets()) { - Card card = player.getLibrary().remove(cardId, game); - if (card != null){ - card.moveToZone(Zone.HAND, source, game, false); - game.informPlayers(sourceCard.getName()+": " + player.getLogName() + " chose " + card.getName() ); - cardsInLibrary.remove(card); - cardToReveal.add(card); - player.revealCards(sourceCard.getName(), cardToReveal, game); - } - } - } - } else { - player.lookAtCards(filter.getMessage(), cardsInLibrary, game); + for (int cmc = 3; cmc > 0; cmc--) { + TargetCardInLibrary target = new TargetCardInLibrary(filterMap.get(cmc)); + player.searchLibrary(target, source, game); + Card card = player.getLibrary().getCard(target.getFirstTarget(), game); + if (card == null) { + continue; } + cardToReveal.clear(); + cardToReveal.add(card); + player.revealCards(source, cardToReveal, game); + player.moveCards(cardToReveal, Zone.HAND, source, game); } - player.shuffleLibrary(source, game); return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/f/FiresOfInvention.java b/Mage.Sets/src/mage/cards/f/FiresOfInvention.java index b87e4c6185cd..2383d18bfcbe 100644 --- a/Mage.Sets/src/mage/cards/f/FiresOfInvention.java +++ b/Mage.Sets/src/mage/cards/f/FiresOfInvention.java @@ -26,7 +26,7 @@ public final class FiresOfInvention extends CardImpl { private static final FilterCard filter - = new FilterCard("spells with converted mana cost less than or equal to the number of lands you control"); + = new FilterCard("spells with mana value less than or equal to the number of lands you control"); static { filter.add(FiresOfInventionPredicate.instance); @@ -57,7 +57,7 @@ enum FiresOfInventionPredicate implements ObjectSourcePlayerPredicate input, Game game) { - return input.getObject().getConvertedManaCost() <= + return input.getObject().getManaValue() <= game.getBattlefield().countAll(StaticFilters.FILTER_LAND, game.getControllerId(input.getSourceId()), game); } } diff --git a/Mage.Sets/src/mage/cards/f/FirstDayOfClass.java b/Mage.Sets/src/mage/cards/f/FirstDayOfClass.java new file mode 100644 index 000000000000..8f43c09fee42 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FirstDayOfClass.java @@ -0,0 +1,82 @@ +package mage.cards.f; + +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.LearnEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class FirstDayOfClass extends CardImpl { + + public FirstDayOfClass(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); + + // Whenever a creature enters the battlefield under your control this turn, put a +1/+1 counter on it and it gains haste until end of turn. + this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new FirstDayOfClassTriggeredAbility())); + + // Learn. + this.getSpellAbility().addEffect(new LearnEffect().concatBy("
")); + } + + private FirstDayOfClass(final FirstDayOfClass card) { + super(card); + } + + @Override + public FirstDayOfClass copy() { + return new FirstDayOfClass(this); + } +} + +class FirstDayOfClassTriggeredAbility extends DelayedTriggeredAbility { + + public FirstDayOfClassTriggeredAbility() { + super(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), Duration.EndOfTurn, false); + this.addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn)); + } + + public FirstDayOfClassTriggeredAbility(FirstDayOfClassTriggeredAbility ability) { + super(ability); + } + + @Override + public FirstDayOfClassTriggeredAbility copy() { + return new FirstDayOfClassTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent.isCreature() && permanent.isControlledBy(this.getControllerId())) { + getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game)); + return true; + } + return false; + } + + @Override + public String getRule() { + return "Whenever a creature enters the battlefield under your control this turn, put a +1/+1 counter on it and it gains haste until end of turn"; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/f/FiveAlarmFire.java b/Mage.Sets/src/mage/cards/f/FiveAlarmFire.java index a38fda36deb0..61ceaef19a59 100644 --- a/Mage.Sets/src/mage/cards/f/FiveAlarmFire.java +++ b/Mage.Sets/src/mage/cards/f/FiveAlarmFire.java @@ -78,16 +78,14 @@ public FiveAlarmFireTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT || event.getType() == GameEvent.EventType.DAMAGED_PLAYER || event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_PRE; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.DAMAGED_CREATURE - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER + if (event.getType() == GameEvent.EventType.DAMAGED_PERMANENT || event.getType() == GameEvent.EventType.DAMAGED_PLAYER) { if (((DamagedEvent) event).isCombatDamage() && !triggeringCreatures.contains(event.getSourceId())) { Permanent permanent = game.getPermanent(event.getSourceId()); diff --git a/Mage.Sets/src/mage/cards/f/FlameKinWarScout.java b/Mage.Sets/src/mage/cards/f/FlameKinWarScout.java index 5e34c8ebb7ed..5787ae25a7a6 100644 --- a/Mage.Sets/src/mage/cards/f/FlameKinWarScout.java +++ b/Mage.Sets/src/mage/cards/f/FlameKinWarScout.java @@ -16,7 +16,7 @@ import mage.constants.SetTargetPointer; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/f/FlameSpill.java b/Mage.Sets/src/mage/cards/f/FlameSpill.java index 6c3a159d454e..c66f856e0c8d 100644 --- a/Mage.Sets/src/mage/cards/f/FlameSpill.java +++ b/Mage.Sets/src/mage/cards/f/FlameSpill.java @@ -1,22 +1,13 @@ package mage.cards.f; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.effects.common.DamageWithExcessEffect; 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.players.Player; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; -import static mage.game.combat.CombatGroup.getLethalDamage; - /** * @author TheElk801 */ @@ -26,7 +17,7 @@ public FlameSpill(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); // Flame Spill deals 4 damage to target creature. Excess damage is dealt to that creature's controller instead. - this.getSpellAbility().addEffect(new FlameSpillEffect()); + this.getSpellAbility().addEffect(new DamageWithExcessEffect(4)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } @@ -39,41 +30,3 @@ public FlameSpill copy() { return new FlameSpill(this); } } - -class FlameSpillEffect extends OneShotEffect { - - FlameSpillEffect() { - super(Outcome.Benefit); - staticText = "{this} deals 4 damage to target creature. " + - "Excess damage is dealt to that creature's controller instead."; - } - - private FlameSpillEffect(final FlameSpillEffect effect) { - super(effect); - } - - @Override - public FlameSpillEffect copy() { - return new FlameSpillEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getFirstTarget()); - MageObject sourceObject = source.getSourceObject(game); - if (permanent == null || sourceObject == null) { - return false; - } - int lethal = getLethalDamage(permanent, game); - if (sourceObject.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) { - lethal = Math.min(lethal, 1); - } - lethal = Math.min(lethal, 4); - permanent.damage(lethal, source.getSourceId(), source, game); - Player player = game.getPlayer(permanent.getControllerId()); - if (player != null && lethal < 4) { - player.damage(4 - lethal, source.getSourceId(), source, game); - } - return true; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/f/FlamebladeAngel.java b/Mage.Sets/src/mage/cards/f/FlamebladeAngel.java index 85c72fdc35a7..00fb7c6b1d8b 100644 --- a/Mage.Sets/src/mage/cards/f/FlamebladeAngel.java +++ b/Mage.Sets/src/mage/cards/f/FlamebladeAngel.java @@ -63,8 +63,7 @@ public FlamebladeAngelTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT || event.getType() == GameEvent.EventType.DAMAGED_PLAYER; } diff --git a/Mage.Sets/src/mage/cards/f/FlameheartWerewolf.java b/Mage.Sets/src/mage/cards/f/FlameheartWerewolf.java index 7493dd9b2ae7..84bf2aba136d 100644 --- a/Mage.Sets/src/mage/cards/f/FlameheartWerewolf.java +++ b/Mage.Sets/src/mage/cards/f/FlameheartWerewolf.java @@ -1,25 +1,18 @@ - package mage.cards.f; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; -import mage.abilities.effects.common.TransformSourceEffect; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; import mage.filter.StaticFilters; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class FlameheartWerewolf extends CardImpl { @@ -40,8 +33,7 @@ public FlameheartWerewolf(UUID ownerId, CardSetInfo setInfo) { StaticFilters.FILTER_PERMANENT_CREATURE, false, null, true)); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Flameheart Werewolf. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private FlameheartWerewolf(final FlameheartWerewolf card) { diff --git a/Mage.Sets/src/mage/cards/f/FlamekinHerald.java b/Mage.Sets/src/mage/cards/f/FlamekinHerald.java index 1ba9d817f640..256420b7072d 100644 --- a/Mage.Sets/src/mage/cards/f/FlamekinHerald.java +++ b/Mage.Sets/src/mage/cards/f/FlamekinHerald.java @@ -9,7 +9,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.FilterSpell; -import mage.filter.predicate.permanent.CommanderPredicate; +import mage.filter.predicate.mageobject.CommanderPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/f/FlamerushRider.java b/Mage.Sets/src/mage/cards/f/FlamerushRider.java index be9f5176bef3..2e8e079c9378 100644 --- a/Mage.Sets/src/mage/cards/f/FlamerushRider.java +++ b/Mage.Sets/src/mage/cards/f/FlamerushRider.java @@ -18,7 +18,7 @@ import mage.constants.SubType; import mage.constants.Outcome; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.AttackingPredicate; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/f/FlamescrollCelebrant.java b/Mage.Sets/src/mage/cards/f/FlamescrollCelebrant.java new file mode 100644 index 000000000000..af8f76575a4d --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FlamescrollCelebrant.java @@ -0,0 +1,169 @@ +package mage.cards.f; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.ExileSpellEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.mana.ActivatedManaAbilityImpl; +import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.StackAbility; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FlamescrollCelebrant extends ModalDoubleFacesCard { + + public FlamescrollCelebrant(UUID ownerId, CardSetInfo setInfo) { + super( + ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.HUMAN, SubType.SHAMAN}, "{1}{R}", + "Revel in Silence", + new CardType[]{CardType.INSTANT}, new SubType[]{}, "{W}{W}" + ); + + + // 1. + // Flamescroll Celebrant + // Creature - Human Shaman + this.getLeftHalfCard().setPT(2, 1); + + // Whenever an opponent activates an ability that isn't a mana ability, Flamescroll Celebrant deals 1 damage to that player. + this.getLeftHalfCard().addAbility(new FlamescrollCelebrantTriggeredAbility()); + + // {1}{R}: Flamescroll Celebrant gets +2/+0 until end of turn. + this.getLeftHalfCard().addAbility(new SimpleActivatedAbility( + new BoostSourceEffect(2, 0, Duration.EndOfTurn), new ManaCostsImpl("{1}{R}") + )); + + // 2. + // Revel in Silence + // Instant + // Your opponents can't cast spells or activate planeswalkers' loyalty abilities this turn. + this.getRightHalfCard().getSpellAbility().addEffect(new RevelInSilenceEffect()); + + // Exile Revel in Silence. + this.getRightHalfCard().getSpellAbility().addEffect(new ExileSpellEffect().concatBy("
")); + } + + private FlamescrollCelebrant(final FlamescrollCelebrant card) { + super(card); + } + + @Override + public FlamescrollCelebrant copy() { + return new FlamescrollCelebrant(this); + } +} + +class FlamescrollCelebrantTriggeredAbility extends TriggeredAbilityImpl { + + FlamescrollCelebrantTriggeredAbility() { + super(Zone.BATTLEFIELD, new DamageTargetEffect(StaticValue.get(2), true, "that player", true)); + } + + private FlamescrollCelebrantTriggeredAbility(final FlamescrollCelebrantTriggeredAbility ability) { + super(ability); + } + + @Override + public FlamescrollCelebrantTriggeredAbility copy() { + return new FlamescrollCelebrantTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ACTIVATED_ABILITY; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!game.getOpponents(event.getPlayerId()).contains(getControllerId())) { + return false; + } + StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); + if (stackAbility.getStackAbility() instanceof ActivatedManaAbilityImpl) { + return false; + } + getEffects().setTargetPointer(new FixedTarget(event.getPlayerId())); + return true; + } + + @Override + public String getRule() { + return "Whenever an opponent activates an ability that isn't a mana ability, " + + "{this} deals 1 damage to that player."; + } +} + +class RevelInSilenceEffect extends ContinuousRuleModifyingEffectImpl { + + RevelInSilenceEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "Your opponents can't cast spells or activate planeswalkers' loyalty abilities this turn."; + } + + private RevelInSilenceEffect(final RevelInSilenceEffect effect) { + super(effect); + } + + @Override + public RevelInSilenceEffect copy() { + return new RevelInSilenceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public String getInfoMessage(Ability source, GameEvent event, Game game) { + Player activePlayer = game.getPlayer(game.getActivePlayerId()); + MageObject mageObject = game.getObject(source.getSourceId()); + if (activePlayer == null || mageObject == null) { + return null; + } + return "You can't cast spells or activate planeswalkers' loyalty abilities this turn (" + mageObject.getLogName() + ')'; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.CAST_SPELL + || event.getType() == GameEvent.EventType.ACTIVATE_ABILITY; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (!game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { + return false; + } + switch (event.getType()) { + case CAST_SPELL: + return true; + case ACTIVATE_ABILITY: + Ability ability = game.getAbility(event.getTargetId(), event.getSourceId()).orElse(null); + if (!(ability instanceof LoyaltyAbility)) { + return false; + } + Permanent permanent = game.getPermanent(event.getSourceId()); + return permanent != null && permanent.isPlaneswalker(); + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/f/FlashConscription.java b/Mage.Sets/src/mage/cards/f/FlashConscription.java index d0eaf93e8e8c..3600f8622f37 100644 --- a/Mage.Sets/src/mage/cards/f/FlashConscription.java +++ b/Mage.Sets/src/mage/cards/f/FlashConscription.java @@ -72,9 +72,8 @@ public FlashConscriptionTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE - || event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT + || event.getType() == GameEvent.EventType.DAMAGED_PLAYER; } @Override diff --git a/Mage.Sets/src/mage/cards/f/FlayerDrone.java b/Mage.Sets/src/mage/cards/f/FlayerDrone.java index 3d26f03461b0..cd80ba19f883 100644 --- a/Mage.Sets/src/mage/cards/f/FlayerDrone.java +++ b/Mage.Sets/src/mage/cards/f/FlayerDrone.java @@ -15,7 +15,7 @@ import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorlessPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetOpponent; /** diff --git a/Mage.Sets/src/mage/cards/f/FleetingAven.java b/Mage.Sets/src/mage/cards/f/FleetingAven.java index 091e32dd7cef..0631d6fc393a 100644 --- a/Mage.Sets/src/mage/cards/f/FleetingAven.java +++ b/Mage.Sets/src/mage/cards/f/FleetingAven.java @@ -29,7 +29,7 @@ public FleetingAven(UUID ownerId, CardSetInfo setInfo) { this.addAbility(FlyingAbility.getInstance()); // Whenever a player cycles a card, return Fleeting Aven to its owner's hand. - this.addAbility(new CycleAllTriggeredAbility(new ReturnToHandSourceEffect(true), true)); + this.addAbility(new CycleAllTriggeredAbility(new ReturnToHandSourceEffect(true), false)); } private FleetingAven(final FleetingAven card) { diff --git a/Mage.Sets/src/mage/cards/f/FleshBlood.java b/Mage.Sets/src/mage/cards/f/FleshBlood.java index ae1807408ab6..e9d240ccd546 100644 --- a/Mage.Sets/src/mage/cards/f/FleshBlood.java +++ b/Mage.Sets/src/mage/cards/f/FleshBlood.java @@ -39,7 +39,6 @@ public FleshBlood(UUID ownerId, CardSetInfo setInfo) { getRightHalfCard().getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); getRightHalfCard().getSpellAbility().addTarget(new TargetAnyTarget()); getRightHalfCard().getSpellAbility().addEffect(new BloodEffect()); - } private FleshBlood(final FleshBlood card) { @@ -65,19 +64,20 @@ public FleshEffect(final FleshEffect effect) { @Override public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); Card targetCard = game.getCard(source.getFirstTarget()); - if (targetCard != null) { - int power = targetCard.getPower().getValue(); - targetCard.moveToExile(null, null, source, game); - if (power > 0) { - Permanent targetCreature = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if (targetCreature != null) { - targetCreature.addCounters(CounterType.P1P1.createInstance(power), source.getControllerId(), source, game); - } + if (player == null || targetCard == null) { + return false; + } + int power = targetCard.getPower().getValue(); + player.moveCards(targetCard, Zone.EXILED, source, game); + if (power > 0) { + Permanent targetCreature = game.getPermanent(source.getTargets().get(1).getFirstTarget()); + if (targetCreature != null) { + targetCreature.addCounters(CounterType.P1P1.createInstance(power), source.getControllerId(), source, game); } - return true; } - return false; + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/f/FleshReaver.java b/Mage.Sets/src/mage/cards/f/FleshReaver.java index 6f0323a0e571..9ae154836ab9 100644 --- a/Mage.Sets/src/mage/cards/f/FleshReaver.java +++ b/Mage.Sets/src/mage/cards/f/FleshReaver.java @@ -3,7 +3,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -50,7 +49,7 @@ class FleshReaverTriggeredAbility extends TriggeredAbilityImpl { super(Zone.BATTLEFIELD, new FleshReaverEffect()); } - FleshReaverTriggeredAbility(final FleshReaverTriggeredAbility effect) { + private FleshReaverTriggeredAbility(final FleshReaverTriggeredAbility effect) { super(effect); } @@ -61,23 +60,22 @@ public FleshReaverTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE || event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT + || event.getType() == GameEvent.EventType.DAMAGED_PLAYER; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (!event.getSourceId().equals(sourceId)) { + if (!event.getSourceId().equals(getSourceId())) { return false; } - if (event.getType() == GameEvent.EventType.DAMAGED_PLAYER) { - if (event.getTargetId().equals(controllerId)) { - return false; - } + Permanent permanent = game.getPermanent(event.getTargetId()); + if ((permanent != null && permanent.isCreature()) + || game.getOpponents(event.getTargetId()).contains(getControllerId())) { + this.getEffects().setValue("damage", event.getAmount()); + return true; } - for (Effect effect : this.getEffects()) { - effect.setValue("damage", event.getAmount()); - } - return true; + return false; } @Override @@ -94,7 +92,7 @@ class FleshReaverEffect extends OneShotEffect { this.staticText = "{this} deals that much damage to you."; } - FleshReaverEffect(final FleshReaverEffect effect) { + private FleshReaverEffect(final FleshReaverEffect effect) { super(effect); } diff --git a/Mage.Sets/src/mage/cards/f/FleshmadSteed.java b/Mage.Sets/src/mage/cards/f/FleshmadSteed.java index 0f3c23e06961..3485ae768186 100644 --- a/Mage.Sets/src/mage/cards/f/FleshmadSteed.java +++ b/Mage.Sets/src/mage/cards/f/FleshmadSteed.java @@ -10,7 +10,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/f/Fleshwrither.java b/Mage.Sets/src/mage/cards/f/Fleshwrither.java index ca1ac8f7a7ca..52f82a49f739 100644 --- a/Mage.Sets/src/mage/cards/f/Fleshwrither.java +++ b/Mage.Sets/src/mage/cards/f/Fleshwrither.java @@ -20,7 +20,7 @@ import mage.constants.TimingRule; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; @@ -59,7 +59,7 @@ class FleshwritherEffect extends OneShotEffect { FleshwritherEffect() { super(Outcome.Benefit); - staticText = "Transfigure: Sacrifice this creature: Search your library for a creature card with the same converted mana cost as this creature and put that card onto the battlefield"; + staticText = "Transfigure: Sacrifice this creature: Search your library for a creature card with the same mana value as this creature and put that card onto the battlefield"; } FleshwritherEffect(final FleshwritherEffect effect) { @@ -71,8 +71,8 @@ public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = game.getObject(source.getSourceId()); if (sourceObject != null && controller != null) { - FilterCreatureCard filter = new FilterCreatureCard("creature with converted mana cost " + sourceObject.getConvertedManaCost()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, sourceObject.getConvertedManaCost())); + FilterCreatureCard filter = new FilterCreatureCard("creature with mana value " + sourceObject.getManaValue()); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, sourceObject.getManaValue())); TargetCardInLibrary target = new TargetCardInLibrary(1, filter); if (controller.searchLibrary(target, source, game)) { if (!target.getTargets().isEmpty()) { diff --git a/Mage.Sets/src/mage/cards/f/FlickeringSpirit.java b/Mage.Sets/src/mage/cards/f/FlickeringSpirit.java index cbaacbb15947..ce116dafb5f4 100644 --- a/Mage.Sets/src/mage/cards/f/FlickeringSpirit.java +++ b/Mage.Sets/src/mage/cards/f/FlickeringSpirit.java @@ -1,7 +1,5 @@ - package mage.cards.f; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -17,9 +15,11 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class FlickeringSpirit extends CardImpl { @@ -34,8 +34,7 @@ public FlickeringSpirit(UUID ownerId, CardSetInfo setInfo) { this.addAbility(FlyingAbility.getInstance()); // {3}{W}: Exile Flickering Spirit, then return it to the battlefield under its owner's control. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new FlickeringSpiritEffect(), new ManaCostsImpl("{3}{W}"))); - + this.addAbility(new SimpleActivatedAbility(new FlickeringSpiritEffect(), new ManaCostsImpl("{3}{W}"))); } private FlickeringSpirit(final FlickeringSpirit card) { @@ -50,12 +49,12 @@ public FlickeringSpirit copy() { class FlickeringSpiritEffect extends OneShotEffect { - public FlickeringSpiritEffect() { + FlickeringSpiritEffect() { super(Outcome.Neutral); this.staticText = "Exile {this}, then return it to the battlefield under its owner's control"; } - public FlickeringSpiritEffect(final FlickeringSpiritEffect effect) { + private FlickeringSpiritEffect(final FlickeringSpiritEffect effect) { super(effect); } @@ -66,15 +65,14 @@ public FlickeringSpiritEffect copy() { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - if (permanent.moveToExile(source.getSourceId(), "Flickering Spirit", source, game)) { - Card card = game.getExile().getCard(source.getSourceId(), game); - if (card != null) { - return card.moveToZone(Zone.BATTLEFIELD, source, game, false); - } - } + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent == null || player == null) { + return false; } - return false; + Card card = permanent.getMainCard(); + player.moveCards(card, Zone.EXILED, source, game); + player.moveCards(card, Zone.BATTLEFIELD, source, game, false, false, true, null); + return true; } } diff --git a/Mage.Sets/src/mage/cards/f/Flickerwisp.java b/Mage.Sets/src/mage/cards/f/Flickerwisp.java index 4da4cc0d8dda..f009d348eba6 100644 --- a/Mage.Sets/src/mage/cards/f/Flickerwisp.java +++ b/Mage.Sets/src/mage/cards/f/Flickerwisp.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/f/FloodOfRecollection.java b/Mage.Sets/src/mage/cards/f/FloodOfRecollection.java index 5229fde4b462..4e53027695e3 100644 --- a/Mage.Sets/src/mage/cards/f/FloodOfRecollection.java +++ b/Mage.Sets/src/mage/cards/f/FloodOfRecollection.java @@ -22,7 +22,7 @@ public FloodOfRecollection(UUID ownerId, CardSetInfo setInfo) { // Return target instant or sorcery card from your graveyard to your hand. Exile Flood of Recollection. this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(new FilterInstantOrSorceryCard("instant or sorcery card from your graveyard"))); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private FloodOfRecollection(final FloodOfRecollection card) { diff --git a/Mage.Sets/src/mage/cards/f/FloodPlain.java b/Mage.Sets/src/mage/cards/f/FloodPlain.java index 5b5ac1d3a039..00b12b1f3181 100644 --- a/Mage.Sets/src/mage/cards/f/FloodPlain.java +++ b/Mage.Sets/src/mage/cards/f/FloodPlain.java @@ -22,7 +22,7 @@ public FloodPlain(UUID ownerId, CardSetInfo setInfo) { // Flood Plain enters the battlefield tapped. this.addAbility(new EntersBattlefieldTappedAbility()); // {tap}, Sacrifice Flood Plain: Search your library for a Plains or Island card and put it onto the battlefield. Then shuffle your library. - this.addAbility(new FetchLandActivatedAbility(false, EnumSet.of(SubType.PLAINS, SubType.ISLAND))); + this.addAbility(new FetchLandActivatedAbility(false, SubType.PLAINS, SubType.ISLAND)); } private FloodPlain(final FloodPlain card) { diff --git a/Mage.Sets/src/mage/cards/f/FloodedStrand.java b/Mage.Sets/src/mage/cards/f/FloodedStrand.java index e2b3e4bebc63..5c42b4f7e286 100644 --- a/Mage.Sets/src/mage/cards/f/FloodedStrand.java +++ b/Mage.Sets/src/mage/cards/f/FloodedStrand.java @@ -21,7 +21,7 @@ public FloodedStrand(UUID ownerId, CardSetInfo setInfo) { this.frameColor = new ObjectColor("UW"); // {tap}, Pay 1 life, Sacrifice Flooded Strand: Search your library for a Plains or Island card and put it onto the battlefield. Then shuffle your library. - this.addAbility(new FetchLandActivatedAbility(EnumSet.of(SubType.PLAINS, SubType.ISLAND))); + this.addAbility(new FetchLandActivatedAbility(SubType.PLAINS, SubType.ISLAND)); } private FloodedStrand(final FloodedStrand card) { diff --git a/Mage.Sets/src/mage/cards/f/Fluctuator.java b/Mage.Sets/src/mage/cards/f/Fluctuator.java index 0682684babff..81885b8c4588 100644 --- a/Mage.Sets/src/mage/cards/f/Fluctuator.java +++ b/Mage.Sets/src/mage/cards/f/Fluctuator.java @@ -69,7 +69,7 @@ public boolean apply(Game game, Ability source, Ability abilityToModify) { } if (reduceMax > 0) { int reduce; - if (game.inCheckPlayableState() || !controller.isHuman()) { + if (game.inCheckPlayableState() || controller.isComputer()) { reduce = reduceMax; } else { ChoiceImpl choice = new ChoiceImpl(true); diff --git a/Mage.Sets/src/mage/cards/f/Flunk.java b/Mage.Sets/src/mage/cards/f/Flunk.java new file mode 100644 index 000000000000..08b8464e6d69 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/Flunk.java @@ -0,0 +1,66 @@ +package mage.cards.f; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Flunk extends CardImpl { + + public Flunk(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{B}"); + + // Target creature gets -X/-X until end of turn, where X is 7 minus the number of cards in that creature's controller's hand. + this.getSpellAbility().addEffect(new FlunkEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private Flunk(final Flunk card) { + super(card); + } + + @Override + public Flunk copy() { + return new Flunk(this); + } +} + +class FlunkEffect extends OneShotEffect { + + FlunkEffect() { + super(Outcome.Benefit); + staticText = "target creature gets -X/-X until end of turn, where X is 7 " + + "minus the number of cards in that creature's controller's hand"; + } + + private FlunkEffect(final FlunkEffect effect) { + super(effect); + } + + @Override + public FlunkEffect copy() { + return new FlunkEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(game.getControllerId(source.getFirstTarget())); + if (player == null) { + return false; + } + int xValue = Math.max(7 - player.getHand().size(), 0); + game.addEffect(new BoostTargetEffect(-xValue, -xValue), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/f/FoodChain.java b/Mage.Sets/src/mage/cards/f/FoodChain.java index de27c4b66491..ecd427045b4f 100644 --- a/Mage.Sets/src/mage/cards/f/FoodChain.java +++ b/Mage.Sets/src/mage/cards/f/FoodChain.java @@ -68,7 +68,7 @@ class FoodChainManaEffect extends ManaEffect { ConditionalManaBuilder manaBuilder = new FoodChainManaBuilder(); FoodChainManaEffect() { - this.staticText = "Add X mana of any one color, where X is 1 plus the exiled creature's converted mana cost. Spend this mana only to cast creature spells"; + this.staticText = "Add X mana of any one color, where X is 1 plus the exiled creature's mana value. Spend this mana only to cast creature spells"; } FoodChainManaEffect(final FoodChainManaEffect effect) { @@ -87,7 +87,7 @@ public List getNetMana(Game game, Ability source) { int cmc = -1; for (Permanent permanent : game.getBattlefield().getAllActivePermanents(source.getControllerId())) { if (permanent.isCreature()) { - cmc = Math.max(cmc, permanent.getManaCost().convertedManaCost()); + cmc = Math.max(cmc, permanent.getManaCost().manaValue()); } } if (cmc != -1) { @@ -113,7 +113,7 @@ public Mana produceMana(Game game, Ability source) { for (Cost cost : source.getCosts()) { if (cost.isPaid() && cost instanceof ExileTargetCost) { for (Card card : ((ExileTargetCost) cost).getPermanents()) { - manaCostExiled += card.getConvertedManaCost(); + manaCostExiled += card.getManaValue(); } } } diff --git a/Mage.Sets/src/mage/cards/f/ForbiddingSpirit.java b/Mage.Sets/src/mage/cards/f/ForbiddingSpirit.java index b96e38211d13..7757037dc9eb 100644 --- a/Mage.Sets/src/mage/cards/f/ForbiddingSpirit.java +++ b/Mage.Sets/src/mage/cards/f/ForbiddingSpirit.java @@ -31,7 +31,7 @@ public ForbiddingSpirit(UUID ownerId, CardSetInfo setInfo) { new ManaCostsImpl("{2}"), true ); effect.setDuration(Duration.UntilYourNextTurn); - effect.setText("until your next turn, creatures can't attack you or a planeswalker you control " + + effect.setText("until your next turn, creatures can't attack you or planeswalkers you control " + "unless their controller pays {2} for each of those creatures."); this.addAbility(new EntersBattlefieldTriggeredAbility(effect)); } diff --git a/Mage.Sets/src/mage/cards/f/ForceChoke.java b/Mage.Sets/src/mage/cards/f/ForceChoke.java index 3d29ddfaf853..bf916742af99 100644 --- a/Mage.Sets/src/mage/cards/f/ForceChoke.java +++ b/Mage.Sets/src/mage/cards/f/ForceChoke.java @@ -50,7 +50,7 @@ class ForceChokeEffect extends OneShotEffect { public ForceChokeEffect() { super(Outcome.ReturnToHand); this.staticText = "Counter target spell. Its controller may pay life " - + "equal to that spell's converted mana cost to return it to its owner's hand"; + + "equal to that spell's mana value to return it to its owner's hand"; } public ForceChokeEffect(final ForceChokeEffect effect) { @@ -68,10 +68,10 @@ public boolean apply(Game game, Ability source) { StackObject stackObject = (StackObject) game.getObject(getTargetPointer().getFirst(game, source)); Player objectController = game.getPlayer(stackObject.getControllerId()); if (player != null) { - Cost cost = new PayLifeCost(stackObject.getConvertedManaCost()); + Cost cost = new PayLifeCost(stackObject.getManaValue()); if (cost.canPay(source, source, objectController.getId(), game) && objectController.chooseUse(Outcome.LoseLife, "Pay " - + stackObject.getConvertedManaCost() + " life?", source, game) + + stackObject.getManaValue() + " life?", source, game) && cost.pay(source, game, source, objectController.getId(), false, null)) { objectController.moveCards((Card) stackObject, Zone.HAND, source, game); } else { diff --git a/Mage.Sets/src/mage/cards/f/ForceMastery.java b/Mage.Sets/src/mage/cards/f/ForceMastery.java index e2052c312b56..e20927051e3e 100644 --- a/Mage.Sets/src/mage/cards/f/ForceMastery.java +++ b/Mage.Sets/src/mage/cards/f/ForceMastery.java @@ -41,7 +41,7 @@ class ForceMasteryEffect extends OneShotEffect { ForceMasteryEffect() { super(Outcome.DrawCard); - this.staticText = "reveal the top card of your library and put that card into your hand. You gain life equal to its converted mana cost"; + this.staticText = "reveal the top card of your library and put that card into your hand. You gain life equal to its mana value"; } ForceMasteryEffect(final ForceMasteryEffect effect) { @@ -56,7 +56,7 @@ public boolean apply(Game game, Ability source) { if (card != null) { controller.revealCards(source, new CardsImpl(card), game); controller.moveCards(card, Zone.HAND, source, game); - controller.gainLife(card.getConvertedManaCost(), game, source); + controller.gainLife(card.getManaValue(), game, source); } return true; } diff --git a/Mage.Sets/src/mage/cards/f/ForceOfNature.java b/Mage.Sets/src/mage/cards/f/ForceOfNature.java index fa5386d94ec8..a9f48d6bab38 100644 --- a/Mage.Sets/src/mage/cards/f/ForceOfNature.java +++ b/Mage.Sets/src/mage/cards/f/ForceOfNature.java @@ -68,8 +68,7 @@ public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { Cost cost = new ManaCostsImpl("{G}{G}{G}{G}"); - String message = "Would you like to pay {G}{G}{G}{G} to prevent taking 8 damage from {this}?"; - if (!(controller.chooseUse(Outcome.Benefit, message, source, game) + if (!(controller.chooseUse(Outcome.Benefit, "Pay {G}{G}{G}{G}?", source, game) && cost.pay(source, game, source, controller.getId(), false, null))) { controller.damage(8, source.getSourceId(), source, game); } diff --git a/Mage.Sets/src/mage/cards/f/ForceProjection.java b/Mage.Sets/src/mage/cards/f/ForceProjection.java index 179f02725efe..e185fe46d155 100644 --- a/Mage.Sets/src/mage/cards/f/ForceProjection.java +++ b/Mage.Sets/src/mage/cards/f/ForceProjection.java @@ -82,7 +82,7 @@ public boolean apply(Game game, Ability source) { Effect sacrificeEffect = new SacrificeSourceEffect(); sacrificeEffect.setTargetPointer(new FixedTarget(effect.getAddedPermanent().get(0), game)); TriggeredAbility ability = new BecomesTargetTriggeredAbility(sacrificeEffect, new FilterSpell()); - game.addTriggeredAbility(ability); + game.addTriggeredAbility(ability, null); return true; } diff --git a/Mage.Sets/src/mage/cards/f/ForcedMarch.java b/Mage.Sets/src/mage/cards/f/ForcedMarch.java index d5123120f42a..89ef76f20015 100644 --- a/Mage.Sets/src/mage/cards/f/ForcedMarch.java +++ b/Mage.Sets/src/mage/cards/f/ForcedMarch.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.cards.f; import java.util.UUID; @@ -42,7 +37,7 @@ class ForcedMarchEffect extends OneShotEffect { public ForcedMarchEffect() { super(Outcome.DestroyPermanent); - staticText = "Destroy all creatures with converted mana cost X or less"; + staticText = "Destroy all creatures with mana value X or less"; } public ForcedMarchEffect(final ForcedMarchEffect effect) { @@ -62,7 +57,7 @@ public boolean apply(Game game, Ability source) { source.getControllerId(), source.getSourceId(), game)) { - if (permanent.getConvertedManaCost() <= source.getManaCostsToPay().getX()) { + if (permanent.getManaValue() <= source.getManaCostsToPay().getX()) { permanent.destroy(source, game, false); } } diff --git a/Mage.Sets/src/mage/cards/f/ForebearsBlade.java b/Mage.Sets/src/mage/cards/f/ForebearsBlade.java index cfea2f65bd1d..40be33397c8e 100644 --- a/Mage.Sets/src/mage/cards/f/ForebearsBlade.java +++ b/Mage.Sets/src/mage/cards/f/ForebearsBlade.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.cards.f; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/f/ForerunnerOfTheCoalition.java b/Mage.Sets/src/mage/cards/f/ForerunnerOfTheCoalition.java index 2f60bb6e9380..2b3c91a3b317 100644 --- a/Mage.Sets/src/mage/cards/f/ForerunnerOfTheCoalition.java +++ b/Mage.Sets/src/mage/cards/f/ForerunnerOfTheCoalition.java @@ -13,7 +13,7 @@ import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterBySubtypeCard; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCardInLibrary; /** diff --git a/Mage.Sets/src/mage/cards/f/ForerunnerOfTheHeralds.java b/Mage.Sets/src/mage/cards/f/ForerunnerOfTheHeralds.java index 8383be5525a8..02419f0cd14d 100644 --- a/Mage.Sets/src/mage/cards/f/ForerunnerOfTheHeralds.java +++ b/Mage.Sets/src/mage/cards/f/ForerunnerOfTheHeralds.java @@ -16,7 +16,7 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterBySubtypeCard; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCardInLibrary; /** diff --git a/Mage.Sets/src/mage/cards/f/ForerunnerOfTheLegion.java b/Mage.Sets/src/mage/cards/f/ForerunnerOfTheLegion.java index 645c36354679..97a71e9dfe94 100644 --- a/Mage.Sets/src/mage/cards/f/ForerunnerOfTheLegion.java +++ b/Mage.Sets/src/mage/cards/f/ForerunnerOfTheLegion.java @@ -13,7 +13,7 @@ import mage.cards.CardSetInfo; import mage.filter.FilterPermanent; import mage.filter.common.FilterBySubtypeCard; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/f/Foresight.java b/Mage.Sets/src/mage/cards/f/Foresight.java index 0bb06df95bdc..2fee2df48063 100644 --- a/Mage.Sets/src/mage/cards/f/Foresight.java +++ b/Mage.Sets/src/mage/cards/f/Foresight.java @@ -1,25 +1,22 @@ - package mage.cards.f; -import java.util.List; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.delayed.AtTheBeginOfNextUpkeepDelayedTriggeredAbility; import mage.abilities.effects.SearchEffect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; +import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; -import mage.filter.FilterCard; +import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class Foresight extends CardImpl { @@ -47,11 +44,11 @@ public Foresight copy() { class ForesightEffect extends SearchEffect { ForesightEffect() { - super(new TargetCardInLibrary(3, new FilterCard()), Outcome.Benefit); - staticText = "Search your library for three cards, exile them, then shuffle your library"; + super(new TargetCardInLibrary(3, StaticFilters.FILTER_CARD), Outcome.Benefit); + staticText = "Search your library for three cards, exile them, then shuffle"; } - ForesightEffect(final ForesightEffect effect) { + private ForesightEffect(final ForesightEffect effect) { super(effect); } @@ -63,21 +60,19 @@ public ForesightEffect copy() { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player != null && player.searchLibrary(target, source, game)) { - for (UUID targetId : getTargets()) { - Card card = player.getLibrary().getCard(targetId, game); - if (card != null) { - card.moveToExile(null, null, source, game); - } + if (player == null) { + return false; + } + player.searchLibrary(target, source, game); + Cards cards = new CardsImpl(); + for (UUID targetId : target.getTargets()) { + Card card = player.getLibrary().getCard(targetId, game); + if (card != null) { + cards.add(card); } - player.shuffleLibrary(source, game); - return true; } - return false; - } - - public List getTargets() { - return target.getTargets(); + player.moveCards(cards, Zone.EXILED, source, game); + player.shuffleLibrary(source, game); + return true; } - } diff --git a/Mage.Sets/src/mage/cards/f/ForethoughtAmulet.java b/Mage.Sets/src/mage/cards/f/ForethoughtAmulet.java index 94f3d4955372..c009d43bffb0 100644 --- a/Mage.Sets/src/mage/cards/f/ForethoughtAmulet.java +++ b/Mage.Sets/src/mage/cards/f/ForethoughtAmulet.java @@ -75,7 +75,7 @@ public boolean apply(Game game, Ability source) { public boolean applies(GameEvent event, Ability source, Game game) { if (event.getAmount() >= 3) { MageObject object = game.getObject(event.getSourceId()); - return object != null && (object.isInstant() || object.isSorcery()); + return object != null && object.isInstantOrSorcery(); } return false; } diff --git a/Mage.Sets/src/mage/cards/f/ForgeArmor.java b/Mage.Sets/src/mage/cards/f/ForgeArmor.java index 2cd4dec3ac17..172af389491b 100644 --- a/Mage.Sets/src/mage/cards/f/ForgeArmor.java +++ b/Mage.Sets/src/mage/cards/f/ForgeArmor.java @@ -9,7 +9,8 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.counters.CounterType; -import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetCreaturePermanent; @@ -23,7 +24,7 @@ public ForgeArmor(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{4}{R}"); // As an additional cost to cast Forge Armor, sacrifice an artifact. - this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledPermanent(new FilterControlledArtifactPermanent()))); + this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledPermanent(new FilterControlledPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT_AN)))); // Put X +1/+1 counters on target creature, where X is the sacrificed artifact's converted mana cost. this.getSpellAbility().addEffect(new AddCountersTargetEffect(new AddCountersTargetEffect( CounterType.P1P1.createInstance(), new SacrificeCostConvertedMana("artifact")))); diff --git a/Mage.Sets/src/mage/cards/f/ForgeOfHeroes.java b/Mage.Sets/src/mage/cards/f/ForgeOfHeroes.java index 2ecafc8814db..84c22e247ec2 100644 --- a/Mage.Sets/src/mage/cards/f/ForgeOfHeroes.java +++ b/Mage.Sets/src/mage/cards/f/ForgeOfHeroes.java @@ -13,7 +13,7 @@ import mage.constants.Outcome; import mage.counters.CounterType; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.CommanderPredicate; +import mage.filter.predicate.mageobject.CommanderPredicate; import mage.filter.predicate.permanent.EnteredThisTurnPredicate; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/f/ForgottenAncient.java b/Mage.Sets/src/mage/cards/f/ForgottenAncient.java index 106b3d9c3c46..69b10c65aeaa 100644 --- a/Mage.Sets/src/mage/cards/f/ForgottenAncient.java +++ b/Mage.Sets/src/mage/cards/f/ForgottenAncient.java @@ -13,7 +13,7 @@ import mage.constants.*; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/f/ForgottenLore.java b/Mage.Sets/src/mage/cards/f/ForgottenLore.java index 51aff33932e5..bdd376c69116 100644 --- a/Mage.Sets/src/mage/cards/f/ForgottenLore.java +++ b/Mage.Sets/src/mage/cards/f/ForgottenLore.java @@ -11,7 +11,7 @@ import mage.filter.FilterCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardIdPredicate; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInGraveyard; diff --git a/Mage.Sets/src/mage/cards/f/Fork.java b/Mage.Sets/src/mage/cards/f/Fork.java index afe08dedb057..d7e88255248d 100644 --- a/Mage.Sets/src/mage/cards/f/Fork.java +++ b/Mage.Sets/src/mage/cards/f/Fork.java @@ -1,34 +1,33 @@ package mage.cards.f; +import mage.ObjectColor; import mage.abilities.Ability; -import mage.abilities.Mode; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.filter.StaticFilters; +import mage.filter.predicate.mageobject.MageObjectReferencePredicate; import mage.game.Game; -import mage.game.events.CopiedStackObjectEvent; import mage.game.stack.Spell; -import mage.players.Player; +import mage.game.stack.StackObject; import mage.target.TargetSpell; +import mage.util.functions.StackObjectCopyApplier; import java.util.UUID; /** - * @author jeffwadsworth + * @author TheElk801 */ public final class Fork extends CardImpl { - public Fork(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}{R}"); // Copy target instant or sorcery spell, except that the copy is red. You may choose new targets for the copy. this.getSpellAbility().addEffect(new ForkEffect()); this.getSpellAbility().addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_INSTANT_OR_SORCERY)); - } private Fork(final Fork card) { @@ -43,39 +42,45 @@ public Fork copy() { class ForkEffect extends OneShotEffect { - public ForkEffect() { + ForkEffect() { super(Outcome.Copy); - staticText = "Copy target instant or sorcery spell, except that the copy is red. You may choose new targets for the copy"; + staticText = "copy target instant or sorcery spell, except that the copy is red. " + + "You may choose new targets for the copy"; } - public ForkEffect(final ForkEffect effect) { + private ForkEffect(final ForkEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); - if (spell != null && controller != null) { - Spell copy = spell.copySpell(source.getControllerId(), game); - copy.getColor(game).setRed(true); - game.getStack().push(copy); - copy.chooseNewTargets(game, controller.getId()); - game.fireEvent(new CopiedStackObjectEvent(spell, copy, source.getControllerId())); - return true; + Spell spell = game.getSpell(source.getFirstTarget()); + if (spell == null) { + return false; } - return false; + spell.createCopyOnStack( + game, source, source.getControllerId(), + true, 1, ForkApplier.instance + ); + return true; } @Override public ForkEffect copy() { return new ForkEffect(this); } +} + +enum ForkApplier implements StackObjectCopyApplier { + instance; + + @Override + public void modifySpell(StackObject stackObject, Game game) { + stackObject.getColor(game).setColor(ObjectColor.RED); + } @Override - public String getText(Mode mode) { - StringBuilder sb = new StringBuilder(); - sb.append("Copy target ").append(mode.getTargets().get(0).getTargetName()).append(", except that the copy is red. You may choose new targets for the copy"); - return sb.toString(); + public MageObjectReferencePredicate getNextPredicate() { + return null; } } diff --git a/Mage.Sets/src/mage/cards/f/ForkInTheRoad.java b/Mage.Sets/src/mage/cards/f/ForkInTheRoad.java index 3d7342c89429..5f69825c1b14 100644 --- a/Mage.Sets/src/mage/cards/f/ForkInTheRoad.java +++ b/Mage.Sets/src/mage/cards/f/ForkInTheRoad.java @@ -46,7 +46,7 @@ class ForkInTheRoadEffect extends OneShotEffect { public ForkInTheRoadEffect() { super(Outcome.PutLandInPlay); - staticText = "Search your library for up to two basic land cards and reveal them. Put one into your hand and the other into your graveyard. Then shuffle your library"; + staticText = "Search your library for up to two basic land cards and reveal them. Put one into your hand and the other into your graveyard. Then shuffle"; } public ForkInTheRoadEffect(final ForkInTheRoadEffect effect) { diff --git a/Mage.Sets/src/mage/cards/f/FortifyingDraught.java b/Mage.Sets/src/mage/cards/f/FortifyingDraught.java new file mode 100644 index 000000000000..f8083d28aef0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FortifyingDraught.java @@ -0,0 +1,43 @@ +package mage.cards.f; + +import mage.abilities.dynamicvalue.common.ControllerGotLifeCount; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.target.common.TargetCreaturePermanent; +import mage.watchers.common.PlayerGainedLifeWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FortifyingDraught extends CardImpl { + + public FortifyingDraught(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}"); + + // You gain 2 life. Target creature gets +X/+X until end of turn, where X is the amount of life you gained this turn. + this.getSpellAbility().addEffect(new GainLifeEffect(2)); + this.getSpellAbility().addEffect(new BoostTargetEffect( + ControllerGotLifeCount.instance, + ControllerGotLifeCount.instance, + Duration.EndOfTurn, true + ).setText("Target creature gets +X/+X until end of turn, where X is the amount of life you gained this turn.")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addWatcher(new PlayerGainedLifeWatcher()); + this.getSpellAbility().addHint(ControllerGotLifeCount.getHint()); + } + + private FortifyingDraught(final FortifyingDraught card) { + super(card); + } + + @Override + public FortifyingDraught copy() { + return new FortifyingDraught(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FoulTongueInvocation.java b/Mage.Sets/src/mage/cards/f/FoulTongueInvocation.java index ae3abe2fbec2..8f5a39603184 100644 --- a/Mage.Sets/src/mage/cards/f/FoulTongueInvocation.java +++ b/Mage.Sets/src/mage/cards/f/FoulTongueInvocation.java @@ -1,24 +1,15 @@ - package mage.cards.f; -import mage.abilities.Ability; -import mage.abilities.costs.CostAdjuster; -import mage.abilities.costs.common.RevealTargetFromHandCost; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.InfoEffect; +import mage.abilities.condition.common.RevealedOrControlledDragonCondition; +import mage.abilities.costs.common.RevealDragonFromHandCost; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.SacrificeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.filter.FilterCard; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.players.Player; import mage.target.TargetPlayer; -import mage.target.common.TargetCardInHand; -import mage.watchers.common.DragonOnTheBattlefieldWhileSpellWasCastWatcher; import java.util.UUID; @@ -31,14 +22,17 @@ public FoulTongueInvocation(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}"); // As an additional cost to cast Foul-Tongue Invocation, you may reveal a Dragon card from your hand. - this.getSpellAbility().addEffect(new InfoEffect("as an additional cost to cast this spell, you may reveal a Dragon card from your hand")); - this.getSpellAbility().setCostAdjuster(FoulTongueInvocationAdjuster.instance); + this.getSpellAbility().addCost(new RevealDragonFromHandCost()); // Target player sacrifices a creature. If you revealed a Dragon card or controlled a Dragon as you cast Foul-Tongue Invocation, you gain 4 life. + this.getSpellAbility().addEffect(new SacrificeEffect( + StaticFilters.FILTER_PERMANENT_CREATURE, 1, "target player" + )); + this.getSpellAbility().addEffect(new ConditionalOneShotEffect( + new GainLifeEffect(4), RevealedOrControlledDragonCondition.instance, + "If you revealed a Dragon card or controlled a Dragon as you cast this spell, you gain 4 life." + )); this.getSpellAbility().addTarget(new TargetPlayer()); - this.getSpellAbility().addEffect(new SacrificeEffect(StaticFilters.FILTER_PERMANENT_CREATURE, 1, "target player")); - this.getSpellAbility().addEffect(new FoulTongueInvocationEffect()); - this.getSpellAbility().addWatcher(new DragonOnTheBattlefieldWhileSpellWasCastWatcher()); } private FoulTongueInvocation(final FoulTongueInvocation card) { @@ -50,52 +44,3 @@ public FoulTongueInvocation copy() { return new FoulTongueInvocation(this); } } - -enum FoulTongueInvocationAdjuster implements CostAdjuster { - instance; - private static final FilterCard filter = new FilterCard("a Dragon card from your hand (you don't have to)"); - - static { - filter.add(SubType.DRAGON.getPredicate()); - } - - @Override - public void adjustCosts(Ability ability, Game game) { - Player controller = game.getPlayer(ability.getControllerId()); - if (controller != null) { - if (controller.getHand().count(filter, game) > 0) { - ability.addCost(new RevealTargetFromHandCost(new TargetCardInHand(0, 1, filter))); - } - } - } -} - -class FoulTongueInvocationEffect extends OneShotEffect { - - public FoulTongueInvocationEffect() { - super(Outcome.Benefit); - this.staticText = "If you revealed a Dragon card or controlled a Dragon as you cast {this}, you gain 4 life"; - } - - public FoulTongueInvocationEffect(final FoulTongueInvocationEffect effect) { - super(effect); - } - - @Override - public FoulTongueInvocationEffect copy() { - return new FoulTongueInvocationEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - DragonOnTheBattlefieldWhileSpellWasCastWatcher watcher = game.getState().getWatcher(DragonOnTheBattlefieldWhileSpellWasCastWatcher.class); - if (watcher != null && watcher.castWithConditionTrue(source.getId())) { - controller.gainLife(4, game, source); - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/f/FoundryStreetDenizen.java b/Mage.Sets/src/mage/cards/f/FoundryStreetDenizen.java index 455e5c58da24..8542f5d87a7c 100644 --- a/Mage.Sets/src/mage/cards/f/FoundryStreetDenizen.java +++ b/Mage.Sets/src/mage/cards/f/FoundryStreetDenizen.java @@ -16,7 +16,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/f/FractalHarness.java b/Mage.Sets/src/mage/cards/f/FractalHarness.java new file mode 100644 index 000000000000..775144b8211a --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FractalHarness.java @@ -0,0 +1,116 @@ +package mage.cards.f; + +import mage.abilities.Ability; +import mage.abilities.common.AttacksAttachedTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.EquipAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.QuandrixToken; +import mage.game.permanent.token.Token; +import mage.watchers.common.ManaSpentToCastWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FractalHarness extends CardImpl { + + public FractalHarness(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{X}{2}{G}"); + + this.subtype.add(SubType.EQUIPMENT); + + // When Fractal Harness enters the battlefield, create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it and attach Fractal Harness to it. + this.addAbility(new EntersBattlefieldTriggeredAbility(new FractalHarnessTokenEffect()), new ManaSpentToCastWatcher()); + + // Whenever equipped creature attacks, double the number of +1/+1 counters on it. + this.addAbility(new AttacksAttachedTriggeredAbility( + new FractalHarnessDoubleEffect(), AttachmentType.EQUIPMENT, false + )); + + // Equip {2} + this.addAbility(new EquipAbility(2)); + } + + private FractalHarness(final FractalHarness card) { + super(card); + } + + @Override + public FractalHarness copy() { + return new FractalHarness(this); + } +} + +class FractalHarnessTokenEffect extends OneShotEffect { + + FractalHarnessTokenEffect() { + super(Outcome.Benefit); + staticText = "create a 0/0 green and blue Fractal creature token. " + + "Put X +1/+1 counters on it and attach {this} to it"; + } + + private FractalHarnessTokenEffect(final FractalHarnessTokenEffect effect) { + super(effect); + } + + @Override + public FractalHarnessTokenEffect copy() { + return new FractalHarnessTokenEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Token token = new QuandrixToken(); + token.putOntoBattlefield(1, game, source, source.getControllerId()); + int xValue = ManacostVariableValue.instance.calculate(game, source, this); + boolean flag = true; + for (UUID tokenId : token.getLastAddedTokenIds()) { + Permanent permanent = game.getPermanent(tokenId); + if (permanent == null) { + continue; + } + if (flag && permanent.addAttachment(tokenId, source, game)) { + flag = false; + } + permanent.addCounters(CounterType.P1P1.createInstance(xValue), source.getControllerId(), source, game); + } + return true; + } +} + +class FractalHarnessDoubleEffect extends OneShotEffect { + + FractalHarnessDoubleEffect() { + super(Outcome.Benefit); + staticText = "double the number of +1/+1 counters on it"; + } + + private FractalHarnessDoubleEffect(final FractalHarnessDoubleEffect effect) { + super(effect); + } + + @Override + public FractalHarnessDoubleEffect copy() { + return new FractalHarnessDoubleEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = (Permanent) getValue("attachedPermanent"); + return permanent != null && permanent.addCounters(CounterType.P1P1.createInstance( + permanent.getCounters(game).getCount(CounterType.P1P1) + ), source.getControllerId(), source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FractalSummoning.java b/Mage.Sets/src/mage/cards/f/FractalSummoning.java new file mode 100644 index 000000000000..1927b787beaa --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FractalSummoning.java @@ -0,0 +1,36 @@ +package mage.cards.f; + +import mage.abilities.dynamicvalue.common.ManacostVariableValue; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.QuandrixToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FractalSummoning extends CardImpl { + + public FractalSummoning(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{G/U}{G/U}"); + + this.subtype.add(SubType.LESSON); + + // Create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it. + this.getSpellAbility().addEffect(QuandrixToken.getEffect( + ManacostVariableValue.instance, "Put X +1/+1 counters on it" + )); + } + + private FractalSummoning(final FractalSummoning card) { + super(card); + } + + @Override + public FractalSummoning copy() { + return new FractalSummoning(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/Fracture.java b/Mage.Sets/src/mage/cards/f/Fracture.java new file mode 100644 index 000000000000..377fa4842955 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/Fracture.java @@ -0,0 +1,44 @@ +package mage.cards.f; + +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.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Fracture extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("artifact, enchantment, or planeswalker"); + + static { + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.ENCHANTMENT.getPredicate(), + CardType.PLANESWALKER.getPredicate() + )); + } + + public Fracture(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}{B}"); + + // Destroy target artifact, enchantment, or planeswalker. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + } + + private Fracture(final Fracture card) { + super(card); + } + + @Override + public Fracture copy() { + return new Fracture(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FracturedIdentity.java b/Mage.Sets/src/mage/cards/f/FracturedIdentity.java index 2ba73c21bb12..00580a2f58fa 100644 --- a/Mage.Sets/src/mage/cards/f/FracturedIdentity.java +++ b/Mage.Sets/src/mage/cards/f/FracturedIdentity.java @@ -1,7 +1,5 @@ - package mage.cards.f; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenCopyTargetEffect; @@ -9,13 +7,16 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.common.TargetNonlandPermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author spjspj */ public final class FracturedIdentity extends CardImpl { @@ -40,12 +41,12 @@ public FracturedIdentity copy() { class FracturedIdentityEffect extends OneShotEffect { - public FracturedIdentityEffect() { + FracturedIdentityEffect() { super(Outcome.Exile); this.staticText = "Exile target nonland permanent. Each player other than its controller creates a token that's a copy of it"; } - public FracturedIdentityEffect(final FracturedIdentityEffect effect) { + private FracturedIdentityEffect(final FracturedIdentityEffect effect) { super(effect); } @@ -56,17 +57,19 @@ public FracturedIdentityEffect copy() { @Override public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent == null) { + if (player == null || permanent == null) { return false; } - - permanent.moveToExile(null, null, source, game); - UUID controllerId = permanent.getControllerId(); - for (UUID opponentId : game.getOpponents(controllerId)) { - CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(opponentId, null, false); - effect.setTargetPointer(new FixedTarget(permanent, game)); - effect.apply(game, source); + player.moveCards(permanent, Zone.EXILED, source, game); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + if (permanent.isControlledBy(playerId)) { + continue; + } + new CreateTokenCopyTargetEffect( + playerId, null, false + ).setTargetPointer(new FixedTarget(permanent, game)).apply(game, source); } return true; } diff --git a/Mage.Sets/src/mage/cards/f/Fragmentize.java b/Mage.Sets/src/mage/cards/f/Fragmentize.java index 9a25ed4237b6..9a27fe659bd7 100644 --- a/Mage.Sets/src/mage/cards/f/Fragmentize.java +++ b/Mage.Sets/src/mage/cards/f/Fragmentize.java @@ -8,7 +8,7 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.filter.common.FilterArtifactOrEnchantmentPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetPermanent; /** @@ -17,10 +17,10 @@ */ public final class Fragmentize extends CardImpl { - private static final FilterArtifactOrEnchantmentPermanent filter = new FilterArtifactOrEnchantmentPermanent("artifact or enchantment with converted mana cost 4 or less"); + private static final FilterArtifactOrEnchantmentPermanent filter = new FilterArtifactOrEnchantmentPermanent("artifact or enchantment with mana value 4 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 5)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 5)); } public Fragmentize(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/f/FreeForAll.java b/Mage.Sets/src/mage/cards/f/FreeForAll.java index a33825339068..77cef34a0e73 100644 --- a/Mage.Sets/src/mage/cards/f/FreeForAll.java +++ b/Mage.Sets/src/mage/cards/f/FreeForAll.java @@ -1,14 +1,10 @@ - package mage.cards.f; -import java.util.List; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.cards.Cards; @@ -17,26 +13,28 @@ import mage.constants.Outcome; import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.ExileZone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author L_J */ public final class FreeForAll extends CardImpl { public FreeForAll(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}"); // When Free-for-All enters the battlefield, exile all creatures face down. this.addAbility(new EntersBattlefieldTriggeredAbility(new FreeForAllExileAllEffect())); // At the beginning of each player's upkeep, that player chooses a card exiled with Free-for-All at random and puts it onto the battlefield. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new FreeForAllReturnFromExileEffect(), TargetController.ANY, false, true)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new FreeForAllReturnFromExileEffect(), TargetController.ANY, false)); // When Free-for-All leaves the battlefield, put all cards exiled with it into their owners' graveyards. this.addAbility(new LeavesBattlefieldTriggeredAbility(new FreeForAllLeavesBattlefieldEffect(), false)); @@ -54,12 +52,12 @@ public FreeForAll copy() { class FreeForAllExileAllEffect extends OneShotEffect { - public FreeForAllExileAllEffect() { + FreeForAllExileAllEffect() { super(Outcome.Exile); staticText = "exile all creatures face down"; } - public FreeForAllExileAllEffect(final FreeForAllExileAllEffect effect) { + private FreeForAllExileAllEffect(final FreeForAllExileAllEffect effect) { super(effect); } @@ -70,26 +68,32 @@ public FreeForAllExileAllEffect copy() { @Override public boolean apply(Game game, Ability source) { - List permanents = game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(), source.getControllerId(), source.getSourceId(), game); - for (Permanent permanent : permanents) { - Card card = game.getCard(permanent.getId()); - permanent.moveToExile(source.getSourceId(), "Free-for-All", source, game); - if (card != null) { - card.setFaceDown(true, game); - } + Player player = game.getPlayer(source.getControllerId()); + Permanent sourcePermanent = source.getSourcePermanentOrLKI(game); + if (player == null || sourcePermanent == null) { + return false; } + Cards cards = new CardsImpl(); + game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), source.getSourceId(), game + ).stream().forEach(cards::add); + player.moveCardsToExile(cards.getCards(game), source, game, false, CardUtil.getExileZoneId(game, source), sourcePermanent.getIdName()); + cards.getCards(game) + .stream() + .filter(card -> game.getState().getZone(card.getId()) == Zone.EXILED) + .forEach(card -> card.setFaceDown(true, game)); return true; } } class FreeForAllReturnFromExileEffect extends OneShotEffect { - public FreeForAllReturnFromExileEffect() { + FreeForAllReturnFromExileEffect() { super(Outcome.PutCardInPlay); - staticText = "that player chooses a card exiled with Free-for-All at random and puts it onto the battlefield"; + staticText = "that player chooses a card exiled with {this} at random and puts it onto the battlefield"; } - public FreeForAllReturnFromExileEffect(final FreeForAllReturnFromExileEffect effect) { + private FreeForAllReturnFromExileEffect(final FreeForAllReturnFromExileEffect effect) { super(effect); } @@ -100,27 +104,24 @@ public FreeForAllReturnFromExileEffect copy() { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(targetPointer.getFirst(game, source)); - if (player != null) { - ExileZone exZone = game.getExile().getExileZone(source.getSourceId()); - if (exZone != null) { - Cards exiledCards = new CardsImpl(exZone.getCards(game)); - return player.moveCards(exiledCards.getRandom(game), Zone.BATTLEFIELD, source, game); - } - return true; + Player player = game.getPlayer(game.getActivePlayerId()); + ExileZone exZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + if (player == null || exZone == null || exZone.isEmpty()) { + return false; } - return false; + Cards exiledCards = new CardsImpl(exZone.getCards(game)); + return player.moveCards(exiledCards.getRandom(game), Zone.BATTLEFIELD, source, game); } } class FreeForAllLeavesBattlefieldEffect extends OneShotEffect { - public FreeForAllLeavesBattlefieldEffect() { + FreeForAllLeavesBattlefieldEffect() { super(Outcome.Detriment); this.staticText = "put all cards exiled with it into their owners' graveyards"; } - public FreeForAllLeavesBattlefieldEffect(final FreeForAllLeavesBattlefieldEffect effect) { + private FreeForAllLeavesBattlefieldEffect(final FreeForAllLeavesBattlefieldEffect effect) { super(effect); } @@ -132,13 +133,9 @@ public FreeForAllLeavesBattlefieldEffect copy() { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - ExileZone exZone = game.getExile().getExileZone(source.getSourceId()); - if (exZone != null) { - return controller.moveCards(exZone.getCards(game), Zone.GRAVEYARD, source, game, false, false, true, null); - } - return true; - } - return false; + ExileZone exZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + return controller != null + && exZone != null + && controller.moveCards(exZone.getCards(game), Zone.GRAVEYARD, source, game); } } diff --git a/Mage.Sets/src/mage/cards/f/FreyaliseLlanowarsFury.java b/Mage.Sets/src/mage/cards/f/FreyaliseLlanowarsFury.java index 9d78666f4aa6..afab8b04b861 100644 --- a/Mage.Sets/src/mage/cards/f/FreyaliseLlanowarsFury.java +++ b/Mage.Sets/src/mage/cards/f/FreyaliseLlanowarsFury.java @@ -1,4 +1,3 @@ - package mage.cards.f; import mage.ObjectColor; @@ -16,7 +15,7 @@ import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.permanent.token.FreyaliseLlanowarsFuryToken; +import mage.game.permanent.token.ElfDruidToken; import mage.target.TargetPermanent; import java.util.UUID; @@ -42,11 +41,13 @@ public FreyaliseLlanowarsFury(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); // +2: Create a 1/1 green Elf Druid creature token with "{T}: Add {G}." - this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new FreyaliseLlanowarsFuryToken()), 2)); + this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new ElfDruidToken()), 2)); + // -2: Destroy target artifact or enchantment. LoyaltyAbility loyaltyAbility = new LoyaltyAbility(new DestroyTargetEffect(), -2); loyaltyAbility.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); this.addAbility(loyaltyAbility); + // -6: Draw a card for each green creature you control. this.addAbility(new LoyaltyAbility(new DrawCardSourceControllerEffect(new PermanentsOnBattlefieldCount(filterGreen)), -6)); diff --git a/Mage.Sets/src/mage/cards/f/FriendlyFire.java b/Mage.Sets/src/mage/cards/f/FriendlyFire.java index a80d15572289..56e12f1db8bf 100644 --- a/Mage.Sets/src/mage/cards/f/FriendlyFire.java +++ b/Mage.Sets/src/mage/cards/f/FriendlyFire.java @@ -41,7 +41,7 @@ class FriendlyFireEffect extends OneShotEffect { public FriendlyFireEffect() { super(Outcome.Discard); - this.staticText = "Target creature's controller reveals a card at random from their hand. {this} deals damage to that creature and that player equal to the revealed card's converted mana cost"; + this.staticText = "Target creature's controller reveals a card at random from their hand. {this} deals damage to that creature and that player equal to the revealed card's mana value"; } public FriendlyFireEffect(final FriendlyFireEffect effect) { @@ -67,7 +67,7 @@ public boolean apply(Game game, Ability source) { if (card != null) { Cards cards = new CardsImpl(card); controllerOfTargetCreature.revealCards(sourceObject.getName(), cards, game); - int damage = card.getConvertedManaCost(); + int damage = card.getManaValue(); targetCreature.damage(damage, source.getSourceId(), source, game, false, true); controllerOfTargetCreature.damage(damage, source.getSourceId(), source, game); return true; diff --git a/Mage.Sets/src/mage/cards/f/FromTheAshes.java b/Mage.Sets/src/mage/cards/f/FromTheAshes.java index 99ed9db9cd12..90c4edbc6209 100644 --- a/Mage.Sets/src/mage/cards/f/FromTheAshes.java +++ b/Mage.Sets/src/mage/cards/f/FromTheAshes.java @@ -48,7 +48,7 @@ class FromTheAshesEffect extends OneShotEffect { public FromTheAshesEffect() { super(Outcome.Benefit); - this.staticText = "Destroy all nonbasic lands. For each land destroyed this way, its controller may search their library for a basic land card and put it onto the battlefield. Then each player who searched their library this way shuffles it"; + this.staticText = "Destroy all nonbasic lands. For each land destroyed this way, its controller may search their library for a basic land card and put it onto the battlefield. Then each player who searched their library this way shuffles"; } public FromTheAshesEffect(final FromTheAshesEffect effect) { diff --git a/Mage.Sets/src/mage/cards/f/FrontlineMedic.java b/Mage.Sets/src/mage/cards/f/FrontlineMedic.java index d77730680648..f3ef1946b11e 100644 --- a/Mage.Sets/src/mage/cards/f/FrontlineMedic.java +++ b/Mage.Sets/src/mage/cards/f/FrontlineMedic.java @@ -1,7 +1,6 @@ package mage.cards.f; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -15,16 +14,17 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterSpell; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.VariableManaCostPredicate; import mage.target.TargetSpell; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class FrontlineMedic extends CardImpl { @@ -32,11 +32,11 @@ public final class FrontlineMedic extends CardImpl { private static final FilterSpell filter = new FilterSpell("spell with {X} in its mana cost"); static { - filter.add(new VariableManaCostPredicate()); + filter.add(VariableManaCostPredicate.instance); } public FrontlineMedic(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.CLERIC); diff --git a/Mage.Sets/src/mage/cards/f/FrostTrickster.java b/Mage.Sets/src/mage/cards/f/FrostTrickster.java new file mode 100644 index 000000000000..ebb755a9fff1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FrostTrickster.java @@ -0,0 +1,48 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.keyword.FlyingAbility; +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 FrostTrickster extends CardImpl { + + public FrostTrickster(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.BIRD); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Frost Trickster enters the battlefield, tap target creature an opponent controls. That creature doesn't untap during its controller's next untap step. + Ability ability = new EntersBattlefieldTriggeredAbility(new TapTargetEffect(), false); + ability.addEffect(new DontUntapInControllersNextUntapStepTargetEffect("That creature")); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + this.addAbility(ability); + } + + private FrostTrickster(final FrostTrickster card) { + super(card); + } + + @Override + public FrostTrickster copy() { + return new FrostTrickster(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FrostboilSnarl.java b/Mage.Sets/src/mage/cards/f/FrostboilSnarl.java new file mode 100644 index 000000000000..28f69e07f51f --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FrostboilSnarl.java @@ -0,0 +1,56 @@ +package mage.cards.f; + +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.costs.common.RevealTargetFromHandCost; +import mage.abilities.effects.common.TapSourceUnlessPaysEffect; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.RedManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.target.common.TargetCardInHand; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FrostboilSnarl extends CardImpl { + + private static final FilterCard filter = new FilterCard("an Island or Mountain card from your hand"); + + static { + filter.add(Predicates.or( + SubType.ISLAND.getPredicate(), + SubType.MOUNTAIN.getPredicate() + )); + } + + public FrostboilSnarl(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // As Frostboil Snarl enters the battlefield, you may reveal an Island or Mountain card from your hand. If you don't, Frostboil Snarl enters the battlefield tapped. + this.addAbility(new AsEntersBattlefieldAbility( + new TapSourceUnlessPaysEffect( + new RevealTargetFromHandCost(new TargetCardInHand(filter)) + ), "you may reveal an Island or Mountain card from your hand. " + + "If you don't, {this} enters the battlefield tapped" + )); + + // {T}: Add {U} or {R}. + this.addAbility(new BlueManaAbility()); + this.addAbility(new RedManaAbility()); + } + + private FrostboilSnarl(final FrostboilSnarl card) { + super(card); + } + + @Override + public FrostboilSnarl copy() { + return new FrostboilSnarl(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FumingEffigy.java b/Mage.Sets/src/mage/cards/f/FumingEffigy.java new file mode 100644 index 000000000000..fef07d141212 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FumingEffigy.java @@ -0,0 +1,38 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.common.CardsLeaveGraveyardTriggeredAbility; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FumingEffigy extends CardImpl { + + public FumingEffigy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Whenever one or more cards leave your graveyard, Fuming Effigy deals 1 damage to each opponent. + this.addAbility(new CardsLeaveGraveyardTriggeredAbility(new DamagePlayersEffect(1, TargetController.OPPONENT))); + } + + private FumingEffigy(final FumingEffigy card) { + super(card); + } + + @Override + public FumingEffigy copy() { + return new FumingEffigy(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FuneralPyre.java b/Mage.Sets/src/mage/cards/f/FuneralPyre.java index 937e819c976a..62d11b91eef7 100644 --- a/Mage.Sets/src/mage/cards/f/FuneralPyre.java +++ b/Mage.Sets/src/mage/cards/f/FuneralPyre.java @@ -1,7 +1,5 @@ - package mage.cards.f; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; @@ -9,27 +7,25 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.token.SpiritWhiteToken; -import mage.game.permanent.token.Token; import mage.players.Player; import mage.target.common.TargetCardInGraveyard; -import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class FuneralPyre extends CardImpl { public FuneralPyre(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}"); - // Exile target card from a graveyard. Its owner puts a 1/1 white Spirit creature token with flying onto the battlefield. this.getSpellAbility().addEffect(new FuneralPyreEffect()); this.getSpellAbility().addTarget(new TargetCardInGraveyard()); - } private FuneralPyre(final FuneralPyre card) { @@ -44,12 +40,13 @@ public FuneralPyre copy() { class FuneralPyreEffect extends OneShotEffect { - public FuneralPyreEffect() { + FuneralPyreEffect() { super(Outcome.Benefit); - this.staticText = "Exile target card from a graveyard. Its owner puts a 1/1 white Spirit creature token with flying onto the battlefield"; + this.staticText = "Exile target card from a graveyard. " + + "Its owner creates a 1/1 white Spirit creature token with flying"; } - public FuneralPyreEffect(final FuneralPyreEffect effect) { + private FuneralPyreEffect(final FuneralPyreEffect effect) { super(effect); } @@ -60,17 +57,16 @@ public FuneralPyreEffect copy() { @Override public boolean apply(Game game, Ability source) { - Card exiledCard = game.getCard(source.getTargets().getFirstTarget()); - if (exiledCard != null) { - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), 0); - if (exiledCard.moveToExile(exileId, "Funeral Pyre", source, game)) { - Player owner = game.getPlayer(exiledCard.getOwnerId()); - if (owner != null) { - Token token = new SpiritWhiteToken(); - return token.putOntoBattlefield(1, game, source, owner.getId()); - } - } + Player player = game.getPlayer(source.getControllerId()); + Card exiledCard = game.getCard(source.getFirstTarget()); + if (player == null || exiledCard == null) { + return false; + } + Player owner = game.getPlayer(exiledCard.getOwnerId()); + player.moveCards(exiledCard, Zone.EXILED, source, game); + if (owner != null) { + new SpiritWhiteToken().putOntoBattlefield(1, game, source, owner.getId()); } - return false; + return true; } } diff --git a/Mage.Sets/src/mage/cards/f/FurnaceOfRath.java b/Mage.Sets/src/mage/cards/f/FurnaceOfRath.java index 68639e1d4bed..b1f7e0548abb 100644 --- a/Mage.Sets/src/mage/cards/f/FurnaceOfRath.java +++ b/Mage.Sets/src/mage/cards/f/FurnaceOfRath.java @@ -59,10 +59,7 @@ public FurnaceOfRathEffect copy() { public boolean checksEventType(GameEvent event, Game game) { switch (event.getType()) { case DAMAGE_PLAYER: - return true; - case DAMAGE_CREATURE: - return true; - case DAMAGE_PLANESWALKER: + case DAMAGE_PERMANENT: return true; } return false; diff --git a/Mage.Sets/src/mage/cards/f/FurycalmSnarl.java b/Mage.Sets/src/mage/cards/f/FurycalmSnarl.java new file mode 100644 index 000000000000..8bcef259f495 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FurycalmSnarl.java @@ -0,0 +1,57 @@ +package mage.cards.f; + +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.costs.common.RevealTargetFromHandCost; +import mage.abilities.effects.common.TapSourceUnlessPaysEffect; +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.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.target.common.TargetCardInHand; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FurycalmSnarl extends CardImpl { + + private static final FilterCard filter = new FilterCard("a Mountain or Plains card from your hand"); + + static { + filter.add(Predicates.or( + SubType.MOUNTAIN.getPredicate(), + SubType.PLAINS.getPredicate() + )); + } + + public FurycalmSnarl(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + + // As Furycalm Snarl enters the battlefield, you may reveal a Mountain or Plains card from your hand. If you don't, Furycalm Snarl enters the battlefield tapped. + this.addAbility(new AsEntersBattlefieldAbility( + new TapSourceUnlessPaysEffect( + new RevealTargetFromHandCost(new TargetCardInHand(filter)) + ), "you may reveal a Mountain or Plains card from your hand. " + + "If you don't, {this} enters the battlefield tapped" + )); + + // {T}: Add {R} or {W}. + this.addAbility(new RedManaAbility()); + this.addAbility(new WhiteManaAbility()); + } + + private FurycalmSnarl(final FurycalmSnarl card) { + super(card); + } + + @Override + public FurycalmSnarl copy() { + return new FurycalmSnarl(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FutureSight.java b/Mage.Sets/src/mage/cards/f/FutureSight.java index 88d4a826e2b2..b8427292165e 100644 --- a/Mage.Sets/src/mage/cards/f/FutureSight.java +++ b/Mage.Sets/src/mage/cards/f/FutureSight.java @@ -1,7 +1,5 @@ - package mage.cards.f; -import java.util.UUID; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.PlayTheTopCardEffect; import mage.abilities.effects.common.continuous.PlayWithTheTopCardRevealedEffect; @@ -10,18 +8,20 @@ import mage.constants.CardType; import mage.constants.Zone; +import java.util.UUID; + /** - * * @author Plopman */ public final class FutureSight extends CardImpl { public FutureSight(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}{U}{U}"); // Play with the top card of your library revealed. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayWithTheTopCardRevealedEffect())); - // You may play the top card of your library. + + // You may play lands and cast spells from the top of your library. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayTheTopCardEffect())); } diff --git a/Mage.Sets/src/mage/cards/g/GaddockTeeg.java b/Mage.Sets/src/mage/cards/g/GaddockTeeg.java index 5f405b6350d4..cd9b4c5b0599 100644 --- a/Mage.Sets/src/mage/cards/g/GaddockTeeg.java +++ b/Mage.Sets/src/mage/cards/g/GaddockTeeg.java @@ -48,7 +48,7 @@ class GaddockTeegReplacementEffect4 extends ContinuousRuleModifyingEffectImpl { public GaddockTeegReplacementEffect4() { super(Duration.WhileOnBattlefield, Outcome.Detriment); - staticText = "Noncreature spells with converted mana cost 4 or greater can't be cast. Noncreature spells with {X} in their mana costs can't be cast"; + staticText = "Noncreature spells with mana value 4 or greater can't be cast. Noncreature spells with {X} in their mana costs can't be cast"; } public GaddockTeegReplacementEffect4(final GaddockTeegReplacementEffect4 effect) { @@ -73,7 +73,7 @@ public boolean checksEventType(GameEvent event, Game game) { @Override public boolean applies(GameEvent event, Ability source, Game game) { Card card = game.getCard(event.getSourceId()); - if (card != null && !card.isCreature() && card.getConvertedManaCost() >= 4) { + if (card != null && !card.isCreature() && card.getManaValue() >= 4) { return true; } return false; diff --git a/Mage.Sets/src/mage/cards/g/GadwickTheWizened.java b/Mage.Sets/src/mage/cards/g/GadwickTheWizened.java index 342637a3788a..518703bb0c7f 100644 --- a/Mage.Sets/src/mage/cards/g/GadwickTheWizened.java +++ b/Mage.Sets/src/mage/cards/g/GadwickTheWizened.java @@ -5,22 +5,21 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.TapTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; import mage.filter.FilterPermanent; import mage.filter.FilterSpell; import mage.filter.common.FilterNonlandPermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.stack.Spell; import mage.target.TargetPermanent; -import mage.watchers.Watcher; +import mage.watchers.common.ManaSpentToCastWatcher; import java.util.UUID; @@ -50,8 +49,8 @@ public GadwickTheWizened(UUID ownerId, CardSetInfo setInfo) { // When Gadwick, the Wizened enters the battlefield, draw X cards. this.addAbility(new EntersBattlefieldTriggeredAbility( - new DrawCardSourceControllerEffect(GadwickTheWizenedValue.instance) - ), new GadwickTheWizenedWatcher()); + new DrawCardSourceControllerEffect(ManacostVariableValue.instance) + ), new ManaSpentToCastWatcher()); // Whenever you cast a blue spell, tap target nonland permanent an opponent controls. Ability ability = new SpellCastControllerTriggeredAbility(new TapTargetEffect(), filter, false); @@ -68,60 +67,3 @@ public GadwickTheWizened copy() { return new GadwickTheWizened(this); } } - -enum GadwickTheWizenedValue implements DynamicValue { - instance; - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - // watcher in card's scope - GadwickTheWizenedWatcher watcher = game.getState().getWatcher(GadwickTheWizenedWatcher.class, sourceAbility.getSourceId()); - if (watcher == null) { - return 0; - } - if (game.getState().getValue(sourceAbility.getSourceId().toString() - + "cardsToDraw") == null) { - return 0; - } - return (Integer) game.getState().getValue(sourceAbility.getSourceId().toString() - + "cardsToDraw"); - } - - @Override - public DynamicValue copy() { - return instance; - } - - @Override - public String toString() { - return "X"; - } - - @Override - public String getMessage() { - return ""; - } -} - -class GadwickTheWizenedWatcher extends Watcher { - - GadwickTheWizenedWatcher() { - super(WatcherScope.CARD); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() != GameEvent.EventType.SPELL_CAST) { - return; - } - Spell spell = game.getSpellOrLKIStack(event.getTargetId()); - if (spell == null) { - return; - } - if (spell.getSourceId() != super.getSourceId()) { - return; // the spell is not Gadwick, the Wizened - } - game.getState().setValue(spell.getSourceId().toString() - + "cardsToDraw", spell.getSpellAbility().getManaCostsToPay().getX()); - } -} diff --git a/Mage.Sets/src/mage/cards/g/GaeasAnthem.java b/Mage.Sets/src/mage/cards/g/GaeasAnthem.java index e7c256048f55..ae5a52b1fb4d 100644 --- a/Mage.Sets/src/mage/cards/g/GaeasAnthem.java +++ b/Mage.Sets/src/mage/cards/g/GaeasAnthem.java @@ -1,18 +1,16 @@ - package mage.cards.g; -import java.util.UUID; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; import mage.filter.StaticFilters; +import java.util.UUID; + /** - * * @author dustinconrad */ public final class GaeasAnthem extends CardImpl { @@ -21,7 +19,10 @@ public GaeasAnthem(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}{G}"); // Creatures you control get +1/+1. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE, false))); + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 1, 1, Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENT_CREATURES, false + ))); } private GaeasAnthem(final GaeasAnthem card) { diff --git a/Mage.Sets/src/mage/cards/g/GaeasBlessing.java b/Mage.Sets/src/mage/cards/g/GaeasBlessing.java index c6742b33565c..c0037e3f258d 100644 --- a/Mage.Sets/src/mage/cards/g/GaeasBlessing.java +++ b/Mage.Sets/src/mage/cards/g/GaeasBlessing.java @@ -1,37 +1,34 @@ - package mage.cards.g; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.ZoneChangeTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.cards.Card; +import mage.abilities.effects.common.TargetPlayerShufflesTargetCardsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.FilterCard; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; -import mage.target.common.TargetCardInGraveyard; +import mage.target.common.TargetCardInTargetPlayersGraveyard; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class GaeasBlessing extends CardImpl { public GaeasBlessing(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}"); // Target player shuffles up to three target cards from their graveyard into their library. - this.getSpellAbility().addEffect(new GaeasBlessingEffect()); + this.getSpellAbility().addEffect(new TargetPlayerShufflesTargetCardsEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); - this.getSpellAbility().addTarget(new GaeasBlessingTarget()); + this.getSpellAbility().addTarget(new TargetCardInTargetPlayersGraveyard(3)); // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); @@ -50,67 +47,13 @@ public GaeasBlessing copy() { } } -class GaeasBlessingEffect extends OneShotEffect { - - public GaeasBlessingEffect() { - super(Outcome.Neutral); - this.staticText = "Target player shuffles up to three target cards from their graveyard into their library"; - } - - public GaeasBlessingEffect(final GaeasBlessingEffect effect) { - super(effect); - } - - @Override - public GaeasBlessingEffect copy() { - return new GaeasBlessingEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player targetPlayer = game.getPlayer(source.getFirstTarget()); - if (targetPlayer != null) { - return targetPlayer.shuffleCardsToLibrary(new CardsImpl(source.getTargets().get(1).getTargets()), game, source); - } - return false; - } -} - -class GaeasBlessingTarget extends TargetCardInGraveyard { - - public GaeasBlessingTarget() { - super(0, 3, new FilterCard()); - } - - public GaeasBlessingTarget(final GaeasBlessingTarget target) { - super(target); - } - - @Override - public boolean canTarget(UUID id, Ability source, Game game) { - Card card = game.getCard(id); - if (card != null && game.getState().getZone(card.getId()) == Zone.GRAVEYARD) { - UUID firstTarget = source.getFirstTarget(); - if (firstTarget != null && game.getPlayer(firstTarget).getGraveyard().contains(id)) { - return filter.match(card, game); - } - } - return false; - } - - @Override - public GaeasBlessingTarget copy() { - return new GaeasBlessingTarget(this); - } -} - class GaeasBlessingTriggeredAbility extends ZoneChangeTriggeredAbility { - public GaeasBlessingTriggeredAbility() { + GaeasBlessingTriggeredAbility() { super(Zone.LIBRARY, Zone.GRAVEYARD, new GaeasBlessingGraveToLibraryEffect(), "", false); } - public GaeasBlessingTriggeredAbility(final GaeasBlessingTriggeredAbility ability) { + private GaeasBlessingTriggeredAbility(final GaeasBlessingTriggeredAbility ability) { super(ability); } @@ -127,12 +70,12 @@ public String getRule() { class GaeasBlessingGraveToLibraryEffect extends OneShotEffect { - public GaeasBlessingGraveToLibraryEffect() { + GaeasBlessingGraveToLibraryEffect() { super(Outcome.GainLife); staticText = "shuffle your graveyard into your library"; } - public GaeasBlessingGraveToLibraryEffect(final GaeasBlessingGraveToLibraryEffect effect) { + private GaeasBlessingGraveToLibraryEffect(final GaeasBlessingGraveToLibraryEffect effect) { super(effect); } diff --git a/Mage.Sets/src/mage/cards/g/GalazethPrismari.java b/Mage.Sets/src/mage/cards/g/GalazethPrismari.java new file mode 100644 index 000000000000..1d3478b61c0f --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GalazethPrismari.java @@ -0,0 +1,57 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.mana.ConditionalAnyColorManaAbility; +import mage.abilities.mana.builder.common.InstantOrSorcerySpellManaBuilder; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; +import mage.game.permanent.token.TreasureToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GalazethPrismari extends CardImpl { + + public GalazethPrismari(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ELDER); + this.subtype.add(SubType.DRAGON); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Galazeth Prismari enters the battlefield, create a Treasure token. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new TreasureToken()))); + + // Artifacts you control have "{T}: Add one mana of any color. Spend this mana only to cast an instant or sorcery spell." + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + new ConditionalAnyColorManaAbility(1, new InstantOrSorcerySpellManaBuilder()), + Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_ARTIFACTS + ))); + } + + private GalazethPrismari(final GalazethPrismari card) { + super(card); + } + + @Override + public GalazethPrismari copy() { + return new GalazethPrismari(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GalepowderMage.java b/Mage.Sets/src/mage/cards/g/GalepowderMage.java index 3345eb879ec2..8aecee8b0492 100644 --- a/Mage.Sets/src/mage/cards/g/GalepowderMage.java +++ b/Mage.Sets/src/mage/cards/g/GalepowderMage.java @@ -17,7 +17,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/g/Galvanoth.java b/Mage.Sets/src/mage/cards/g/Galvanoth.java index e1830588c0c2..5e0228d23ce1 100644 --- a/Mage.Sets/src/mage/cards/g/Galvanoth.java +++ b/Mage.Sets/src/mage/cards/g/Galvanoth.java @@ -60,7 +60,7 @@ public boolean apply(Game game, Ability source) { Card card = controller.getLibrary().getFromTop(game); if (card != null) { controller.lookAtCards(source, null, new CardsImpl(card), game); - if (card.isInstant() || card.isSorcery()) { + if (card.isInstantOrSorcery()) { if (controller.chooseUse(Outcome.PlayForFree, "Cast " + card.getName() + " without paying its mana cost?", source, game)) { game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); controller.cast(controller.chooseAbilityForCast(card, game, true), diff --git a/Mage.Sets/src/mage/cards/g/Gamble.java b/Mage.Sets/src/mage/cards/g/Gamble.java index a45b261d1ac3..79d4114fcf6f 100644 --- a/Mage.Sets/src/mage/cards/g/Gamble.java +++ b/Mage.Sets/src/mage/cards/g/Gamble.java @@ -25,7 +25,7 @@ public Gamble(UUID ownerId, CardSetInfo setInfo) { effect.setText("Search your library for a card, put that card into your hand"); this.getSpellAbility().addEffect(effect); effect = new DiscardControllerEffect(1, true); - effect.setText(", discard a card at random, then shuffle your library"); + effect.setText(", discard a card at random, then shuffle"); this.getSpellAbility().addEffect(effect); } diff --git a/Mage.Sets/src/mage/cards/g/GamePlan.java b/Mage.Sets/src/mage/cards/g/GamePlan.java index db0532897d7a..7fc45b72bad6 100644 --- a/Mage.Sets/src/mage/cards/g/GamePlan.java +++ b/Mage.Sets/src/mage/cards/g/GamePlan.java @@ -28,7 +28,7 @@ public GamePlan(UUID ownerId, CardSetInfo setInfo) { Effect effect = new DrawCardAllEffect(7); effect.setText(", then draws seven cards"); this.getSpellAbility().addEffect(effect); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private GamePlan(final GamePlan card) { diff --git a/Mage.Sets/src/mage/cards/g/GargantuanGorilla.java b/Mage.Sets/src/mage/cards/g/GargantuanGorilla.java index 2d16cebed065..0ccdda880563 100644 --- a/Mage.Sets/src/mage/cards/g/GargantuanGorilla.java +++ b/Mage.Sets/src/mage/cards/g/GargantuanGorilla.java @@ -16,7 +16,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -88,7 +88,7 @@ public boolean apply(Game game, Ability source) { if (controller != null && sourcePermanent != null) { TargetControlledPermanent target = new TargetControlledPermanent(1, 1, filter, true); SacrificeTargetCost cost = new SacrificeTargetCost(target); - if (!controller.chooseUse(Outcome.Benefit, "Do you wish to sacrifice a Forest?", source, game) + if (!controller.chooseUse(Outcome.Benefit, "Sacrifice a Forest?", source, game) || !cost.canPay(source, source, source.getControllerId(), game) || !cost.pay(source, game, source, source.getControllerId(), true)) { sourcePermanent.sacrifice(source, game); diff --git a/Mage.Sets/src/mage/cards/g/GarrukApexPredator.java b/Mage.Sets/src/mage/cards/g/GarrukApexPredator.java index 7c8a2c0716c5..dbdfb8147339 100644 --- a/Mage.Sets/src/mage/cards/g/GarrukApexPredator.java +++ b/Mage.Sets/src/mage/cards/g/GarrukApexPredator.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.command.emblems.GarrukApexPredatorEmblem; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java b/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java index 5d2aaf48a866..c8c2e32740a2 100644 --- a/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java +++ b/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java @@ -20,7 +20,7 @@ import mage.constants.SuperType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/g/GarruksHorde.java b/Mage.Sets/src/mage/cards/g/GarruksHorde.java index a8513fe25faa..37cb6954913b 100644 --- a/Mage.Sets/src/mage/cards/g/GarruksHorde.java +++ b/Mage.Sets/src/mage/cards/g/GarruksHorde.java @@ -34,8 +34,8 @@ public GarruksHorde(UUID ownerId, CardSetInfo setInfo) { // Play with the top card of your library revealed. this.addAbility(new SimpleStaticAbility(new PlayWithTheTopCardRevealedEffect())); - // You may cast the top card of your library if it's a creature card. - this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter))); + // You may cast creature spells from the top of your library. + this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false))); } private GarruksHorde(final GarruksHorde card) { diff --git a/Mage.Sets/src/mage/cards/g/GarruksPackleader.java b/Mage.Sets/src/mage/cards/g/GarruksPackleader.java index 6d80a1a8eb63..937bdb456580 100644 --- a/Mage.Sets/src/mage/cards/g/GarruksPackleader.java +++ b/Mage.Sets/src/mage/cards/g/GarruksPackleader.java @@ -15,7 +15,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/g/GateToTheAfterlife.java b/Mage.Sets/src/mage/cards/g/GateToTheAfterlife.java index df641cdfd4e3..0a0bd474bb47 100644 --- a/Mage.Sets/src/mage/cards/g/GateToTheAfterlife.java +++ b/Mage.Sets/src/mage/cards/g/GateToTheAfterlife.java @@ -81,7 +81,7 @@ public GateToTheAfterlifeEffect() { super(Outcome.Benefit); this.staticText = "Search your graveyard, hand, and/or library for a card named " + cardName - + " and put it onto the battlefield. If you search your library this way, shuffle it"; + + " and put it onto the battlefield. If you search your library this way, shuffle"; } public GateToTheAfterlifeEffect(final GateToTheAfterlifeEffect effect) { diff --git a/Mage.Sets/src/mage/cards/g/GatstafArsonists.java b/Mage.Sets/src/mage/cards/g/GatstafArsonists.java index 22acdb8783c8..d71ff6bac961 100644 --- a/Mage.Sets/src/mage/cards/g/GatstafArsonists.java +++ b/Mage.Sets/src/mage/cards/g/GatstafArsonists.java @@ -1,10 +1,10 @@ - package mage.cards.g; import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.TransformSourceEffect; @@ -33,8 +33,7 @@ public GatstafArsonists(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each upkeep, if no spells were cast last turn, transform Gatstaf Arsonists. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private GatstafArsonists(final GatstafArsonists card) { diff --git a/Mage.Sets/src/mage/cards/g/GatstafHowler.java b/Mage.Sets/src/mage/cards/g/GatstafHowler.java index 16c91b5ba5ed..df0faacf87cd 100644 --- a/Mage.Sets/src/mage/cards/g/GatstafHowler.java +++ b/Mage.Sets/src/mage/cards/g/GatstafHowler.java @@ -1,20 +1,14 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.keyword.IntimidateAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** * @author nantuko @@ -22,7 +16,7 @@ public final class GatstafHowler extends CardImpl { public GatstafHowler(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.WEREWOLF); this.color.setGreen(true); @@ -36,8 +30,7 @@ public GatstafHowler(UUID ownerId, CardSetInfo setInfo) { this.addAbility(IntimidateAbility.getInstance()); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Gatstaf Howler. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private GatstafHowler(final GatstafHowler card) { diff --git a/Mage.Sets/src/mage/cards/g/GatstafRavagers.java b/Mage.Sets/src/mage/cards/g/GatstafRavagers.java index 924f6fdb0e20..ce353bf6473b 100644 --- a/Mage.Sets/src/mage/cards/g/GatstafRavagers.java +++ b/Mage.Sets/src/mage/cards/g/GatstafRavagers.java @@ -1,33 +1,26 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.keyword.MenaceAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class GatstafRavagers extends CardImpl { public GatstafRavagers(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.WEREWOLF); this.power = new MageInt(6); this.toughness = new MageInt(5); - + this.color.setRed(true); this.transformable = true; @@ -35,9 +28,9 @@ public GatstafRavagers(UUID ownerId, CardSetInfo setInfo) { // Menace this.addAbility(new MenaceAbility()); + // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Gatstaf Ravagers. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private GatstafRavagers(final GatstafRavagers card) { diff --git a/Mage.Sets/src/mage/cards/g/GatstafShepherd.java b/Mage.Sets/src/mage/cards/g/GatstafShepherd.java index 8c98fe5a6d04..1e49e181c0fc 100644 --- a/Mage.Sets/src/mage/cards/g/GatstafShepherd.java +++ b/Mage.Sets/src/mage/cards/g/GatstafShepherd.java @@ -1,19 +1,14 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** * @author nantuko @@ -21,7 +16,7 @@ public final class GatstafShepherd extends CardImpl { public GatstafShepherd(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WEREWOLF); @@ -33,8 +28,7 @@ public GatstafShepherd(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each upkeep, if no spells were cast last turn, transform Gatstaf Shepherd. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private GatstafShepherd(final GatstafShepherd card) { diff --git a/Mage.Sets/src/mage/cards/g/GauntletOfPower.java b/Mage.Sets/src/mage/cards/g/GauntletOfPower.java index 5ca8d54ccdef..858b4af28059 100644 --- a/Mage.Sets/src/mage/cards/g/GauntletOfPower.java +++ b/Mage.Sets/src/mage/cards/g/GauntletOfPower.java @@ -1,11 +1,9 @@ package mage.cards.g; -import java.util.ArrayList; -import java.util.List; import mage.Mana; import mage.ObjectColor; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.Effect; @@ -20,12 +18,13 @@ import mage.filter.common.FilterLandPermanent; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.events.ManaEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; /** @@ -43,7 +42,7 @@ public GauntletOfPower(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{5}"); // As Gauntlet of Power enters the battlefield, choose a color. - this.addAbility(new EntersBattlefieldAbility(new ChooseColorEffect(Outcome.Neutral))); + this.addAbility(new AsEntersBattlefieldAbility(new ChooseColorEffect(Outcome.Neutral))); // Creatures of the chosen color get +1/+1. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GauntletOfPowerBoostEffect())); diff --git a/Mage.Sets/src/mage/cards/g/GavonyUnhallowed.java b/Mage.Sets/src/mage/cards/g/GavonyUnhallowed.java index 816604fbab08..d4758818d82b 100644 --- a/Mage.Sets/src/mage/cards/g/GavonyUnhallowed.java +++ b/Mage.Sets/src/mage/cards/g/GavonyUnhallowed.java @@ -12,7 +12,7 @@ import mage.constants.TargetController; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/g/GazeOfGranite.java b/Mage.Sets/src/mage/cards/g/GazeOfGranite.java index b884ed9bfa73..dae39139fa9f 100644 --- a/Mage.Sets/src/mage/cards/g/GazeOfGranite.java +++ b/Mage.Sets/src/mage/cards/g/GazeOfGranite.java @@ -10,7 +10,7 @@ import mage.constants.ComparisonType; import mage.constants.Outcome; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; @@ -20,9 +20,9 @@ */ public final class GazeOfGranite extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("each nonland permanent with converted mana cost X or less"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("each nonland permanent with mana value X or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public GazeOfGranite(UUID ownerId, CardSetInfo setInfo) { @@ -47,7 +47,7 @@ class GazeOfGraniteEffect extends OneShotEffect { public GazeOfGraniteEffect() { super(Outcome.DestroyPermanent); - staticText = "Destroy each nonland permanent with converted mana cost X or less"; + staticText = "Destroy each nonland permanent with mana value X or less"; } public GazeOfGraniteEffect(final GazeOfGraniteEffect effect) { @@ -62,7 +62,7 @@ public GazeOfGraniteEffect copy() { @Override public boolean apply(Game game, Ability source) { for (Permanent permanent : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) { - if (!permanent.isLand() && permanent.getConvertedManaCost() <= source.getManaCostsToPay().getX()) { + if (!permanent.isLand() && permanent.getManaValue() <= source.getManaCostsToPay().getX()) { permanent.destroy(source, game, false); } } diff --git a/Mage.Sets/src/mage/cards/g/GazeOfPain.java b/Mage.Sets/src/mage/cards/g/GazeOfPain.java index 0e3a8fee35d7..f1f5d1531ad7 100644 --- a/Mage.Sets/src/mage/cards/g/GazeOfPain.java +++ b/Mage.Sets/src/mage/cards/g/GazeOfPain.java @@ -1,23 +1,17 @@ package mage.cards.g; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; -import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; import mage.game.Game; -import mage.game.combat.CombatGroup; import mage.game.events.DamageEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -25,8 +19,9 @@ import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class GazeOfPain extends CardImpl { @@ -50,38 +45,29 @@ public GazeOfPain copy() { class GazeOfPainDelayedTriggeredAbility extends DelayedTriggeredAbility { - Set creatures = new HashSet<>(); - - public GazeOfPainDelayedTriggeredAbility() { - super(new GazeOfPainEffect(), Duration.EndOfTurn, false, true); + GazeOfPainDelayedTriggeredAbility() { + super(null, Duration.EndOfTurn, false, true); + this.addTarget(new TargetCreaturePermanent()); } - public GazeOfPainDelayedTriggeredAbility(GazeOfPainDelayedTriggeredAbility ability) { + private GazeOfPainDelayedTriggeredAbility(final GazeOfPainDelayedTriggeredAbility ability) { super(ability); } @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARE_BLOCKERS_STEP; + return event.getType() == GameEvent.EventType.UNBLOCKED_ATTACKER; } @Override public boolean checkTrigger(GameEvent event, Game game) { - for (CombatGroup combatGroup : game.getCombat().getGroups()) { - if (combatGroup.getBlockers().isEmpty()) { - for (UUID attackerId : combatGroup.getAttackers()) { - if (game.getPermanent(attackerId).getControllerId() == controllerId - && game.getPermanent(attackerId).isAttacking()) { - creatures.add(attackerId); - } - } - } + if (!isControlledBy(game.getControllerId(event.getTargetId()))) { + return false; } - if (!creatures.isEmpty()) { - game.getState().setValue("gazeOfPain", creatures); - return true; - } - return false; + this.getEffects().clear(); + this.addEffect(new GazeOfPainEffect(new MageObjectReference(event.getTargetId(), game))); + this.addEffect(new GazeOfPainDamageEffect().setTargetPointer(new FixedTarget(event.getTargetId(), game))); + return true; } @Override @@ -91,21 +77,24 @@ public GazeOfPainDelayedTriggeredAbility copy() { @Override public String getRule() { - return "Until end of turn, whenever a creature you control attacks and isn't blocked, " + super.getRule(); + return "Until end of turn, whenever a creature you control attacks and isn't blocked, " + + "you may choose to have it deal damage equal to its power to a target creature. " + + "If you do, it assigns no combat damage this turn."; } } class GazeOfPainEffect extends OneShotEffect { - Set creatures = new HashSet<>(); + private final MageObjectReference mor; - GazeOfPainEffect() { + GazeOfPainEffect(MageObjectReference mor) { super(Outcome.Benefit); - this.staticText = "you may choose to have it deal damage equal to its power to a target creature. If you do, it assigns no combat damage this turn."; + this.mor = mor; } - GazeOfPainEffect(final GazeOfPainEffect effect) { + private GazeOfPainEffect(final GazeOfPainEffect effect) { super(effect); + this.mor = effect.mor; } @Override @@ -115,46 +104,29 @@ public GazeOfPainEffect copy() { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - creatures = (Set) game.getState().getValue("gazeOfPain"); - if (!creatures.isEmpty()) { - for (UUID attackerId : creatures) { - Permanent attacker = game.getPermanent(attackerId); - if (controller != null - && attacker != null) { - if (controller.chooseUse(outcome, "Do you wish to deal damage equal to " + attacker.getName() + "'s power to a target creature?", source, game)) { - TargetCreaturePermanent target = new TargetCreaturePermanent(); - if (target.canChoose(source.getSourceId(), controller.getId(), game) - && controller.choose(Outcome.Detriment, target, source.getSourceId(), game)) { - Effect effect = new DamageTargetEffect(attacker.getPower().getValue()); - effect.setTargetPointer(new FixedTarget(target.getFirstTarget())); - effect.apply(game, source); - ContinuousEffect effect2 = new AssignNoCombatDamageTargetEffect(); - effect2.setTargetPointer(new FixedTarget(attackerId)); - game.addEffect(effect2, source); - } - } - } - } - return true; - } - return false; + Player player = game.getPlayer(source.getControllerId()); + Permanent creature = mor.getPermanent(game); + Permanent targeted = game.getPermanent(source.getFirstTarget()); + return player != null + && creature != null + && targeted != null + && targeted.damage(creature.getPower().getValue(), creature.getId(), source, game) > 0; } } -class AssignNoCombatDamageTargetEffect extends ReplacementEffectImpl { +class GazeOfPainDamageEffect extends ReplacementEffectImpl { - public AssignNoCombatDamageTargetEffect() { + GazeOfPainDamageEffect() { super(Duration.EndOfTurn, Outcome.Neutral); } - public AssignNoCombatDamageTargetEffect(final AssignNoCombatDamageTargetEffect effect) { + private GazeOfPainDamageEffect(final GazeOfPainDamageEffect effect) { super(effect); } @Override - public AssignNoCombatDamageTargetEffect copy() { - return new AssignNoCombatDamageTargetEffect(this); + public GazeOfPainDamageEffect copy() { + return new GazeOfPainDamageEffect(this); } @Override @@ -170,9 +142,8 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { @Override public boolean checksEventType(GameEvent event, Game game) { switch (event.getType()) { - case DAMAGE_CREATURE: + case DAMAGE_PERMANENT: case DAMAGE_PLAYER: - case DAMAGE_PLANESWALKER: return true; default: return false; @@ -181,8 +152,6 @@ public boolean checksEventType(GameEvent event, Game game) { @Override public boolean applies(GameEvent event, Ability source, Game game) { - DamageEvent damageEvent = (DamageEvent) event; - return event.getSourceId().equals(targetPointer.getFirst(game, source)) - && damageEvent.isCombatDamage(); + return ((DamageEvent) event).isCombatDamage() && event.getSourceId().equals(targetPointer.getFirst(game, source)); } } diff --git a/Mage.Sets/src/mage/cards/g/GeierReachBandit.java b/Mage.Sets/src/mage/cards/g/GeierReachBandit.java index 2ba42eefb499..9fde0db14a41 100644 --- a/Mage.Sets/src/mage/cards/g/GeierReachBandit.java +++ b/Mage.Sets/src/mage/cards/g/GeierReachBandit.java @@ -1,21 +1,15 @@ - package mage.cards.g; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** * @author LevelX2 @@ -38,8 +32,7 @@ public GeierReachBandit(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each upkeep, if no spells were cast last turn, transform Geier Reach Bandit. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private GeierReachBandit(final GeierReachBandit card) { diff --git a/Mage.Sets/src/mage/cards/g/GemOfBecoming.java b/Mage.Sets/src/mage/cards/g/GemOfBecoming.java index 9ccd85ccc2df..9873bfba0267 100644 --- a/Mage.Sets/src/mage/cards/g/GemOfBecoming.java +++ b/Mage.Sets/src/mage/cards/g/GemOfBecoming.java @@ -30,7 +30,8 @@ public GemOfBecoming(UUID ownerId, CardSetInfo setInfo) { Ability ability = new SimpleActivatedAbility( new SearchLibraryPutInHandEffect( new GemOfBecomingTarget(), true - ), new GenericManaCost(3) + ).setText("search your library for an Island card, a Swamp card, and a Mountain card. " + + "Reveal those cards, put them into your hand, then shuffle"), new GenericManaCost(3) ); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); diff --git a/Mage.Sets/src/mage/cards/g/GemstoneCaverns.java b/Mage.Sets/src/mage/cards/g/GemstoneCaverns.java index 44f6010d85d9..d04721cd29c6 100644 --- a/Mage.Sets/src/mage/cards/g/GemstoneCaverns.java +++ b/Mage.Sets/src/mage/cards/g/GemstoneCaverns.java @@ -87,7 +87,7 @@ public String getRule() { @Override public boolean askUseOpeningHandAction(Card card, Player player, Game game) { - return player.chooseUse(Outcome.PutCardInPlay, "Do you wish to put " + card.getIdName() + " into play?", this, game); + return player.chooseUse(Outcome.PutCardInPlay, "Put " + card.getIdName() + " onto the battlefield?", this, game); } @Override diff --git a/Mage.Sets/src/mage/cards/g/GenesisHydra.java b/Mage.Sets/src/mage/cards/g/GenesisHydra.java index 3b9ce87bb716..2b5cd6c7a211 100644 --- a/Mage.Sets/src/mage/cards/g/GenesisHydra.java +++ b/Mage.Sets/src/mage/cards/g/GenesisHydra.java @@ -19,7 +19,7 @@ import mage.filter.FilterCard; import mage.filter.common.FilterPermanentCard; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; @@ -59,7 +59,7 @@ class GenesisHydraPutOntoBattlefieldEffect extends OneShotEffect { public GenesisHydraPutOntoBattlefieldEffect() { super(Outcome.PutCardInPlay); - staticText = "reveal the top X cards of your library. You may put a nonland permanent card with converted mana cost X or less from among them onto the battlefield. Then shuffle the rest into your library"; + staticText = "reveal the top X cards of your library. You may put a nonland permanent card with mana value X or less from among them onto the battlefield. Then shuffle the rest into your library"; } public GenesisHydraPutOntoBattlefieldEffect(final GenesisHydraPutOntoBattlefieldEffect effect) { @@ -75,9 +75,9 @@ public boolean apply(Game game, Ability source) { if (count > 0) { Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, count)); controller.revealCards(source, cards, game); - FilterCard filter = new FilterPermanentCard("a nonland permanent card with converted mana cost " + count + " or less to put onto the battlefield"); + FilterCard filter = new FilterPermanentCard("a nonland permanent card with mana value " + count + " or less to put onto the battlefield"); filter.add(Predicates.not(CardType.LAND.getPredicate())); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, count + 1)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, count + 1)); TargetCard target1 = new TargetCard(Zone.LIBRARY, filter); target1.setRequired(false); if (cards.count(filter, controller.getId(), source.getSourceId(), game) > 0) { @@ -92,7 +92,7 @@ public boolean apply(Game game, Ability source) { game.informPlayers(controller.getLogName() + " didn't choose anything"); } } else { - game.informPlayers("No nonland permanent card with converted mana cost " + count + " or less to choose."); + game.informPlayers("No nonland permanent card with mana value " + count + " or less to choose."); } if (!cards.isEmpty()) { controller.moveCards(cards, Zone.LIBRARY, source, game); diff --git a/Mage.Sets/src/mage/cards/g/GenesisUltimatum.java b/Mage.Sets/src/mage/cards/g/GenesisUltimatum.java index 129c44bf2afd..210cf9f1683e 100644 --- a/Mage.Sets/src/mage/cards/g/GenesisUltimatum.java +++ b/Mage.Sets/src/mage/cards/g/GenesisUltimatum.java @@ -29,7 +29,7 @@ public GenesisUltimatum(UUID ownerId, CardSetInfo setInfo) { // Look at the top five cards of your library. Put any number of permanent cards from among them onto the battlefield and the rest into your hand. Exile Genesis Ultimatum. this.getSpellAbility().addEffect(new GenesisUltimatumEffect()); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private GenesisUltimatum(final GenesisUltimatum card) { diff --git a/Mage.Sets/src/mage/cards/g/GenesisWave.java b/Mage.Sets/src/mage/cards/g/GenesisWave.java index 5b337d25381d..f809721ebca6 100644 --- a/Mage.Sets/src/mage/cards/g/GenesisWave.java +++ b/Mage.Sets/src/mage/cards/g/GenesisWave.java @@ -12,7 +12,7 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; @@ -46,7 +46,7 @@ class GenesisWaveEffect extends OneShotEffect { public GenesisWaveEffect() { super(Outcome.PutCardInPlay); - staticText = "Reveal the top X cards of your library. You may put any number of permanent cards with converted mana cost X or less from among them onto the battlefield. Then put all cards revealed this way that weren't put onto the battlefield into your graveyard"; + staticText = "Reveal the top X cards of your library. You may put any number of permanent cards with mana value X or less from among them onto the battlefield. Then put all cards revealed this way that weren't put onto the battlefield into your graveyard"; } public GenesisWaveEffect(final GenesisWaveEffect effect) { @@ -63,8 +63,8 @@ public boolean apply(Game game, Ability source) { Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, xValue)); if (!cards.isEmpty()) { controller.revealCards(source, cards, game); - FilterCard filter = new FilterPermanentCard("cards with converted mana cost " + xValue + " or less to put onto the battlefield"); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, xValue + 1)); + FilterCard filter = new FilterPermanentCard("cards with mana value " + xValue + " or less to put onto the battlefield"); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); TargetCard target1 = new TargetCard(0, Integer.MAX_VALUE, Zone.LIBRARY, filter); target1.setNotTarget(true); controller.choose(Outcome.PutCardInPlay, cards, target1, game); diff --git a/Mage.Sets/src/mage/cards/g/GeodeGolem.java b/Mage.Sets/src/mage/cards/g/GeodeGolem.java index 82b5c9e8ba4a..bdc737f1a8b0 100644 --- a/Mage.Sets/src/mage/cards/g/GeodeGolem.java +++ b/Mage.Sets/src/mage/cards/g/GeodeGolem.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; -import mage.abilities.costs.mana.ManaCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.Card; @@ -16,8 +15,6 @@ import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; -import mage.util.ManaUtil; -import mage.watchers.common.CommanderPlaysCountWatcher; import java.util.Set; import java.util.UUID; @@ -37,8 +34,7 @@ public GeodeGolem(UUID ownerId, CardSetInfo setInfo) { // Trample this.addAbility(TrampleAbility.getInstance()); - // Whenever Geode Golem deals combat damage to a player, you may - // cast your commander from the command zone without paying its mana cost. + // Whenever Geode Golem deals combat damage to a player, you may cast your commander from the command zone without paying its mana cost. this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new GeodeGolemEffect(), true)); } @@ -83,12 +79,11 @@ public boolean apply(Game game, Ability source) { target.setNotTarget(true); if (controller.canRespond() && controller.choose(Outcome.PlayForFree, new CardsImpl(commandersInCommandZone), target, game)) { - if (target.getFirstTarget() != null) { - selectedCommander = commandersInCommandZone.stream() - .filter(c -> c.getId().equals(target.getFirstTarget())) - .findFirst() - .orElse(null); - } + selectedCommander = commandersInCommandZone.stream() + .filter(c -> c.getId().equals(target.getFirstTarget())) + .findFirst() + .orElse(null); + } } @@ -96,27 +91,17 @@ public boolean apply(Game game, Ability source) { return false; } - // PAY - // TODO: this is broken with the commander cost reduction effect - ManaCost cost = null; - CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class); - int castCount = watcher.getPlaysCount(selectedCommander.getId()); - if (castCount > 0) { - cost = ManaUtil.createManaCost(castCount * 2, false); - } - - // CAST: as spell or as land - if (cost == null - || cost.pay(source, game, source, controller.getId(), false, null)) { - if (selectedCommander.getSpellAbility() != null) { // TODO: can be broken with mdf cards (one side creature, one side land)? - game.getState().setValue("PlayFromNotOwnHandZone" + selectedCommander.getId(), Boolean.TRUE); - Boolean commanderWasCast = controller.cast(controller.chooseAbilityForCast(selectedCommander, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + selectedCommander.getId(), null); - return commanderWasCast; - } else { - return controller.playLand(selectedCommander, game, true); - } + // commander tax applies as additional cost + if (selectedCommander.getSpellAbility() != null) { + game.getState().setValue("PlayFromNotOwnHandZone" + selectedCommander.getId(), Boolean.TRUE); + Boolean commanderWasCast = controller.cast(controller.chooseAbilityForCast(selectedCommander, game, true), + game, true, new ApprovingObject(source, game)); + game.getState().setValue("PlayFromNotOwnHandZone" + selectedCommander.getId(), null); + return commanderWasCast; + } else { + // play commander as land is xmage feature, but mtg rules for text "cast commander" doesn't allow that + // TODO: improve lands support for "cast your commander" (allow land play from mdf cards)? + return controller.playLand(selectedCommander, game, true); } } return false; diff --git a/Mage.Sets/src/mage/cards/g/GeomancersGambit.java b/Mage.Sets/src/mage/cards/g/GeomancersGambit.java index e1bcac24d811..add2ce62d79c 100644 --- a/Mage.Sets/src/mage/cards/g/GeomancersGambit.java +++ b/Mage.Sets/src/mage/cards/g/GeomancersGambit.java @@ -52,7 +52,7 @@ class GeomancersGambitEffect extends OneShotEffect { super(Outcome.PutLandInPlay); this.staticText = "Its controller may search their library " + "for a basic land card, put it onto the battlefield, " - + "then shuffle their library"; + + "then shuffle"; } private GeomancersGambitEffect(final GeomancersGambitEffect effect) { @@ -74,7 +74,7 @@ public boolean apply(Game game, Ability source) { if (controller == null) { return false; } - if (!controller.chooseUse(Outcome.PutLandInPlay, "Do you wish to search for a basic land, put it onto the battlefield and then shuffle your library?", source, game)) { + if (!controller.chooseUse(Outcome.PutLandInPlay, "Search for a basic land, put it onto the battlefield, and then shuffle?", source, game)) { return true; } TargetCardInLibrary target = new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND); diff --git a/Mage.Sets/src/mage/cards/g/GeometricNexus.java b/Mage.Sets/src/mage/cards/g/GeometricNexus.java new file mode 100644 index 000000000000..237b820beb6b --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GeometricNexus.java @@ -0,0 +1,104 @@ +package mage.cards.g; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SpellCastAllTriggeredAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.RemoveAllCountersSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.token.QuandrixToken; +import mage.game.stack.Spell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GeometricNexus extends CardImpl { + + public GeometricNexus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); + + // Whenever a player casts an instant or sorcery spell, put a number of charge counters on Geometric Nexus equal to that spell's mana value. + this.addAbility(new SpellCastAllTriggeredAbility( + new AddCountersSourceEffect( + CounterType.CHARGE.createInstance(0), + GeometricNexusMVValue.instance, false + ).setText("put a number of charge counters on {this} equal to that spell's mana value"), + StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, false + )); + + // {6}, {T}, Remove all charge counters from Geometric Nexus: Create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it, where X is the number of charge counters removed this way. + Ability ability = new SimpleActivatedAbility(QuandrixToken.getEffect( + GeometricNexusRemovedCounterValue.instance, "Put X +1/+1 counters on it, " + + "where X is the number of charge counters removed this way" + ), new GenericManaCost(6)); + ability.addCost(new TapSourceCost()); + ability.addCost(new RemoveAllCountersSourceCost(CounterType.CHARGE)); + this.addAbility(ability); + } + + private GeometricNexus(final GeometricNexus card) { + super(card); + } + + @Override + public GeometricNexus copy() { + return new GeometricNexus(this); + } +} + +enum GeometricNexusMVValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + Spell spell = (Spell) effect.getValue("spellCast"); + return spell != null ? spell.getManaValue() : 0; + } + + @Override + public GeometricNexusMVValue copy() { + return instance; + } + + @Override + public String getMessage() { + return ""; + } +} + +enum GeometricNexusRemovedCounterValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + int countersRemoved = 0; + for (Cost cost : sourceAbility.getCosts()) { + if (cost instanceof RemoveAllCountersSourceCost) { + countersRemoved = ((RemoveAllCountersSourceCost) cost).getRemovedCounters(); + } + } + return countersRemoved; + } + + @Override + public GeometricNexusRemovedCounterValue copy() { + return instance; + } + + @Override + public String getMessage() { + return ""; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GerrardWeatherlightHero.java b/Mage.Sets/src/mage/cards/g/GerrardWeatherlightHero.java index 9e627ba1b4e5..7411e7c33b52 100644 --- a/Mage.Sets/src/mage/cards/g/GerrardWeatherlightHero.java +++ b/Mage.Sets/src/mage/cards/g/GerrardWeatherlightHero.java @@ -1,26 +1,21 @@ package mage.cards.g; import mage.MageInt; -import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileSourceEffect; import mage.abilities.keyword.FirstStrikeAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; import mage.players.Player; -import mage.watchers.Watcher; +import mage.watchers.common.CardsPutIntoGraveyardWatcher; -import java.util.ArrayList; -import java.util.List; import java.util.UUID; -import java.util.stream.Collectors; /** * @author TheElk801 @@ -42,7 +37,7 @@ public GerrardWeatherlightHero(UUID ownerId, CardSetInfo setInfo) { // When Gerrard, Weatherlight Hero dies, exile it and return to the battlefield all artifact and creature cards in your graveyard that were put there from the battlefield this turn. Ability ability = new DiesSourceTriggeredAbility(new ExileSourceEffect().setText("exile it")); ability.addEffect(new GerrardWeatherlightHeroEffect()); - this.addAbility(ability, new GerrardWeatherlightHeroWatcher()); + this.addAbility(ability, new CardsPutIntoGraveyardWatcher()); } private GerrardWeatherlightHero(final GerrardWeatherlightHero card) { @@ -57,6 +52,15 @@ public GerrardWeatherlightHero copy() { class GerrardWeatherlightHeroEffect extends OneShotEffect { + private static final FilterCard filter = new FilterCard(); + + static { + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.CREATURE.getPredicate() + )); + } + GerrardWeatherlightHeroEffect() { super(Outcome.Benefit); staticText = "and return to the battlefield all artifact and creature cards " + @@ -75,48 +79,12 @@ public GerrardWeatherlightHeroEffect copy() { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - GerrardWeatherlightHeroWatcher watcher = game.getState().getWatcher(GerrardWeatherlightHeroWatcher.class); - if (player == null || watcher == null) { + if (player == null) { return false; } - return player.moveCards( - player.getGraveyard() - .getCards(game) - .stream() - .filter(card -> watcher.checkCard(card, game)) - .collect(Collectors.toSet()), - Zone.BATTLEFIELD, source, game - ); - } -} - -class GerrardWeatherlightHeroWatcher extends Watcher { - - private final List cards = new ArrayList<>(); - - GerrardWeatherlightHeroWatcher() { - super(WatcherScope.GAME); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.ZONE_CHANGE - && ((ZoneChangeEvent) event).isDiesEvent()) { - cards.add(new MageObjectReference(event.getTargetId(), game)); - } - } - - boolean checkCard(Card card, Game game) { - if (!card.isCreature() && !card.isArtifact()) { - return false; - } - return cards.stream().anyMatch(mageObjectReference -> mageObjectReference.refersTo(card, game)); - } - - @Override - public void reset() { - super.reset(); - cards.clear(); + return player.moveCards(player.getGraveyard().getCards( + filter, source.getSourceId(), source.getControllerId(), game + ), Zone.BATTLEFIELD, source, game); } } // don’t mourn for me. this is my destiny. diff --git a/Mage.Sets/src/mage/cards/g/GethLordOfTheVault.java b/Mage.Sets/src/mage/cards/g/GethLordOfTheVault.java index d93dc196a8c3..ce4b6e749baa 100644 --- a/Mage.Sets/src/mage/cards/g/GethLordOfTheVault.java +++ b/Mage.Sets/src/mage/cards/g/GethLordOfTheVault.java @@ -24,7 +24,7 @@ */ public final class GethLordOfTheVault extends CardImpl { - private static final FilterCard filter = new FilterCard("artifact or creature card with converted mana cost X from an opponent's graveyard"); + private static final FilterCard filter = new FilterCard("artifact or creature card with mana value X from an opponent's graveyard"); static { filter.add(TargetController.OPPONENT.getOwnerPredicate()); @@ -66,7 +66,7 @@ class GethLordOfTheVaultEffect extends OneShotEffect { public GethLordOfTheVaultEffect() { super(Outcome.Benefit); - staticText = "Put target artifact or creature card with converted mana cost X from an opponent's graveyard onto the battlefield under your control tapped. Then that player mills X cards"; + staticText = "Put target artifact or creature card with mana value X from an opponent's graveyard onto the battlefield under your control tapped. Then that player mills X cards"; } public GethLordOfTheVaultEffect(final GethLordOfTheVaultEffect effect) { @@ -86,7 +86,7 @@ public boolean apply(Game game, Ability source) { controller.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null); Player player = game.getPlayer(card.getOwnerId()); if (player != null) { - player.millCards(card.getConvertedManaCost(), source, game); + player.millCards(card.getManaValue(), source, game); } return true; } diff --git a/Mage.Sets/src/mage/cards/g/GhaltaPrimalHunger.java b/Mage.Sets/src/mage/cards/g/GhaltaPrimalHunger.java index 4fe9c62cf563..82d3296a5beb 100644 --- a/Mage.Sets/src/mage/cards/g/GhaltaPrimalHunger.java +++ b/Mage.Sets/src/mage/cards/g/GhaltaPrimalHunger.java @@ -66,7 +66,7 @@ class GhaltaPrimalHungerCostReductionEffect extends CostModificationEffectImpl { GhaltaPrimalHungerCostReductionEffect() { super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "{this} costs {X} less to cast, where X is the total power of creatures you control"; + staticText = "this spell costs {X} less to cast, where X is the total power of creatures you control"; } GhaltaPrimalHungerCostReductionEffect(final GhaltaPrimalHungerCostReductionEffect effect) { diff --git a/Mage.Sets/src/mage/cards/g/GhituJourneymage.java b/Mage.Sets/src/mage/cards/g/GhituJourneymage.java index c5e192c07518..4e442174ef56 100644 --- a/Mage.Sets/src/mage/cards/g/GhituJourneymage.java +++ b/Mage.Sets/src/mage/cards/g/GhituJourneymage.java @@ -14,7 +14,7 @@ import mage.constants.CardType; import mage.constants.TargetController; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/g/GhostQuarter.java b/Mage.Sets/src/mage/cards/g/GhostQuarter.java index 2cb44a1d198f..f6242d01cea9 100644 --- a/Mage.Sets/src/mage/cards/g/GhostQuarter.java +++ b/Mage.Sets/src/mage/cards/g/GhostQuarter.java @@ -54,7 +54,7 @@ class GhostQuarterEffect extends OneShotEffect { public GhostQuarterEffect() { super(Outcome.PutLandInPlay); - this.staticText = "Its controller may search their library for a basic land card, put it onto the battlefield, then shuffle their library"; + this.staticText = "Its controller may search their library for a basic land card, put it onto the battlefield, then shuffle"; } public GhostQuarterEffect(final GhostQuarterEffect effect) { @@ -71,7 +71,7 @@ public boolean apply(Game game, Ability source) { Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { Player controller = game.getPlayer(permanent.getControllerId()); - if (controller != null && controller.chooseUse(Outcome.PutLandInPlay, "Do you wish to search for a basic land, put it onto the battlefield and then shuffle your library?", source, game)) { + if (controller != null && controller.chooseUse(Outcome.PutLandInPlay, "Search for a basic land, put it onto the battlefield, and then shuffle?", source, game)) { TargetCardInLibrary target = new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND); if (controller.searchLibrary(target, source, game)) { Card card = controller.getLibrary().getCard(target.getFirstTarget(), game); diff --git a/Mage.Sets/src/mage/cards/g/GhostfireBlade.java b/Mage.Sets/src/mage/cards/g/GhostfireBlade.java index 9f4d828a5e2c..b11633fd2740 100644 --- a/Mage.Sets/src/mage/cards/g/GhostfireBlade.java +++ b/Mage.Sets/src/mage/cards/g/GhostfireBlade.java @@ -2,16 +2,14 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.costs.CostAdjuster; import mage.abilities.effects.common.InfoEffect; import mage.abilities.effects.common.continuous.BoostEquippedEffect; import mage.abilities.keyword.EquipAbility; 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.game.Game; import mage.game.permanent.Permanent; import mage.util.CardUtil; @@ -29,35 +27,15 @@ public GhostfireBlade(UUID ownerId, CardSetInfo setInfo) { this.subtype.add(SubType.EQUIPMENT); // Equipped creature gets +2/+2 - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(2, 2))); + this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(2, 2))); // Equip {3} - this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(3))); // todo + Ability ability = new EquipAbility(3); + ability.setCostAdjuster(GhostfireBladeAdjuster.instance); + this.addAbility(ability); // Ghostfire Blade's equip ability costs {2} less to activate if it targets a colorless creature. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new InfoEffect("{this}'s equip ability costs {2} less to activate if it targets a colorless creature"))); - } - - @Override - public void adjustCosts(Ability ability, Game game) { - if (ability instanceof EquipAbility) { - if (game.inCheckPlayableState()) { - // checking state - boolean canSelectColorlessCreature = CardUtil.getAllPossibleTargets(ability, game).stream() - .map(game::getPermanent) - .filter(Objects::nonNull) - .anyMatch(permanent -> permanent.getColor(game).isColorless()); - if (canSelectColorlessCreature) { - CardUtil.reduceCost(ability, 2); - } - } else { - // real cast state - Permanent targetCreature = game.getPermanent(ability.getTargets().getFirstTarget()); - if (targetCreature != null && targetCreature.getColor(game).isColorless()) { - CardUtil.reduceCost(ability, 2); - } - } - } + this.addAbility(new SimpleStaticAbility(new InfoEffect("{this}'s equip ability costs {2} less to activate if it targets a colorless creature"))); } private GhostfireBlade(final GhostfireBlade card) { @@ -69,3 +47,28 @@ public GhostfireBlade copy() { return new GhostfireBlade(this); } } + +enum GhostfireBladeAdjuster implements CostAdjuster { + instance; + + @Override + public void adjustCosts(Ability ability, Game game) { + // checking state + if (game.inCheckPlayableState()) { + if (CardUtil + .getAllPossibleTargets(ability, game) + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .noneMatch(permanent -> permanent.getColor(game).isColorless())) { + return; + } + } else { + Permanent permanent = game.getPermanent(ability.getFirstTarget()); + if (permanent == null || !permanent.getColor(game).isColorless()) { + return; + } + } + CardUtil.reduceCost(ability, 2); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GhostsOfTheInnocent.java b/Mage.Sets/src/mage/cards/g/GhostsOfTheInnocent.java index 607e592ac741..d8ffa45b50c0 100644 --- a/Mage.Sets/src/mage/cards/g/GhostsOfTheInnocent.java +++ b/Mage.Sets/src/mage/cards/g/GhostsOfTheInnocent.java @@ -61,9 +61,8 @@ public GhostsOfTheInnocentPreventDamageEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE - || event.getType() == GameEvent.EventType.DAMAGE_PLAYER - || event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER; + return event.getType() == GameEvent.EventType.DAMAGE_PLAYER + || event.getType() == EventType.DAMAGE_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/g/Ghoulraiser.java b/Mage.Sets/src/mage/cards/g/Ghoulraiser.java index 123f0d889708..b70c6b27b9f6 100644 --- a/Mage.Sets/src/mage/cards/g/Ghoulraiser.java +++ b/Mage.Sets/src/mage/cards/g/Ghoulraiser.java @@ -1,12 +1,9 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -16,16 +13,18 @@ import mage.filter.FilterCard; import mage.game.Game; import mage.players.Player; -import mage.util.RandomUtil; +import mage.target.TargetCard; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; /** - * * @author North */ public final class Ghoulraiser extends CardImpl { public Ghoulraiser(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}"); this.subtype.add(SubType.ZOMBIE); this.power = new MageInt(2); @@ -47,12 +46,18 @@ public Ghoulraiser copy() { class GhoulraiserEffect extends OneShotEffect { - public GhoulraiserEffect() { + private static final FilterCard filter = new FilterCard(); + + static { + filter.add(SubType.ZOMBIE.getPredicate()); + } + + GhoulraiserEffect() { super(Outcome.ReturnToHand); this.staticText = "return a Zombie card at random from your graveyard to your hand"; } - public GhoulraiserEffect(final GhoulraiserEffect effect) { + private GhoulraiserEffect(final GhoulraiserEffect effect) { super(effect); } @@ -64,17 +69,13 @@ public GhoulraiserEffect copy() { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - FilterCard filter = new FilterCard("Zombie card"); - filter.add(SubType.ZOMBIE.getPredicate()); - Card[] cards = player.getGraveyard().getCards(filter, game).toArray(new Card[0]); - if (cards.length > 0) { - Card card = cards[RandomUtil.nextInt(cards.length)]; - card.moveToZone(Zone.HAND, source, game, true); - game.informPlayers(card.getName() + "returned to the hand of" + player.getLogName()); - return true; - } + if (player == null || player.getGraveyard().count(filter, game) < 1) { + return false; } - return false; + TargetCard target = new TargetCardInYourGraveyard(filter); + target.setNotTarget(true); + target.setRandom(true); + player.chooseTarget(outcome, target, source, game); + return player.moveCards(game.getCard(target.getFirstTarget()), Zone.HAND, source, game); } } diff --git a/Mage.Sets/src/mage/cards/g/GiantsSkewer.java b/Mage.Sets/src/mage/cards/g/GiantsSkewer.java index 1baf34e5fa29..8fc467963b4c 100644 --- a/Mage.Sets/src/mage/cards/g/GiantsSkewer.java +++ b/Mage.Sets/src/mage/cards/g/GiantsSkewer.java @@ -11,7 +11,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamagedCreatureEvent; +import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.permanent.token.FoodToken; @@ -65,12 +65,12 @@ public GiantsSkewerTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (!((DamagedCreatureEvent) event).isCombatDamage()) { + if (!((DamagedEvent) event).isCombatDamage()) { return false; } Permanent permanent = game.getPermanent(event.getSourceId()); diff --git a/Mage.Sets/src/mage/cards/g/GideonBlackblade.java b/Mage.Sets/src/mage/cards/g/GideonBlackblade.java index a1da242b62b4..00e7ec80c044 100644 --- a/Mage.Sets/src/mage/cards/g/GideonBlackblade.java +++ b/Mage.Sets/src/mage/cards/g/GideonBlackblade.java @@ -24,7 +24,7 @@ import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.TokenImpl; diff --git a/Mage.Sets/src/mage/cards/g/GideonChampionOfJustice.java b/Mage.Sets/src/mage/cards/g/GideonChampionOfJustice.java index bdf5924d09d2..93e3ba6cf0a2 100644 --- a/Mage.Sets/src/mage/cards/g/GideonChampionOfJustice.java +++ b/Mage.Sets/src/mage/cards/g/GideonChampionOfJustice.java @@ -1,8 +1,7 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageInt; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; @@ -16,26 +15,27 @@ import mage.abilities.keyword.IndestructibleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.SuperType; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.*; import mage.counters.CounterType; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.game.permanent.token.TokenImpl; +import mage.players.Player; import mage.target.common.TargetOpponent; +import java.util.Objects; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class GideonChampionOfJustice extends CardImpl { public GideonChampionOfJustice(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.PLANESWALKER},"{2}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{W}{W}"); this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.GIDEON); @@ -56,7 +56,6 @@ public GideonChampionOfJustice(UUID ownerId, CardSetInfo setInfo) { // -15: Exile all other permanents. this.addAbility(new LoyaltyAbility(new GideonExileAllOtherPermanentsEffect(), -15)); - } private GideonChampionOfJustice(final GideonChampionOfJustice card) { @@ -71,12 +70,12 @@ public GideonChampionOfJustice copy() { class GideonExileAllOtherPermanentsEffect extends OneShotEffect { - public GideonExileAllOtherPermanentsEffect() { + GideonExileAllOtherPermanentsEffect() { super(Outcome.Exile); - staticText = "Exile all other permanents"; + staticText = "exile all other permanents"; } - public GideonExileAllOtherPermanentsEffect(final GideonExileAllOtherPermanentsEffect effect) { + private GideonExileAllOtherPermanentsEffect(final GideonExileAllOtherPermanentsEffect effect) { super(effect); } @@ -87,18 +86,27 @@ public GideonExileAllOtherPermanentsEffect copy() { @Override public boolean apply(Game game, Ability source) { - for (Permanent permanent : game.getBattlefield().getAllActivePermanents()) { - if (!permanent.getId().equals(source.getSourceId())) { - permanent.moveToExile(null, null, source, game); - } + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; } - return true; + MageObjectReference mor = new MageObjectReference(source.getSourceObject(game), game); + Cards cards = new CardsImpl(); + game.getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_PERMANENT, + source.getControllerId(), game + ).stream() + .filter(Objects::nonNull) + .filter(permanent -> !mor.refersTo(permanent, game)) + .forEach(cards::add); + return player.moveCards(cards, Zone.EXILED, source, game); } } class GideonChampionOfJusticeToken extends TokenImpl { - public GideonChampionOfJusticeToken() { + GideonChampionOfJusticeToken() { super("", "indestructible Human Soldier creature with power and toughness each equal to the number of loyalty counters on him"); cardType.add(CardType.CREATURE); subtype.add(SubType.HUMAN); @@ -109,7 +117,8 @@ public GideonChampionOfJusticeToken() { this.addAbility(IndestructibleAbility.getInstance()); } - public GideonChampionOfJusticeToken(final GideonChampionOfJusticeToken token) { + + private GideonChampionOfJusticeToken(final GideonChampionOfJusticeToken token) { super(token); } diff --git a/Mage.Sets/src/mage/cards/g/GideonsIntervention.java b/Mage.Sets/src/mage/cards/g/GideonsIntervention.java index a8964c25f897..1549b31485a3 100644 --- a/Mage.Sets/src/mage/cards/g/GideonsIntervention.java +++ b/Mage.Sets/src/mage/cards/g/GideonsIntervention.java @@ -135,9 +135,8 @@ public boolean applies(GameEvent event, Ability source, Game game) { MageObject object = game.getObject(event.getSourceId()); Permanent targetPerm = game.getPermanent(event.getTargetId()); String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); - if (object != null && (event.getType() == GameEvent.EventType.DAMAGE_PLAYER - || targetPerm != null && (event.getType() == GameEvent.EventType.DAMAGE_CREATURE - || event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER))) { + if (object != null && (event.getType() == EventType.DAMAGE_PLAYER + || targetPerm != null && event.getType() == EventType.DAMAGE_PERMANENT)) { if (CardUtil.haveSameNames(object, cardName, game) && (event.getTargetId().equals(source.getControllerId()) || targetPerm != null && targetPerm.isControlledBy(source.getControllerId()))) { diff --git a/Mage.Sets/src/mage/cards/g/GideonsSacrifice.java b/Mage.Sets/src/mage/cards/g/GideonsSacrifice.java index d52bedde4e3e..49ee47ae86eb 100644 --- a/Mage.Sets/src/mage/cards/g/GideonsSacrifice.java +++ b/Mage.Sets/src/mage/cards/g/GideonsSacrifice.java @@ -107,9 +107,8 @@ private GideonsSacrificeEffectReplacementEffect(final GideonsSacrificeEffectRepl @Override public boolean checksEventType(GameEvent event, Game game) { switch (event.getType()) { - case DAMAGE_CREATURE: case DAMAGE_PLAYER: - case DAMAGE_PLANESWALKER: + case DAMAGE_PERMANENT: return true; default: return false; @@ -122,8 +121,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { && event.getPlayerId().equals(source.getControllerId())) { return true; } - if (event.getType() == GameEvent.EventType.DAMAGE_CREATURE - || event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER) { + if (event.getType() == GameEvent.EventType.DAMAGE_PERMANENT) { Permanent targetPermanent = game.getPermanent(event.getTargetId()); if (targetPermanent != null && targetPermanent.isControlledBy(source.getControllerId())) { diff --git a/Mage.Sets/src/mage/cards/g/GiftOfDoom.java b/Mage.Sets/src/mage/cards/g/GiftOfDoom.java index ee063f9baaef..15265c8dc64e 100644 --- a/Mage.Sets/src/mage/cards/g/GiftOfDoom.java +++ b/Mage.Sets/src/mage/cards/g/GiftOfDoom.java @@ -17,7 +17,7 @@ import mage.constants.*; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/g/GiftOfEstates.java b/Mage.Sets/src/mage/cards/g/GiftOfEstates.java index c96dd58a1699..429110791599 100644 --- a/Mage.Sets/src/mage/cards/g/GiftOfEstates.java +++ b/Mage.Sets/src/mage/cards/g/GiftOfEstates.java @@ -1,7 +1,5 @@ - package mage.cards.g; -import java.util.UUID; import mage.abilities.condition.common.OpponentControlsMoreCondition; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; @@ -13,27 +11,30 @@ import mage.filter.StaticFilters; import mage.target.common.TargetCardInLibrary; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class GiftOfEstates extends CardImpl { - + private static final FilterCard filter = new FilterCard("Plains cards"); - + static { filter.add(SubType.PLAINS.getPredicate()); } - public GiftOfEstates(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{W}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{W}"); // If an opponent controls more lands than you, search your library for up to three Plains cards, reveal them, and put them into your hand. Then shuffle your library. this.getSpellAbility().addEffect(new ConditionalOneShotEffect( - new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 3, filter), true), - new OpponentControlsMoreCondition(StaticFilters.FILTER_LANDS))); + new SearchLibraryPutInHandEffect( + new TargetCardInLibrary(0, 3, filter), true + ), new OpponentControlsMoreCondition(StaticFilters.FILTER_LANDS), "if an opponent controls " + + "more lands than you, search your library for up to three Plains cards, " + + "reveal them, put them into your hand, then shuffle" + )); } private GiftOfEstates(final GiftOfEstates card) { diff --git a/Mage.Sets/src/mage/cards/g/GiftOfTheGargantuan.java b/Mage.Sets/src/mage/cards/g/GiftOfTheGargantuan.java index a017abb157d8..3060031d1bcc 100644 --- a/Mage.Sets/src/mage/cards/g/GiftOfTheGargantuan.java +++ b/Mage.Sets/src/mage/cards/g/GiftOfTheGargantuan.java @@ -1,21 +1,22 @@ - package mage.cards.g; -import java.util.UUID; import mage.abilities.Ability; +import mage.abilities.dynamicvalue.common.CardTypeAssignment; import mage.abilities.effects.OneShotEffect; import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.common.FilterCreatureCard; -import mage.filter.common.FilterLandCard; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; /** - * * @author North */ public final class GiftOfTheGargantuan extends CardImpl { @@ -39,12 +40,14 @@ public GiftOfTheGargantuan copy() { class GiftOfTheGargantuanEffect extends OneShotEffect { - public GiftOfTheGargantuanEffect() { + GiftOfTheGargantuanEffect() { super(Outcome.DrawCard); - this.staticText = "Look at the top four cards of your library. You may reveal a creature card and/or a land card from among them and put the revealed cards into your hand. Put the rest on the bottom of your library in any order"; + this.staticText = "Look at the top four cards of your library. You may reveal a creature card " + + "and/or a land card from among them and put the revealed cards into your hand. " + + "Put the rest on the bottom of your library in any order"; } - public GiftOfTheGargantuanEffect(final GiftOfTheGargantuanEffect effect) { + private GiftOfTheGargantuanEffect(final GiftOfTheGargantuanEffect effect) { super(effect); } @@ -60,32 +63,60 @@ public boolean apply(Game game, Ability source) { return false; } Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 4)); - player.lookAtCards(source, null, cards, game); - Cards revealedCards = new CardsImpl(); - TargetCard target = new TargetCard(Zone.LIBRARY, new FilterCreatureCard("creature card to reveal and put into your hand")); - if (target.canChoose(source.getSourceId(), source.getControllerId(), game) - && player.choose(Outcome.DrawCard, cards, target, game)) { - Card card = cards.get(target.getFirstTarget(), game); - if (card != null) { - cards.remove(card); - card.moveToZone(Zone.HAND, source, game, false); - revealedCards.add(card); - } + TargetCard target = new GiftOfTheGargantuanTarget(); + player.choose(outcome, cards, target, game); + Cards toHand = new CardsImpl(); + toHand.addAll(target.getTargets()); + player.revealCards(source, toHand, game); + player.moveCards(toHand, Zone.HAND, source, game); + cards.removeAll(toHand); + player.putCardsOnBottomOfLibrary(cards, game, source, true); + return true; + } +} + +class GiftOfTheGargantuanTarget extends TargetCardInLibrary { + + private static final FilterCard filter + = new FilterCard("a creature card and/or a land card"); + + static { + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + CardType.LAND.getPredicate() + )); + } + + private static final CardTypeAssignment cardTypeAssigner + = new CardTypeAssignment(CardType.CREATURE, CardType.LAND); + + GiftOfTheGargantuanTarget() { + super(0, 2, filter); + } + + private GiftOfTheGargantuanTarget(final GiftOfTheGargantuanTarget target) { + super(target); + } + + @Override + public GiftOfTheGargantuanTarget copy() { + return new GiftOfTheGargantuanTarget(this); + } + + @Override + public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) { + if (!super.canTarget(playerId, id, source, game)) { + return false; } - target = new TargetCard(Zone.LIBRARY, new FilterLandCard("land card to reveal and put into your hand")); - if (target.canChoose(source.getSourceId(), source.getControllerId(), game) - && player.choose(Outcome.DrawCard, cards, target, game)) { - Card card = cards.get(target.getFirstTarget(), game); - if (card != null) { - cards.remove(card); - card.moveToZone(Zone.HAND, source, game, false); - revealedCards.add(card); - } + Card card = game.getCard(id); + if (card == null) { + return false; } - if (!revealedCards.isEmpty()) { - player.revealCards(source, revealedCards, game); + if (this.getTargets().isEmpty()) { + return true; } - player.putCardsOnBottomOfLibrary(cards, game, source, true); - return true; + Cards cards = new CardsImpl(this.getTargets()); + cards.add(card); + return cardTypeAssigner.getRoleCount(cards, game) >= cards.size(); } } diff --git a/Mage.Sets/src/mage/cards/g/GiftsUngiven.java b/Mage.Sets/src/mage/cards/g/GiftsUngiven.java index 5c835f5e5071..34ef730d9f46 100644 --- a/Mage.Sets/src/mage/cards/g/GiftsUngiven.java +++ b/Mage.Sets/src/mage/cards/g/GiftsUngiven.java @@ -42,7 +42,7 @@ class GiftsUngivenEffect extends OneShotEffect { public GiftsUngivenEffect() { super(Outcome.DrawCard); - this.staticText = "Search your library for up to four cards with different names and reveal them. Target opponent chooses two of those cards. Put the chosen cards into your graveyard and the rest into your hand. Then shuffle your library"; + this.staticText = "Search your library for up to four cards with different names and reveal them. Target opponent chooses two of those cards. Put the chosen cards into your graveyard and the rest into your hand. Then shuffle"; } public GiftsUngivenEffect(final GiftsUngivenEffect effect) { diff --git a/Mage.Sets/src/mage/cards/g/Gigantiform.java b/Mage.Sets/src/mage/cards/g/Gigantiform.java index 692ac3bc4487..9aa5aa4f76a3 100644 --- a/Mage.Sets/src/mage/cards/g/Gigantiform.java +++ b/Mage.Sets/src/mage/cards/g/Gigantiform.java @@ -52,7 +52,7 @@ public Gigantiform(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new ConditionalInterveningIfTriggeredAbility( new EntersBattlefieldTriggeredAbility(new GigantiformEffect(), true), KickedCondition.instance, - "When {this} enters the battlefield, if it was kicked, you may search your library for a card named Gigantiform, put it onto the battlefield, then shuffle your library.")); + "When {this} enters the battlefield, if it was kicked, you may search your library for a card named Gigantiform, put it onto the battlefield, then shuffle.")); } private Gigantiform(final Gigantiform card) { @@ -84,7 +84,7 @@ public GigantiformAbility copy() { @Override public String getRule() { - return "Enchanted creature has base power and toughness 8/8 and has trample"; + return "Enchanted creature has base power and toughness 8/8 and has trample."; } } diff --git a/Mage.Sets/src/mage/cards/g/GilanraCallerOfWirewood.java b/Mage.Sets/src/mage/cards/g/GilanraCallerOfWirewood.java index 4f3bdc208cad..685cb7448f6c 100644 --- a/Mage.Sets/src/mage/cards/g/GilanraCallerOfWirewood.java +++ b/Mage.Sets/src/mage/cards/g/GilanraCallerOfWirewood.java @@ -18,6 +18,7 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.stack.Spell; +import mage.players.ManaPoolItem; import mage.players.Player; import java.util.UUID; @@ -81,19 +82,18 @@ public boolean checkTrigger(GameEvent event, Game game) { if (!getSourceId().equals(event.getSourceId())) { return false; } - Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(getSourceId()); + Permanent sourcePermanent = getSourcePermanentOrLKI(game); if (sourcePermanent == null || sourcePermanent .getAbilities(game) .stream() - .filter(GreenManaAbility.class::isInstance) .map(Ability::getOriginalId) .map(UUID::toString) .noneMatch(event.getData()::equals)) { return false; } Spell spell = game.getStack().getSpell(event.getTargetId()); - return spell != null && spell.getConvertedManaCost() >= 6; + return spell != null && spell.getManaValue() >= 6; } @Override @@ -101,19 +101,20 @@ public boolean isInactive(Game game) { if (super.isInactive(game)) { return true; } - // must remove effect on empty mana pool to fix accumulate bug - Player player = game.getPlayer(this.getControllerId()); - if (player == null) { - return true; - } - // if no mana in pool then it can be discarded - return player.getManaPool().getManaItems().stream().noneMatch(m -> m.getSourceId().equals(getSourceId())); + Player player = game.getPlayer(this.getControllerId()); + return player == null + || player + .getManaPool() + .getManaItems() + .stream() + .map(ManaPoolItem::getSourceId) + .noneMatch(getSourceId()::equals); } @Override public String getRule() { - return "When you spend this mana to cast a spell with converted mana cost 6 or greater, draw a card."; + return "When you spend this mana to cast a spell with mana value 6 or greater, draw a card."; } } diff --git a/Mage.Sets/src/mage/cards/g/GiltLeafAmbush.java b/Mage.Sets/src/mage/cards/g/GiltLeafAmbush.java index d63476df9c0f..a8dfa2fd1402 100644 --- a/Mage.Sets/src/mage/cards/g/GiltLeafAmbush.java +++ b/Mage.Sets/src/mage/cards/g/GiltLeafAmbush.java @@ -48,7 +48,7 @@ class GiltLeafAmbushCreateTokenEffect extends OneShotEffect { public GiltLeafAmbushCreateTokenEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "Create two 1/1 green Elf Warrior creature tokens into play. Clash with an opponent. If you win, those creatures gain deathtouch until end of turn"; + this.staticText = "Create two 1/1 green Elf Warrior creature tokens. Clash with an opponent. If you win, those creatures gain deathtouch until end of turn"; } public GiltLeafAmbushCreateTokenEffect(final GiltLeafAmbushCreateTokenEffect effect) { diff --git a/Mage.Sets/src/mage/cards/g/GingerbreadCabin.java b/Mage.Sets/src/mage/cards/g/GingerbreadCabin.java index a6d6295fbb64..0a9d37c861ad 100644 --- a/Mage.Sets/src/mage/cards/g/GingerbreadCabin.java +++ b/Mage.Sets/src/mage/cards/g/GingerbreadCabin.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.FoodToken; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/g/GiselaBladeOfGoldnight.java b/Mage.Sets/src/mage/cards/g/GiselaBladeOfGoldnight.java index c01ba19127ff..93dd84e4330c 100644 --- a/Mage.Sets/src/mage/cards/g/GiselaBladeOfGoldnight.java +++ b/Mage.Sets/src/mage/cards/g/GiselaBladeOfGoldnight.java @@ -72,9 +72,8 @@ public GiselaBladeOfGoldnightDoubleDamageEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { switch (event.getType()) { - case DAMAGE_CREATURE: case DAMAGE_PLAYER: - case DAMAGE_PLANESWALKER: + case DAMAGE_PERMANENT: return true; default: return false; diff --git a/Mage.Sets/src/mage/cards/g/GiverOfRunes.java b/Mage.Sets/src/mage/cards/g/GiverOfRunes.java index b7ba14508795..cffe27d9c0eb 100644 --- a/Mage.Sets/src/mage/cards/g/GiverOfRunes.java +++ b/Mage.Sets/src/mage/cards/g/GiverOfRunes.java @@ -18,7 +18,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.ColorlessPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetPermanent; diff --git a/Mage.Sets/src/mage/cards/g/GladehartCavalry.java b/Mage.Sets/src/mage/cards/g/GladehartCavalry.java index 007eb99798cc..5a4fcf035b1c 100644 --- a/Mage.Sets/src/mage/cards/g/GladehartCavalry.java +++ b/Mage.Sets/src/mage/cards/g/GladehartCavalry.java @@ -70,8 +70,8 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD - && ((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD) { + ZoneChangeEvent zoneChangeEvent = (ZoneChangeEvent) event; + if (zoneChangeEvent.isDiesEvent()) { Permanent permanent = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); if (permanent != null && permanent.isControlledBy(this.getControllerId()) diff --git a/Mage.Sets/src/mage/cards/g/GladewalkerRitualist.java b/Mage.Sets/src/mage/cards/g/GladewalkerRitualist.java index 15401e582e83..4dd49aa41f30 100644 --- a/Mage.Sets/src/mage/cards/g/GladewalkerRitualist.java +++ b/Mage.Sets/src/mage/cards/g/GladewalkerRitualist.java @@ -11,7 +11,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.NamePredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/g/Glarecaster.java b/Mage.Sets/src/mage/cards/g/Glarecaster.java index c3f53b4ea829..30cbef5c455e 100644 --- a/Mage.Sets/src/mage/cards/g/Glarecaster.java +++ b/Mage.Sets/src/mage/cards/g/Glarecaster.java @@ -87,7 +87,7 @@ public void init(Ability source, Game game) { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE || event.getType() == GameEvent.EventType.DAMAGE_PLAYER; + return event.getType() == EventType.DAMAGE_PERMANENT || event.getType() == GameEvent.EventType.DAMAGE_PLAYER; } @Override diff --git a/Mage.Sets/src/mage/cards/g/GlassCasket.java b/Mage.Sets/src/mage/cards/g/GlassCasket.java index a2267dcbb0f8..c857d8e8d802 100644 --- a/Mage.Sets/src/mage/cards/g/GlassCasket.java +++ b/Mage.Sets/src/mage/cards/g/GlassCasket.java @@ -11,7 +11,7 @@ import mage.constants.ComparisonType; import mage.filter.FilterPermanent; import mage.filter.common.FilterOpponentsCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetPermanent; import java.util.UUID; @@ -22,10 +22,10 @@ public final class GlassCasket extends CardImpl { private static final FilterPermanent filter - = new FilterOpponentsCreaturePermanent("creature an opponent controls with converted mana cost 3 or less"); + = new FilterOpponentsCreaturePermanent("creature an opponent controls with mana value 3 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public GlassCasket(UUID ownerId, CardSetInfo setInfo) { @@ -34,7 +34,7 @@ public GlassCasket(UUID ownerId, CardSetInfo setInfo) { // When Glass Casket enters the battlefield, exile target creature an opponent controls with converted mana cost 3 or less until Glass Casket leaves the battlefield. Ability ability = new EntersBattlefieldTriggeredAbility( new ExileUntilSourceLeavesEffect("") - .setText("exile target creature an opponent controls with converted mana cost 3 " + + .setText("exile target creature an opponent controls with mana value 3 " + "or less until {this} leaves the battlefield") ); ability.addTarget(new TargetPermanent(filter)); diff --git a/Mage.Sets/src/mage/cards/g/GlassdustHulk.java b/Mage.Sets/src/mage/cards/g/GlassdustHulk.java index b962ceb23ef7..543f43fd2511 100644 --- a/Mage.Sets/src/mage/cards/g/GlassdustHulk.java +++ b/Mage.Sets/src/mage/cards/g/GlassdustHulk.java @@ -17,7 +17,7 @@ import mage.constants.TargetController; import mage.filter.FilterPermanent; import mage.filter.common.FilterArtifactPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/g/GlazeFiend.java b/Mage.Sets/src/mage/cards/g/GlazeFiend.java index 6f8ce0204e37..9dc45be74943 100644 --- a/Mage.Sets/src/mage/cards/g/GlazeFiend.java +++ b/Mage.Sets/src/mage/cards/g/GlazeFiend.java @@ -13,7 +13,7 @@ import mage.constants.Duration; import mage.constants.TargetController; import mage.filter.common.FilterArtifactPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/g/Gleancrawler.java b/Mage.Sets/src/mage/cards/g/Gleancrawler.java index a5223fafd623..0bd1536904c3 100644 --- a/Mage.Sets/src/mage/cards/g/Gleancrawler.java +++ b/Mage.Sets/src/mage/cards/g/Gleancrawler.java @@ -1,34 +1,37 @@ - package mage.cards.g; -import java.util.Set; -import java.util.UUID; import mage.MageInt; -import mage.MageObjectReference; -import mage.abilities.Ability; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.common.ReturnToHandFromGraveyardAllEffect; import mage.abilities.keyword.TrampleAbility; -import mage.cards.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Outcome; import mage.constants.TargetController; import mage.constants.Zone; -import mage.game.Game; -import mage.players.Player; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.card.PutIntoGraveFromBattlefieldThisTurnPredicate; import mage.watchers.common.CardsPutIntoGraveyardWatcher; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class Gleancrawler extends CardImpl { + private static final FilterCard filter = new FilterCreatureCard(); + + static { + filter.add(PutIntoGraveFromBattlefieldThisTurnPredicate.instance); + } + public Gleancrawler(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B/G}{B/G}{B/G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B/G}{B/G}{B/G}"); this.subtype.add(SubType.INSECT); this.subtype.add(SubType.HORROR); this.power = new MageInt(6); @@ -36,11 +39,17 @@ public Gleancrawler(UUID ownerId, CardSetInfo setInfo) { // ({B/G} can be paid with either {B} or {G}.) this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("({B/G} can be paid with either {B} or {G}.)"))); + // Trample this.addAbility(TrampleAbility.getInstance()); - // At the beginning of your end step, return to your hand all creature cards in your graveyard that were put there from the battlefield this turn. - this.addAbility(new BeginningOfEndStepTriggeredAbility(new GleancrawlerEffect(), TargetController.YOU, false), new CardsPutIntoGraveyardWatcher()); + // At the beginning of your end step, return to your hand all creature cards in your graveyard that were put there from the battlefield this turn. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + new ReturnToHandFromGraveyardAllEffect(filter, TargetController.YOU) + .setText("return to your hand all creature cards in your graveyard " + + "that were put there from the battlefield this turn"), + TargetController.YOU, false + ), new CardsPutIntoGraveyardWatcher()); } private Gleancrawler(final Gleancrawler card) { @@ -52,44 +61,3 @@ public Gleancrawler copy() { return new Gleancrawler(this); } } - -class GleancrawlerEffect extends OneShotEffect { - - boolean applied = false; - - public GleancrawlerEffect() { - super(Outcome.ReturnToHand); - this.staticText = "return to your hand all creature cards in your graveyard that were put there from the battlefield this turn"; - } - - public GleancrawlerEffect(final GleancrawlerEffect effect) { - super(effect); - } - - @Override - public GleancrawlerEffect copy() { - return new GleancrawlerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - CardsPutIntoGraveyardWatcher watcher = game.getState().getWatcher(CardsPutIntoGraveyardWatcher.class); - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null && watcher != null) { - Set cardsToGraveyardThisTurn = watcher.getCardsPutToGraveyardFromBattlefield(); - Cards cardsToHand = new CardsImpl(); - for (MageObjectReference mor : cardsToGraveyardThisTurn) { - if (game.getState().getZoneChangeCounter(mor.getSourceId()) == mor.getZoneChangeCounter()) { - Card card = game.getCard(mor.getSourceId()); - if (card != null && card.isCreature() - && card.isOwnedBy(source.getControllerId())) { - cardsToHand.add(card); - } - } - } - controller.moveCards(cardsToHand, Zone.HAND, source, game); - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/g/GlimmerpointStag.java b/Mage.Sets/src/mage/cards/g/GlimmerpointStag.java index 7411d47583c6..412f122b9c08 100644 --- a/Mage.Sets/src/mage/cards/g/GlimmerpointStag.java +++ b/Mage.Sets/src/mage/cards/g/GlimmerpointStag.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/g/GlintHawk.java b/Mage.Sets/src/mage/cards/g/GlintHawk.java index 7d6ab51bcfa4..9ed600102919 100644 --- a/Mage.Sets/src/mage/cards/g/GlintHawk.java +++ b/Mage.Sets/src/mage/cards/g/GlintHawk.java @@ -1,41 +1,40 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.common.FilterControlledPermanent; +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 maurer.it_at_gmail.com */ public final class GlintHawk extends CardImpl { public GlintHawk(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); this.subtype.add(SubType.BIRD); this.power = new MageInt(2); this.toughness = new MageInt(2); - - // When Glint Hawk enters the battlefield, sacrifice it unless you return an artifact you control to its owner's hand. + // Flying this.addAbility(FlyingAbility.getInstance()); + + // When Glint Hawk enters the battlefield, sacrifice it unless you return an artifact you control to its owner's hand. this.addAbility(new EntersBattlefieldTriggeredAbility(new GlintHawkEffect())); } @@ -51,49 +50,40 @@ public GlintHawk copy() { class GlintHawkEffect extends OneShotEffect { - private static final FilterControlledPermanent filter; private static final String effectText = "sacrifice it unless you return an artifact you control to its owner's hand"; - static { - filter = new FilterControlledPermanent(); - filter.add(CardType.ARTIFACT.getPredicate()); - } - - GlintHawkEffect ( ) { + GlintHawkEffect() { super(Outcome.Sacrifice); staticText = effectText; } - GlintHawkEffect ( GlintHawkEffect effect ) { + private GlintHawkEffect(final GlintHawkEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - boolean targetChosen = false; - TargetPermanent target = new TargetPermanent(1, 1, filter, true); - if (target.canChoose(source.getSourceId(), controller.getId(), game) && controller.chooseUse(outcome, "Return an artifact you control to its owner's hand?", source, game)) { - controller.chooseTarget(Outcome.Sacrifice, target, source, game); - Permanent permanent = game.getPermanent(target.getFirstTarget()); - if (permanent != null) { - targetChosen = true; - permanent.moveToZone(Zone.HAND, source, game, false); - } - } - - if (!targetChosen) { - new SacrificeSourceEffect().apply(game, source); + if (controller == null) { + return false; + } + TargetPermanent target = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT); + target.setNotTarget(true); + if (target.canChoose(source.getSourceId(), controller.getId(), game) + && controller.chooseUse(outcome, "Return an artifact you control to its owner's hand?", source, game)) { + controller.chooseTarget(Outcome.ReturnToHand, target, source, game); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent != null) { + controller.moveCards(permanent, Zone.HAND, source, game); + return true; } - return true; } - return false; + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + return permanent != null && permanent.sacrifice(source, game); } @Override public GlintHawkEffect copy() { return new GlintHawkEffect(this); } - -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/g/GlintHawkIdol.java b/Mage.Sets/src/mage/cards/g/GlintHawkIdol.java index b7fc79084a45..28f124f16712 100644 --- a/Mage.Sets/src/mage/cards/g/GlintHawkIdol.java +++ b/Mage.Sets/src/mage/cards/g/GlintHawkIdol.java @@ -14,7 +14,7 @@ import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterArtifactPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.TokenImpl; /** diff --git a/Mage.Sets/src/mage/cards/g/GlissaSunseeker.java b/Mage.Sets/src/mage/cards/g/GlissaSunseeker.java index 702c38114c7e..501d594d07a4 100644 --- a/Mage.Sets/src/mage/cards/g/GlissaSunseeker.java +++ b/Mage.Sets/src/mage/cards/g/GlissaSunseeker.java @@ -57,7 +57,7 @@ class GlissaSunseekerEffect extends OneShotEffect { public GlissaSunseekerEffect() { super(Outcome.DestroyPermanent); - this.staticText = "Destroy target artifact if its converted mana cost is equal to the amount of unspent mana you have"; + this.staticText = "Destroy target artifact if its mana value is equal to the amount of unspent mana you have"; } public GlissaSunseekerEffect(final GlissaSunseekerEffect effect) { @@ -84,7 +84,7 @@ public boolean apply(Game game, Ability source) { int redMana = pool.getRed(); int colorlessMana = pool.getColorless(); int manaPoolTotal = blackMana + whiteMana + blueMana + greenMana + redMana + colorlessMana; - if (permanent.getConvertedManaCost() == manaPoolTotal) { + if (permanent.getManaValue() == manaPoolTotal) { return permanent.destroy(source, game, false); } return false; diff --git a/Mage.Sets/src/mage/cards/g/GlitteringWish.java b/Mage.Sets/src/mage/cards/g/GlitteringWish.java index 988147a25368..9a68a3525a09 100644 --- a/Mage.Sets/src/mage/cards/g/GlitteringWish.java +++ b/Mage.Sets/src/mage/cards/g/GlitteringWish.java @@ -29,7 +29,7 @@ public GlitteringWish(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new WishEffect(filter)); // Exile Glittering Wish. - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private GlitteringWish(final GlitteringWish card) { diff --git a/Mage.Sets/src/mage/cards/g/GloomSurgeon.java b/Mage.Sets/src/mage/cards/g/GloomSurgeon.java index 0419ea6d5fc9..7b60e91a5779 100644 --- a/Mage.Sets/src/mage/cards/g/GloomSurgeon.java +++ b/Mage.Sets/src/mage/cards/g/GloomSurgeon.java @@ -67,13 +67,13 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT; } @Override public boolean applies(GameEvent event, Ability source, Game game) { if (event.getTargetId().equals(source.getSourceId())) { - DamageCreatureEvent damageEvent = (DamageCreatureEvent) event; + DamageEvent damageEvent = (DamageEvent) event; return damageEvent.isCombatDamage(); } return false; diff --git a/Mage.Sets/src/mage/cards/g/Glory.java b/Mage.Sets/src/mage/cards/g/Glory.java index 7e433cc65ad8..29504d962253 100644 --- a/Mage.Sets/src/mage/cards/g/Glory.java +++ b/Mage.Sets/src/mage/cards/g/Glory.java @@ -32,7 +32,7 @@ public Glory(UUID ownerId, CardSetInfo setInfo) { this.addAbility(FlyingAbility.getInstance()); // {2}{W}: Choose a color. Creatures you control gain protection from the chosen color until end of turn. Activate this ability only if Glory is in your graveyard. Effect effect = new GainProtectionFromColorAllEffect(Duration.EndOfTurn, new FilterControlledCreaturePermanent("Creatures you control")); - effect.setText("Choose a color. Creatures you control gain protection from the chosen color until end of turn. Activate this ability only if {this} is in your graveyard."); + effect.setText("Choose a color. Creatures you control gain protection from the chosen color until end of turn. Activate only if {this} is in your graveyard."); this.addAbility(new SimpleActivatedAbility(Zone.GRAVEYARD, effect, new ManaCostsImpl("{2}{W}"))); } diff --git a/Mage.Sets/src/mage/cards/g/GloryBearers.java b/Mage.Sets/src/mage/cards/g/GloryBearers.java index 6416ae90c6cc..9383c201c144 100644 --- a/Mage.Sets/src/mage/cards/g/GloryBearers.java +++ b/Mage.Sets/src/mage/cards/g/GloryBearers.java @@ -8,7 +8,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/g/GluttonousTroll.java b/Mage.Sets/src/mage/cards/g/GluttonousTroll.java index 6e9825b010f9..3770ec3942b8 100644 --- a/Mage.Sets/src/mage/cards/g/GluttonousTroll.java +++ b/Mage.Sets/src/mage/cards/g/GluttonousTroll.java @@ -17,7 +17,7 @@ import mage.constants.SubType; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.FoodToken; import mage.target.common.TargetControlledPermanent; diff --git a/Mage.Sets/src/mage/cards/g/GlyphOfLife.java b/Mage.Sets/src/mage/cards/g/GlyphOfLife.java index 94f927bbe705..7c91d7128d7c 100644 --- a/Mage.Sets/src/mage/cards/g/GlyphOfLife.java +++ b/Mage.Sets/src/mage/cards/g/GlyphOfLife.java @@ -15,9 +15,8 @@ import mage.constants.Outcome; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; +import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; -import mage.game.events.DamagedCreatureEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; @@ -70,13 +69,13 @@ public GlyphOfLifeTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getTargetId().equals(this.getFirstTarget())) { - DamagedCreatureEvent damageEvent = (DamagedCreatureEvent) event; + DamagedEvent damageEvent = (DamagedEvent) event; Permanent attackingCreature = game.getPermanentOrLKIBattlefield(damageEvent.getSourceId()); if (attackingCreature != null && attackingCreature.isCreature() && attackingCreature.isAttacking()) { this.getEffects().get(0).setValue("damageAmount", event.getAmount()); diff --git a/Mage.Sets/src/mage/cards/g/GlyphOfReincarnation.java b/Mage.Sets/src/mage/cards/g/GlyphOfReincarnation.java index 41be4d22924b..96e569be88dd 100644 --- a/Mage.Sets/src/mage/cards/g/GlyphOfReincarnation.java +++ b/Mage.Sets/src/mage/cards/g/GlyphOfReincarnation.java @@ -15,7 +15,7 @@ import mage.filter.StaticFilters; import mage.filter.common.FilterCreatureCard; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/g/GnarledProfessor.java b/Mage.Sets/src/mage/cards/g/GnarledProfessor.java new file mode 100644 index 000000000000..7d17ce9c005d --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GnarledProfessor.java @@ -0,0 +1,42 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.LearnEffect; +import mage.abilities.keyword.TrampleAbility; +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 GnarledProfessor extends CardImpl { + + public GnarledProfessor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}"); + + this.subtype.add(SubType.TREEFOLK); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(5); + this.toughness = new MageInt(4); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // When Gnarled Professor enters the battlefield, learn. + this.addAbility(new EntersBattlefieldTriggeredAbility(new LearnEffect())); + } + + private GnarledProfessor(final GnarledProfessor card) { + super(card); + } + + @Override + public GnarledProfessor copy() { + return new GnarledProfessor(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GoBlank.java b/Mage.Sets/src/mage/cards/g/GoBlank.java new file mode 100644 index 000000000000..f62dcc7eceeb --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GoBlank.java @@ -0,0 +1,35 @@ +package mage.cards.g; + +import mage.abilities.effects.common.ExileGraveyardAllTargetPlayerEffect; +import mage.abilities.effects.common.discard.DiscardTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.TargetPlayer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GoBlank extends CardImpl { + + public GoBlank(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}"); + + // Target player discards two cards. Then exile all cards from that player's graveyard. + this.getSpellAbility().addEffect(new DiscardTargetEffect(2)); + this.getSpellAbility().addEffect(new ExileGraveyardAllTargetPlayerEffect() + .setText("Then exile all cards from that player's graveyard")); + this.getSpellAbility().addTarget(new TargetPlayer()); + } + + private GoBlank(final GoBlank card) { + super(card); + } + + @Override + public GoBlank copy() { + return new GoBlank(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GoblinArchaeologist.java b/Mage.Sets/src/mage/cards/g/GoblinArchaeologist.java index d2cd0296c220..ae07c81c85bb 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinArchaeologist.java +++ b/Mage.Sets/src/mage/cards/g/GoblinArchaeologist.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.cards.g; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/g/GoblinArtisans.java b/Mage.Sets/src/mage/cards/g/GoblinArtisans.java index aa2a7ae2b802..d675fafe7cfe 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinArtisans.java +++ b/Mage.Sets/src/mage/cards/g/GoblinArtisans.java @@ -1,50 +1,49 @@ - package mage.cards.g; -import java.util.List; -import java.util.UUID; import mage.MageInt; +import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.Mode; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.FlipCoinEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledArtifactPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.NamePredicate; -import mage.filter.predicate.permanent.PermanentIdPredicate; +import mage.constants.TargetController; +import mage.filter.FilterSpell; +import mage.filter.common.FilterArtifactSpell; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.game.stack.Spell; +import mage.game.stack.StackAbility; +import mage.game.stack.StackObject; import mage.target.Target; -import mage.target.TargetCard; +import mage.target.TargetSpell; + +import java.util.Collection; +import java.util.UUID; /** - * - * @author MarcoMarin + * @author TheElk801 */ public final class GoblinArtisans extends CardImpl { public GoblinArtisans(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}"); this.subtype.add(SubType.GOBLIN); this.subtype.add(SubType.ARTIFICER); this.power = new MageInt(1); this.toughness = new MageInt(1); // {tap}: Flip a coin. If you win the flip, draw a card. If you lose the flip, counter target artifact spell you control that isn't the target of an ability from another creature named Goblin Artisans. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GoblinArtisansEffect(), new TapSourceCost())); - + Ability ability = new SimpleActivatedAbility(new FlipCoinEffect( + new DrawCardSourceControllerEffect(1), new CounterTargetEffect() + ), new TapSourceCost()); + ability.addTarget(new GoblinArtisansTarget()); + this.addAbility(ability); } private GoblinArtisans(final GoblinArtisans card) { @@ -57,68 +56,59 @@ public GoblinArtisans copy() { } } -class GoblinArtisansEffect extends OneShotEffect { +class GoblinArtisansTarget extends TargetSpell { - private static final FilterPermanent filter = new FilterPermanent("permanent named Goblin Artisans"); + private static final FilterSpell filter = new FilterArtifactSpell( + "artifact spell you control that isn't the target " + + "of an ability from another creature named Goblin Artisans" + ); static { - filter.add(new NamePredicate("Goblin Artisans")); + filter.add(TargetController.YOU.getOwnerPredicate()); } - public GoblinArtisansEffect() { - super(Outcome.Damage); - staticText = "Flip a coin. If you win the flip, draw a card. If you lose the flip, counter target artifact spell you control that isn't the target of an ability from another creature named Goblin Artisans."; + GoblinArtisansTarget() { + super(filter); } - public GoblinArtisansEffect(GoblinArtisansEffect effect) { - super(effect); + private GoblinArtisansTarget(final GoblinArtisansTarget target) { + super(target); } @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - if (controller.flipCoin(source, game, true)) { - controller.drawCards(1, source, game); - } else { - List artifacts = game.getBattlefield().getActivePermanents(new FilterControlledArtifactPermanent(), source.getControllerId(), game); - if (artifacts.isEmpty()) {//Don't even bother if there is no artifact to 'counter'/sacrifice - return true; - } - - filter.add(Predicates.not(new PermanentIdPredicate(source.getSourceId()))); - //removed the activating instance of Artisans, btw, wasn't that filter declared as static final? How come I can do this here? :) - List list = game.getBattlefield().getAllActivePermanents(filter, game); - for (Permanent perm : list) { // should I limit below for a particular kind of ability? Going for the most general, it's unlikely there'll be any other artisans anyway, so not concerned about efficiency :p - for (Ability abil : perm.getAbilities(game)) {//below is copied from TargetsPermanentPredicate, but why only "selectedModes"? Shouldnt be more general as well? - for (UUID modeId : abil.getModes().getSelectedModes()) { - Mode mode = abil.getModes().get(modeId); - for (Target target : mode.getTargets()) { - for (UUID targetId : target.getTargets()) { - artifacts.remove(game.getPermanentOrLKIBattlefield(targetId)); - }// we could - }// remove this - }//closing bracers - }// pyramid, if it's bothering anyone - } //they are all one-liners after all :) - if (!artifacts.isEmpty()) { - Cards cards = new CardsImpl(); - for (Permanent perm : artifacts) { - cards.add(perm.getId()); - } - TargetCard target = new TargetCard(Zone.BATTLEFIELD, new FilterCard()); - controller.choose(Outcome.Sacrifice, cards, target, game); - game.getPermanent(target.getFirstTarget()).sacrifice(source, game); - } - return true; - } - } - - return false; + public GoblinArtisansTarget copy() { + return new GoblinArtisansTarget(this); } @Override - public GoblinArtisansEffect copy() { - return new GoblinArtisansEffect(this); + public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) { + if (!super.canTarget(controllerId, id, source, game)) { + return false; + } + MageObjectReference sourceRef = new MageObjectReference(source.getSourceObject(game), game); + Spell spell = game.getSpell(id); + if (spell == null) { + return false; + } + for (StackObject stackObject : game.getStack()) { + if (!(stackObject instanceof StackAbility)) { + continue; + } + Permanent permanent = ((StackAbility) stackObject).getSourcePermanentOrLKI(game); + if (permanent != null + && !sourceRef.refersTo(permanent, game) + && permanent.isCreature() + && "Goblin Artisans".equals(permanent.getName()) + && stackObject + .getStackAbility() + .getTargets() + .stream() + .map(Target::getTargets) + .flatMap(Collection::stream) + .anyMatch(id::equals)) { + return false; + } + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/g/GoblinBowlingTeam.java b/Mage.Sets/src/mage/cards/g/GoblinBowlingTeam.java index cadb994f2930..bd0a9c5041b7 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinBowlingTeam.java +++ b/Mage.Sets/src/mage/cards/g/GoblinBowlingTeam.java @@ -66,9 +66,8 @@ public GoblinBowlingTeamEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { switch (event.getType()) { - case DAMAGE_CREATURE: case DAMAGE_PLAYER: - case DAMAGE_PLANESWALKER: + case DAMAGE_PERMANENT: return true; default: return false; diff --git a/Mage.Sets/src/mage/cards/g/GoblinDarkDwellers.java b/Mage.Sets/src/mage/cards/g/GoblinDarkDwellers.java index e68cc7bd5eef..6c0642398b0f 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinDarkDwellers.java +++ b/Mage.Sets/src/mage/cards/g/GoblinDarkDwellers.java @@ -19,7 +19,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterInstantOrSorceryCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; @@ -34,10 +34,10 @@ public final class GoblinDarkDwellers extends CardImpl { private static final FilterInstantOrSorceryCard filter - = new FilterInstantOrSorceryCard("instant or sorcery card with converted mana cost 3 or less"); + = new FilterInstantOrSorceryCard("instant or sorcery card with mana value 3 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public GoblinDarkDwellers(UUID ownerId, CardSetInfo setInfo) { @@ -71,7 +71,7 @@ class GoblinDarkDwellersEffect extends OneShotEffect { GoblinDarkDwellersEffect() { super(Outcome.PlayForFree); this.staticText = "you may cast target instant or sorcery card with " - + "converted mana cost 3 or less from your graveyard without paying its mana cost. " + + "mana value 3 or less from your graveyard without paying its mana cost. " + "If that card would be put into your graveyard this turn, exile it instead"; } diff --git a/Mage.Sets/src/mage/cards/g/GoblinEngineer.java b/Mage.Sets/src/mage/cards/g/GoblinEngineer.java index 5ba01844c45b..c17f3450b660 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinEngineer.java +++ b/Mage.Sets/src/mage/cards/g/GoblinEngineer.java @@ -17,7 +17,7 @@ import mage.filter.common.FilterArtifactCard; import mage.filter.common.FilterControlledArtifactPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; @@ -34,10 +34,10 @@ public final class GoblinEngineer extends CardImpl { private static final FilterControlledPermanent filter = new FilterControlledArtifactPermanent("an artifact"); private static final FilterCard filter2 - = new FilterArtifactCard("artifact card with converted mana cost 3 or less from your graveyard"); + = new FilterArtifactCard("artifact card with mana value 3 or less from your graveyard"); static { - filter2.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter2.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public GoblinEngineer(UUID ownerId, CardSetInfo setInfo) { @@ -75,7 +75,7 @@ class GoblinEngineerEffect extends SearchEffect { GoblinEngineerEffect() { super(new TargetCardInLibrary(StaticFilters.FILTER_CARD_ARTIFACT_AN), Outcome.Neutral); - staticText = "search your library for an artifact card, put it into your graveyard, then shuffle your library"; + staticText = "search your library for an artifact card, put it into your graveyard, then shuffle"; } private GoblinEngineerEffect(final GoblinEngineerEffect effect) { diff --git a/Mage.Sets/src/mage/cards/g/GoblinFirestarter.java b/Mage.Sets/src/mage/cards/g/GoblinFirestarter.java index 292e339f93d7..92a7ac76a7ed 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinFirestarter.java +++ b/Mage.Sets/src/mage/cards/g/GoblinFirestarter.java @@ -29,7 +29,7 @@ public GoblinFirestarter(UUID ownerId, CardSetInfo setInfo) { // Sacrifice Goblin Firestarter: Goblin Firestarter deals 1 damage to any target. Activate this ability only during your turn, before attackers are declared. Ability ability = new ActivateIfConditionActivatedAbility(Zone.BATTLEFIELD, - new DamageTargetEffect(1), new SacrificeSourceCost(), MyTurnBeforeAttackersDeclaredCondition.instance); + new DamageTargetEffect(1,"it"), new SacrificeSourceCost(), MyTurnBeforeAttackersDeclaredCondition.instance); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GoblinGangLeader.java b/Mage.Sets/src/mage/cards/g/GoblinGangLeader.java index 7f8bef0787cb..3b80682219db 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinGangLeader.java +++ b/Mage.Sets/src/mage/cards/g/GoblinGangLeader.java @@ -25,7 +25,7 @@ public GoblinGangLeader(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(2); // When Goblin Gang Leader enters the battlefield, create two 1/1 red Goblin creature tokens. - this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GoblinToken("ANA"), 2))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GoblinToken(), 2))); } private GoblinGangLeader(final GoblinGangLeader card) { diff --git a/Mage.Sets/src/mage/cards/g/GoblinMachinist.java b/Mage.Sets/src/mage/cards/g/GoblinMachinist.java index 4b7c47053cfe..33dc8bea4a60 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinMachinist.java +++ b/Mage.Sets/src/mage/cards/g/GoblinMachinist.java @@ -50,7 +50,7 @@ class GoblinMachinistEffect extends OneShotEffect { public GoblinMachinistEffect() { super(Outcome.DrawCard); - this.staticText = "Reveal cards from the top of your library until you reveal a nonland card. {this} gets +X/+0 until end of turn, where X is that card's converted mana cost. Put the revealed cards on the bottom of your library in any order"; + this.staticText = "Reveal cards from the top of your library until you reveal a nonland card. {this} gets +X/+0 until end of turn, where X is that card's mana value. Put the revealed cards on the bottom of your library in any order"; } public GoblinMachinistEffect(final GoblinMachinistEffect effect) { @@ -71,8 +71,8 @@ public boolean apply(Game game, Ability source) { if (card != null) { cards.add(card); if (!card.isLand()) { - if (card.getConvertedManaCost() > 0) { - game.addEffect(new BoostSourceEffect(card.getConvertedManaCost(), 0, Duration.EndOfTurn), source); + if (card.getManaValue() > 0) { + game.addEffect(new BoostSourceEffect(card.getManaValue(), 0, Duration.EndOfTurn), source); } break; } diff --git a/Mage.Sets/src/mage/cards/g/GoblinPiledriver.java b/Mage.Sets/src/mage/cards/g/GoblinPiledriver.java index 88fddaa829f4..a6aff231e732 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinPiledriver.java +++ b/Mage.Sets/src/mage/cards/g/GoblinPiledriver.java @@ -15,7 +15,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.common.FilterAttackingCreature; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/g/GoblinPsychopath.java b/Mage.Sets/src/mage/cards/g/GoblinPsychopath.java index 0fc141c9eb46..d143fc2df330 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinPsychopath.java +++ b/Mage.Sets/src/mage/cards/g/GoblinPsychopath.java @@ -70,8 +70,7 @@ public GoblinPsychopathEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE - || event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT || event.getType() == GameEvent.EventType.DAMAGE_PLAYER; } diff --git a/Mage.Sets/src/mage/cards/g/GoblinRabblemaster.java b/Mage.Sets/src/mage/cards/g/GoblinRabblemaster.java index 8e7842b95811..77c078fae51f 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinRabblemaster.java +++ b/Mage.Sets/src/mage/cards/g/GoblinRabblemaster.java @@ -16,7 +16,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.AttackingPredicate; import mage.game.permanent.token.GoblinToken; import mage.watchers.common.AttackedThisTurnWatcher; diff --git a/Mage.Sets/src/mage/cards/g/GoblinSmuggler.java b/Mage.Sets/src/mage/cards/g/GoblinSmuggler.java index 4e5edd90078a..2e2de0c11a32 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinSmuggler.java +++ b/Mage.Sets/src/mage/cards/g/GoblinSmuggler.java @@ -15,7 +15,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/g/GoblinTinkerer.java b/Mage.Sets/src/mage/cards/g/GoblinTinkerer.java index 8541f5816cc1..1658419229c8 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinTinkerer.java +++ b/Mage.Sets/src/mage/cards/g/GoblinTinkerer.java @@ -56,7 +56,7 @@ class GoblinTinkererDamageEffect extends OneShotEffect { public GoblinTinkererDamageEffect() { super(Outcome.Detriment); - this.staticText = "That artifact deals damage equal to its converted mana cost to {this}"; + this.staticText = "That artifact deals damage equal to its mana value to {this}"; } public GoblinTinkererDamageEffect(final GoblinTinkererDamageEffect effect) { @@ -74,7 +74,7 @@ public boolean apply(Game game, Ability source) { Permanent targetArtifact = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (controller != null && targetArtifact != null) { Permanent sourceObject = game.getPermanent(source.getSourceId()); - int damage = targetArtifact.getConvertedManaCost(); + int damage = targetArtifact.getManaValue(); if (sourceObject != null && damage > 0) { sourceObject.damage(damage, targetArtifact.getId(), source, game, false, true); } diff --git a/Mage.Sets/src/mage/cards/g/GoblinTutor.java b/Mage.Sets/src/mage/cards/g/GoblinTutor.java index e1d71b17baa0..8cb65ae01915 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinTutor.java +++ b/Mage.Sets/src/mage/cards/g/GoblinTutor.java @@ -50,7 +50,7 @@ class GoblinTutorEffect extends OneShotEffect { public GoblinTutorEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "Roll a six-sided die. If you roll a 1, {this} has no effect. Otherwise, search your library for the indicated card, reveal it, put it into your hand, then shuffle your library. 2 - A card named Goblin Tutor 3 - An enchantment card 4 - An artifact card 5 - A creature card 6 - An instant or sorcery card"; + this.staticText = "Roll a six-sided die. If you roll a 1, {this} has no effect. Otherwise, search your library for the indicated card, reveal it, put it into your hand, then shuffle. 2 - A card named Goblin Tutor 3 - An enchantment card 4 - An artifact card 5 - A creature card 6 - An instant or sorcery card"; } public GoblinTutorEffect(final GoblinTutorEffect effect) { diff --git a/Mage.Sets/src/mage/cards/g/GodEternalBontu.java b/Mage.Sets/src/mage/cards/g/GodEternalBontu.java index e34a96cad2ba..2244e8db4d0c 100644 --- a/Mage.Sets/src/mage/cards/g/GodEternalBontu.java +++ b/Mage.Sets/src/mage/cards/g/GodEternalBontu.java @@ -14,7 +14,7 @@ import mage.constants.SuperType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/g/GodEternalKefnet.java b/Mage.Sets/src/mage/cards/g/GodEternalKefnet.java index a4fc79e72e6f..9f7b37a5dd90 100644 --- a/Mage.Sets/src/mage/cards/g/GodEternalKefnet.java +++ b/Mage.Sets/src/mage/cards/g/GodEternalKefnet.java @@ -90,9 +90,8 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { you.setTopCardRevealed(true); // cast copy - if (topCard.isInstantOrSorcery() - && you.chooseUse(outcome, "Would you like to copy " + topCard.getName() + " and cast it for {2} less?", source, game)) { + && you.chooseUse(outcome, "Copy " + topCard.getName() + " and cast it for {2} less?", source, game)) { Card blueprint = topCard.copy(); if (blueprint instanceof SplitCard) { ((SplitCard) blueprint).getLeftHalfCard().addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(2))); @@ -105,7 +104,9 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { } Card copiedCard = game.copyCard(blueprint, source, source.getControllerId()); you.moveCardToHandWithInfo(copiedCard, source, game, true); // The copy is created in and cast from your hand. (2019-05-03) + game.getState().setValue("PlayFromNotOwnHandZone" + copiedCard.getId(), Boolean.TRUE); you.cast(you.chooseAbilityForCast(copiedCard, game, false), game, false, new ApprovingObject(source, game)); + game.getState().setValue("PlayFromNotOwnHandZone" + copiedCard.getId(), null); } // draw (return false for default draw) @@ -156,7 +157,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { String mes = topCard.getName() + ", " + (topCard.isInstantOrSorcery() ? HintUtils.prepareText("you can copy it and cast {2} less", Color.green) : HintUtils.prepareText("you can't copy it", Color.red)); - return you.chooseUse(Outcome.Benefit, "Would you like to reveal first drawn card (" + mes + ")?", source, game); + return you.chooseUse(Outcome.Benefit, "Reveal first drawn card (" + mes + ")?", source, game); } } diff --git a/Mage.Sets/src/mage/cards/g/GodPharaohsGift.java b/Mage.Sets/src/mage/cards/g/GodPharaohsGift.java index 84fde58cee85..70677d46ea66 100644 --- a/Mage.Sets/src/mage/cards/g/GodPharaohsGift.java +++ b/Mage.Sets/src/mage/cards/g/GodPharaohsGift.java @@ -4,7 +4,6 @@ import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.BeginningOfCombatTriggeredAbility; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.HasteAbility; @@ -18,10 +17,13 @@ import mage.game.permanent.token.EmptyToken; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; -import mage.target.targetpointer.FixedTarget; +import mage.target.targetpointer.FixedTargets; import mage.util.CardUtil; +import java.util.List; +import java.util.Objects; import java.util.UUID; +import java.util.stream.Collectors; /** * @author jeffwadsworth @@ -32,8 +34,9 @@ public GodPharaohsGift(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{7}"); // At the beginning of combat on your turn, you may exile a creature card from your graveyard. If you do, create a token that's a copy of that card, except it's a 4/4 black Zombie. It gains haste until end of turn. - this.addAbility(new BeginningOfCombatTriggeredAbility(Zone.BATTLEFIELD, new GodPharaohsGiftEffect(), TargetController.YOU, true, false)); - + this.addAbility(new BeginningOfCombatTriggeredAbility( + new GodPharaohsGiftEffect(), TargetController.YOU, false + )); } private GodPharaohsGift(final GodPharaohsGift card) { @@ -48,14 +51,14 @@ public GodPharaohsGift copy() { class GodPharaohsGiftEffect extends OneShotEffect { - private final UUID exileId = UUID.randomUUID(); - - public GodPharaohsGiftEffect() { + GodPharaohsGiftEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "you may exile a creature card from your graveyard. If you do, create a token that's a copy of that card, except it's a 4/4 black Zombie. It gains haste until end of turn"; + this.staticText = "you may exile a creature card from your graveyard. " + + "If you do, create a token that's a copy of that card, " + + "except it's a 4/4 black Zombie. It gains haste until end of turn"; } - public GodPharaohsGiftEffect(final GodPharaohsGiftEffect effect) { + private GodPharaohsGiftEffect(final GodPharaohsGiftEffect effect) { super(effect); } @@ -68,35 +71,38 @@ public GodPharaohsGiftEffect copy() { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = source.getSourceObject(game); - if (controller != null && sourceObject != null) { - TargetCardInYourGraveyard target = new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD); - target.setNotTarget(true); - if (!controller.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD, game).isEmpty() - && controller.choose(Outcome.PutCreatureInPlay, target, source.getId(), game)) { - Card cardChosen = game.getCard(target.getFirstTarget()); - if (cardChosen != null - && cardChosen.moveToExile(exileId, sourceObject.getIdName(), source, game)) { - // create token and modify all attributes permanently (without game usage) - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(cardChosen, game); - token.removePTCDA(); - token.getPower().modifyBaseValue(4); - token.getToughness().modifyBaseValue(4); - token.getColor().setColor(ObjectColor.BLACK); - token.removeAllCreatureTypes(); - token.addSubType(SubType.ZOMBIE); - if (token.putOntoBattlefield(1, game, source, source.getControllerId())) { - Permanent tokenPermanent = game.getPermanent(token.getLastAddedToken()); - if (tokenPermanent != null) { - ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(tokenPermanent.getId())); - game.addEffect(effect, source); - } - } - } - } - return true; + if (controller == null || sourceObject == null) { + return false; + } + TargetCardInYourGraveyard target = new TargetCardInYourGraveyard( + 0, 1, StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD, true + ); + controller.choose(Outcome.PutCreatureInPlay, target, source.getSourceId(), game); + Card cardChosen = game.getCard(target.getFirstTarget()); + if (cardChosen == null || !controller.moveCards(cardChosen, Zone.EXILED, source, game)) { + return false; + } + // create token and modify all attributes permanently (without game usage) + EmptyToken token = new EmptyToken(); + CardUtil.copyTo(token).from(cardChosen, game); + token.removePTCDA(); + token.getPower().modifyBaseValue(4); + token.getToughness().modifyBaseValue(4); + token.getColor().setColor(ObjectColor.BLACK); + token.removeAllCreatureTypes(); + token.addSubType(SubType.ZOMBIE); + token.putOntoBattlefield(1, game, source, source.getControllerId()); + List permanents = token + .getLastAddedTokenIds() + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + if (!permanents.isEmpty()) { + game.addEffect(new GainAbilityTargetEffect( + HasteAbility.getInstance(), Duration.EndOfTurn + ).setTargetPointer(new FixedTargets(permanents, game)), source); } - return false; + return true; } } diff --git a/Mage.Sets/src/mage/cards/g/GodheadOfAwe.java b/Mage.Sets/src/mage/cards/g/GodheadOfAwe.java index b0f19907724c..6ee4263f9fad 100644 --- a/Mage.Sets/src/mage/cards/g/GodheadOfAwe.java +++ b/Mage.Sets/src/mage/cards/g/GodheadOfAwe.java @@ -13,7 +13,7 @@ import mage.constants.Duration; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/g/GodoBanditWarlord.java b/Mage.Sets/src/mage/cards/g/GodoBanditWarlord.java index 6309133f3b9f..26a2902ee76e 100644 --- a/Mage.Sets/src/mage/cards/g/GodoBanditWarlord.java +++ b/Mage.Sets/src/mage/cards/g/GodoBanditWarlord.java @@ -17,7 +17,7 @@ import mage.constants.SuperType; import mage.filter.FilterCard; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCardInLibrary; /** diff --git a/Mage.Sets/src/mage/cards/g/GodsWilling.java b/Mage.Sets/src/mage/cards/g/GodsWilling.java index feba9913f6a4..a70df1c51e45 100644 --- a/Mage.Sets/src/mage/cards/g/GodsWilling.java +++ b/Mage.Sets/src/mage/cards/g/GodsWilling.java @@ -22,7 +22,7 @@ public GodsWilling(UUID ownerId, CardSetInfo setInfo) { // Target creature you control gains protection from the color of your choice until end of turn. Scry 1. this.getSpellAbility().addEffect(new GainProtectionFromColorTargetEffect(Duration.EndOfTurn)); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - this.getSpellAbility().addEffect(new ScryEffect(1)); + this.getSpellAbility().addEffect(new ScryEffect(1).concatBy("
")); } private GodsWilling(final GodsWilling card) { diff --git a/Mage.Sets/src/mage/cards/g/GoldenGuardian.java b/Mage.Sets/src/mage/cards/g/GoldenGuardian.java index 404b9cf78345..1314a1e26e45 100644 --- a/Mage.Sets/src/mage/cards/g/GoldenGuardian.java +++ b/Mage.Sets/src/mage/cards/g/GoldenGuardian.java @@ -20,10 +20,9 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/g/GoldenRatio.java b/Mage.Sets/src/mage/cards/g/GoldenRatio.java new file mode 100644 index 000000000000..6c6ad5f270b8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GoldenRatio.java @@ -0,0 +1,114 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.hint.Hint; +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 java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class GoldenRatio extends CardImpl { + + public GoldenRatio(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}{U}"); + + // Draw a card for each different power among creatures you control. + this.getSpellAbility().addEffect(new GoldenRatioEffect()); + this.getSpellAbility().addHint(GoldenRatioHint.instance); + } + + private GoldenRatio(final GoldenRatio card) { + super(card); + } + + @Override + public GoldenRatio copy() { + return new GoldenRatio(this); + } +} + +class GoldenRatioEffect extends OneShotEffect { + + GoldenRatioEffect() { + super(Outcome.Benefit); + staticText = "draw a card for each different power among creatures you control"; + } + + private GoldenRatioEffect(final GoldenRatioEffect effect) { + super(effect); + } + + @Override + public GoldenRatioEffect copy() { + return new GoldenRatioEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + int unique = game + .getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_CONTROLLED_CREATURE, + source.getControllerId(), source.getSourceId(), game + ) + .stream() + .filter(Objects::nonNull) + .map(MageObject::getPower) + .mapToInt(MageInt::getValue) + .distinct() + .map(x -> 1) + .sum(); + return player.drawCards(unique, source, game) > 0; + } +} + +enum GoldenRatioHint implements Hint { + instance; + + @Override + public String getText(Game game, Ability ability) { + List values = game + .getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_CONTROLLED_CREATURE, + ability.getControllerId(), ability.getSourceId(), game + ) + .stream() + .filter(Objects::nonNull) + .map(MageObject::getPower) + .map(MageInt::getValue) + .distinct() + .sorted() + .collect(Collectors.toList()); + String message = "" + values.size(); + if (values.size() > 0) { + message += " ("; + message += values.stream().map(i -> "" + i).reduce((a, b) -> a + ", " + b).orElse(""); + message += ')'; + } + return "Different powers among creatures you control: " + message; + } + + @Override + public GoldenRatioHint copy() { + return instance; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/g/GoldenWish.java b/Mage.Sets/src/mage/cards/g/GoldenWish.java index fb516b6b39a5..276d79134d92 100644 --- a/Mage.Sets/src/mage/cards/g/GoldenWish.java +++ b/Mage.Sets/src/mage/cards/g/GoldenWish.java @@ -31,7 +31,7 @@ public GoldenWish(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new WishEffect(filter)); // Exile Golden Wish. - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private GoldenWish(final GoldenWish card) { diff --git a/Mage.Sets/src/mage/cards/g/GoldnightCastigator.java b/Mage.Sets/src/mage/cards/g/GoldnightCastigator.java index 7380fd13b95c..5300b4bcfeac 100644 --- a/Mage.Sets/src/mage/cards/g/GoldnightCastigator.java +++ b/Mage.Sets/src/mage/cards/g/GoldnightCastigator.java @@ -72,7 +72,7 @@ public GoldnightCastigatorDoubleDamageEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE || + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT || event.getType() == GameEvent.EventType.DAMAGE_PLAYER; } @@ -95,7 +95,7 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { event.setAmount(CardUtil.overflowMultiply(event.getAmount(), 2)); } break; - case DAMAGE_CREATURE: + case DAMAGE_PERMANENT: Permanent permanent = game.getPermanent(event.getTargetId()); if (permanent != null) { if (permanent.getId().equals(source.getSourceId())) { diff --git a/Mage.Sets/src/mage/cards/g/GoldnightRedeemer.java b/Mage.Sets/src/mage/cards/g/GoldnightRedeemer.java index ff6cf85a06d9..201fe8b6b7cd 100644 --- a/Mage.Sets/src/mage/cards/g/GoldnightRedeemer.java +++ b/Mage.Sets/src/mage/cards/g/GoldnightRedeemer.java @@ -12,7 +12,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/g/GolemArtisan.java b/Mage.Sets/src/mage/cards/g/GolemArtisan.java index 5eef14e4f1aa..3ee60a4f3ff6 100644 --- a/Mage.Sets/src/mage/cards/g/GolemArtisan.java +++ b/Mage.Sets/src/mage/cards/g/GolemArtisan.java @@ -44,12 +44,12 @@ public GolemArtisan(UUID ownerId, CardSetInfo setInfo) { // {2}: Target artifact creature gets +1/+1 until end of turn. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(1, 1, Duration.EndOfTurn), new GenericManaCost(2)); - ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_ARTIFACT_CREATURE_PERMANENT)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_CREATURE)); this.addAbility(ability); // {2}: Target artifact creature gains your choice of flying, trample, or haste until end of turn. ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GolemArtisanEffect(), new GenericManaCost(2)); - ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_ARTIFACT_CREATURE_PERMANENT)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GolgothianSylex.java b/Mage.Sets/src/mage/cards/g/GolgothianSylex.java index 58beb5af3486..0180d2f2edcf 100644 --- a/Mage.Sets/src/mage/cards/g/GolgothianSylex.java +++ b/Mage.Sets/src/mage/cards/g/GolgothianSylex.java @@ -14,7 +14,7 @@ import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.other.ExpansionSetPredicate; +import mage.filter.predicate.card.ExpansionSetPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/g/Gomazoa.java b/Mage.Sets/src/mage/cards/g/Gomazoa.java index e3b3395cfa2b..500960c57cf8 100644 --- a/Mage.Sets/src/mage/cards/g/Gomazoa.java +++ b/Mage.Sets/src/mage/cards/g/Gomazoa.java @@ -62,7 +62,7 @@ class GomazoaEffect extends OneShotEffect { public GomazoaEffect() { super(Outcome.Neutral); - this.staticText = "Put {this} and each creature it's blocking on top of their owners' libraries, then those players shuffle their libraries"; + this.staticText = "Put {this} and each creature it's blocking on top of their owners' libraries, then those players shuffle"; } public GomazoaEffect(final GomazoaEffect effect) { diff --git a/Mage.Sets/src/mage/cards/g/GorillaShaman.java b/Mage.Sets/src/mage/cards/g/GorillaShaman.java index 050bda821cbe..33eeffbdf0b5 100644 --- a/Mage.Sets/src/mage/cards/g/GorillaShaman.java +++ b/Mage.Sets/src/mage/cards/g/GorillaShaman.java @@ -22,7 +22,7 @@ */ public final class GorillaShaman extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("noncreature artifact with converted mana cost X"); + private static final FilterPermanent filter = new FilterPermanent("noncreature artifact with mana value X"); static { filter.add(CardType.ARTIFACT.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/g/GossamerPhantasm.java b/Mage.Sets/src/mage/cards/g/GossamerPhantasm.java index d55f708bb4bd..d14b9635552f 100644 --- a/Mage.Sets/src/mage/cards/g/GossamerPhantasm.java +++ b/Mage.Sets/src/mage/cards/g/GossamerPhantasm.java @@ -1,7 +1,5 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.BecomesTargetTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; @@ -11,22 +9,24 @@ import mage.constants.CardType; import mage.constants.SubType; +import java.util.UUID; + /** - * * @author LoneFox */ public final class GossamerPhantasm extends CardImpl { public GossamerPhantasm(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.ILLUSION); this.power = new MageInt(2); this.toughness = new MageInt(1); // Flying this.addAbility(FlyingAbility.getInstance()); + // When Gossamer Phantasm becomes the target of a spell or ability, sacrifice it. - this.addAbility(new BecomesTargetTriggeredAbility(new SacrificeSourceEffect())); + this.addAbility(new BecomesTargetTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"))); } private GossamerPhantasm(final GossamerPhantasm card) { diff --git a/Mage.Sets/src/mage/cards/g/GovernTheGuildless.java b/Mage.Sets/src/mage/cards/g/GovernTheGuildless.java index 8c016dfe096c..d7c2388295f0 100644 --- a/Mage.Sets/src/mage/cards/g/GovernTheGuildless.java +++ b/Mage.Sets/src/mage/cards/g/GovernTheGuildless.java @@ -30,7 +30,7 @@ public GovernTheGuildless(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{5}{U}"); // Gain control of target monocolored creature. - this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.EndOfGame)); + this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.Custom)); this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); // Forecast - {1}{U}, Reveal Govern the Guildless from your hand: Target creature becomes the color or colors of your choice until end of turn. diff --git a/Mage.Sets/src/mage/cards/g/GrakmawSkyclaveRavager.java b/Mage.Sets/src/mage/cards/g/GrakmawSkyclaveRavager.java index e414e6b35c7b..297021a025f8 100644 --- a/Mage.Sets/src/mage/cards/g/GrakmawSkyclaveRavager.java +++ b/Mage.Sets/src/mage/cards/g/GrakmawSkyclaveRavager.java @@ -16,7 +16,7 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.GrakmawSkyclaveRavagerHydraToken; diff --git a/Mage.Sets/src/mage/cards/g/Granulate.java b/Mage.Sets/src/mage/cards/g/Granulate.java index 88ac14173e50..a940961d24dd 100644 --- a/Mage.Sets/src/mage/cards/g/Granulate.java +++ b/Mage.Sets/src/mage/cards/g/Granulate.java @@ -8,7 +8,7 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.filter.common.FilterNonlandPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; /** * @@ -16,17 +16,17 @@ */ public final class Granulate extends CardImpl { - private static final FilterNonlandPermanent filter = new FilterNonlandPermanent("nonland artifact with converted mana cost 4 or less"); + private static final FilterNonlandPermanent filter = new FilterNonlandPermanent(); static { filter.add(CardType.ARTIFACT.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 5)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 5)); } public Granulate(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{R}{R}"); // Destroy each nonland artifact with converted mana cost 4 or less. - this.getSpellAbility().addEffect(new DestroyAllEffect(filter)); + this.getSpellAbility().addEffect(new DestroyAllEffect(filter).setText("destroy each nonland artifact with mana value 4 or less")); } private Granulate(final Granulate card) { diff --git a/Mage.Sets/src/mage/cards/g/GraspingCurrent.java b/Mage.Sets/src/mage/cards/g/GraspingCurrent.java index 24777c907039..2433de4b9336 100644 --- a/Mage.Sets/src/mage/cards/g/GraspingCurrent.java +++ b/Mage.Sets/src/mage/cards/g/GraspingCurrent.java @@ -31,7 +31,7 @@ public GraspingCurrent(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 2)); // Search your library and/or graveyard for a card named Jace, Ingenious Mind-Mage, reveal it, then put it into your hand. If you searched your library this way, shuffle it. - this.getSpellAbility().addEffect(new SearchLibraryGraveyardPutInHandEffect(filter)); + this.getSpellAbility().addEffect(new SearchLibraryGraveyardPutInHandEffect(filter).concatBy("
")); } private GraspingCurrent(final GraspingCurrent card) { diff --git a/Mage.Sets/src/mage/cards/g/Grasslands.java b/Mage.Sets/src/mage/cards/g/Grasslands.java index 4d94e9b92934..a22d475b1b8f 100644 --- a/Mage.Sets/src/mage/cards/g/Grasslands.java +++ b/Mage.Sets/src/mage/cards/g/Grasslands.java @@ -21,7 +21,7 @@ public Grasslands(UUID ownerId, CardSetInfo setInfo) { // Grasslands enters the battlefield tapped. this.addAbility(new EntersBattlefieldTappedAbility()); // {tap}, Sacrifice Grasslands: Search your library for a Forest or Plains card and put it onto the battlefield. Then shuffle your library. - this.addAbility(new FetchLandActivatedAbility(false, EnumSet.of(SubType.FOREST, SubType.PLAINS))); + this.addAbility(new FetchLandActivatedAbility(false, SubType.FOREST, SubType.PLAINS)); } private Grasslands(final Grasslands card) { diff --git a/Mage.Sets/src/mage/cards/g/GratuitousViolence.java b/Mage.Sets/src/mage/cards/g/GratuitousViolence.java index b9ba8512fdc2..eb60b065e7ee 100644 --- a/Mage.Sets/src/mage/cards/g/GratuitousViolence.java +++ b/Mage.Sets/src/mage/cards/g/GratuitousViolence.java @@ -58,9 +58,8 @@ public GratuitousViolenceReplacementEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { switch (event.getType()) { - case DAMAGE_CREATURE: case DAMAGE_PLAYER: - case DAMAGE_PLANESWALKER: + case DAMAGE_PERMANENT: return true; default: return false; diff --git a/Mage.Sets/src/mage/cards/g/GraveBetrayal.java b/Mage.Sets/src/mage/cards/g/GraveBetrayal.java index ad30568c7173..6d1743d6a5bf 100644 --- a/Mage.Sets/src/mage/cards/g/GraveBetrayal.java +++ b/Mage.Sets/src/mage/cards/g/GraveBetrayal.java @@ -71,8 +71,8 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD - && ((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD) { + ZoneChangeEvent zoneChangeEvent = (ZoneChangeEvent) event; + if (zoneChangeEvent.isDiesEvent()) { Permanent permanent = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); if (permanent != null && !permanent.isControlledBy(this.getControllerId()) && permanent.isCreature()) { Card card = (Card) game.getObject(permanent.getId()); diff --git a/Mage.Sets/src/mage/cards/g/GravePact.java b/Mage.Sets/src/mage/cards/g/GravePact.java index 4bf3cf724ef4..076dd41195db 100644 --- a/Mage.Sets/src/mage/cards/g/GravePact.java +++ b/Mage.Sets/src/mage/cards/g/GravePact.java @@ -64,8 +64,8 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD - && ((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD) { + ZoneChangeEvent zoneChangeEvent = (ZoneChangeEvent) event; + if (zoneChangeEvent.isDiesEvent()) { Permanent permanent = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); return permanent != null && permanent.isControlledBy(this.getControllerId()) && permanent.isCreature(); } diff --git a/Mage.Sets/src/mage/cards/g/GravebreakerLamia.java b/Mage.Sets/src/mage/cards/g/GravebreakerLamia.java index e9fd1f39bbf9..3e4e13508526 100644 --- a/Mage.Sets/src/mage/cards/g/GravebreakerLamia.java +++ b/Mage.Sets/src/mage/cards/g/GravebreakerLamia.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.other.CastFromZonePredicate; +import mage.filter.predicate.card.CastFromZonePredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; @@ -66,7 +66,7 @@ class GravebreakerLamiaSearchEffect extends SearchEffect { GravebreakerLamiaSearchEffect() { super(new TargetCardInLibrary(), Outcome.Neutral); - staticText = "search your library for a card, put it into your graveyard, then shuffle your library"; + staticText = "search your library for a card, put it into your graveyard, then shuffle"; } private GravebreakerLamiaSearchEffect(final GravebreakerLamiaSearchEffect effect) { diff --git a/Mage.Sets/src/mage/cards/g/GravenAbomination.java b/Mage.Sets/src/mage/cards/g/GravenAbomination.java index 5d64b3860e8e..8ca543256d2b 100644 --- a/Mage.Sets/src/mage/cards/g/GravenAbomination.java +++ b/Mage.Sets/src/mage/cards/g/GravenAbomination.java @@ -10,7 +10,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.FilterCard; -import mage.filter.predicate.permanent.DefendingPlayerOwnsCardPredicate; +import mage.filter.predicate.card.DefendingPlayerOwnsCardPredicate; import mage.target.common.TargetCardInGraveyard; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/g/GravenDominator.java b/Mage.Sets/src/mage/cards/g/GravenDominator.java index 28caa75ab23c..3dc20009ac78 100644 --- a/Mage.Sets/src/mage/cards/g/GravenDominator.java +++ b/Mage.Sets/src/mage/cards/g/GravenDominator.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.constants.Duration; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/g/Gravestorm.java b/Mage.Sets/src/mage/cards/g/Gravestorm.java index c39f35d0b58f..410118c9ec05 100644 --- a/Mage.Sets/src/mage/cards/g/Gravestorm.java +++ b/Mage.Sets/src/mage/cards/g/Gravestorm.java @@ -13,7 +13,7 @@ import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInGraveyard; diff --git a/Mage.Sets/src/mage/cards/g/GraveyardShovel.java b/Mage.Sets/src/mage/cards/g/GraveyardShovel.java index 70c437a6831d..e6e9e9dc9acc 100644 --- a/Mage.Sets/src/mage/cards/g/GraveyardShovel.java +++ b/Mage.Sets/src/mage/cards/g/GraveyardShovel.java @@ -1,7 +1,5 @@ - package mage.cards.g; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; @@ -18,17 +16,18 @@ import mage.target.TargetPlayer; import mage.target.common.TargetCardInYourGraveyard; +import java.util.UUID; + /** - * * @author North */ public final class GraveyardShovel extends CardImpl { public GraveyardShovel(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); // {2}, {tap}: Target player exiles a card from their graveyard. If it's a creature card, you gain 2 life. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GraveyardShovelEffect(), new GenericManaCost(2)); + Ability ability = new SimpleActivatedAbility(new GraveyardShovelEffect(), new GenericManaCost(2)); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetPlayer()); this.addAbility(ability); @@ -46,12 +45,12 @@ public GraveyardShovel copy() { class GraveyardShovelEffect extends OneShotEffect { - public GraveyardShovelEffect() { + GraveyardShovelEffect() { super(Outcome.Exile); this.staticText = "Target player exiles a card from their graveyard. If it's a creature card, you gain 2 life"; } - public GraveyardShovelEffect(final GraveyardShovelEffect effect) { + private GraveyardShovelEffect(final GraveyardShovelEffect effect) { super(effect); } @@ -64,19 +63,20 @@ public GraveyardShovelEffect copy() { public boolean apply(Game game, Ability source) { Player targetPlayer = game.getPlayer(source.getFirstTarget()); Player controller = game.getPlayer(source.getControllerId()); - if (targetPlayer != null && controller != null) { - TargetCardInYourGraveyard target = new TargetCardInYourGraveyard(); - if (targetPlayer.chooseTarget(Outcome.Exile, target, source, game)) { - Card card = game.getCard(target.getFirstTarget()); - if (card != null) { - card.moveToExile(null, "", source, game); - if (card.isCreature()) { - controller.gainLife(2, game, source); - } - } - return true; - } + if (targetPlayer == null || controller == null || targetPlayer.getGraveyard().isEmpty()) { + return false; + } + TargetCardInYourGraveyard target = new TargetCardInYourGraveyard(); + target.setNotTarget(true); + targetPlayer.chooseTarget(Outcome.Exile, target, source, game); + Card card = game.getCard(target.getFirstTarget()); + if (card == null) { + return true; + } + targetPlayer.moveCards(card, Zone.EXILED, source, game); + if (card.isCreature()) { + controller.gainLife(2, game, source); } - return false; + return true; } } diff --git a/Mage.Sets/src/mage/cards/g/GravityNegator.java b/Mage.Sets/src/mage/cards/g/GravityNegator.java index 3af4010e285c..3f21ccefa5b5 100644 --- a/Mage.Sets/src/mage/cards/g/GravityNegator.java +++ b/Mage.Sets/src/mage/cards/g/GravityNegator.java @@ -16,7 +16,7 @@ import mage.constants.SubType; import mage.constants.Duration; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/g/GreatDefender.java b/Mage.Sets/src/mage/cards/g/GreatDefender.java index d6fae702962b..5f82bc8fa332 100644 --- a/Mage.Sets/src/mage/cards/g/GreatDefender.java +++ b/Mage.Sets/src/mage/cards/g/GreatDefender.java @@ -3,7 +3,7 @@ import java.util.UUID; import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.dynamicvalue.common.TargetConvertedManaCost; +import mage.abilities.dynamicvalue.common.TargetManaValue; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -22,8 +22,8 @@ public GreatDefender(UUID ownerId, CardSetInfo setInfo) { // Target creature gets +0/+X until end of turn, where X is its converted mana cost. this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - this.getSpellAbility().addEffect(new BoostTargetEffect(StaticValue.get(0), TargetConvertedManaCost.instance, Duration.EndOfTurn, true) - .setText("Target creature gets +0/+X until end of turn, where X is its converted mana cost.") + this.getSpellAbility().addEffect(new BoostTargetEffect(StaticValue.get(0), TargetManaValue.instance, Duration.EndOfTurn, true) + .setText("Target creature gets +0/+X until end of turn, where X is its mana value.") ); } diff --git a/Mage.Sets/src/mage/cards/g/GreatbowDoyen.java b/Mage.Sets/src/mage/cards/g/GreatbowDoyen.java index 36d4ccb9d455..9da3f0a5d034 100644 --- a/Mage.Sets/src/mage/cards/g/GreatbowDoyen.java +++ b/Mage.Sets/src/mage/cards/g/GreatbowDoyen.java @@ -72,7 +72,7 @@ public GreatbowDoyenTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/g/GreaterGargadon.java b/Mage.Sets/src/mage/cards/g/GreaterGargadon.java index db0ba60824e0..e1dd9917d1e6 100644 --- a/Mage.Sets/src/mage/cards/g/GreaterGargadon.java +++ b/Mage.Sets/src/mage/cards/g/GreaterGargadon.java @@ -81,6 +81,6 @@ public ActivationStatus canActivate(UUID playerId, Game game) { @Override public String getRule() { - return super.getRule() + " Activate this ability only if Greater Gargadon is suspended."; + return super.getRule() + " Activate only if Greater Gargadon is suspended."; } } diff --git a/Mage.Sets/src/mage/cards/g/GreaterHarvester.java b/Mage.Sets/src/mage/cards/g/GreaterHarvester.java index 6259bbbf1703..32e9bfe2ddd7 100644 --- a/Mage.Sets/src/mage/cards/g/GreaterHarvester.java +++ b/Mage.Sets/src/mage/cards/g/GreaterHarvester.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.cards.g; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/g/GreenhiltTrainee.java b/Mage.Sets/src/mage/cards/g/GreenhiltTrainee.java index 61276ca121e7..74c544c5aef5 100644 --- a/Mage.Sets/src/mage/cards/g/GreenhiltTrainee.java +++ b/Mage.Sets/src/mage/cards/g/GreenhiltTrainee.java @@ -1,45 +1,44 @@ - - package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.condition.Condition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author Loki */ public final class GreenhiltTrainee extends CardImpl { - public GreenhiltTrainee (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}"); + public GreenhiltTrainee(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); this.subtype.add(SubType.ELF); this.subtype.add(SubType.WARRIOR); this.power = new MageInt(2); this.toughness = new MageInt(3); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(4, 4, Duration.EndOfTurn), new TapSourceCost()); + Ability ability = new ActivateIfConditionActivatedAbility( + Zone.BATTLEFIELD, new BoostTargetEffect(4, 4, Duration.EndOfTurn), + new TapSourceCost(), GreenhiltTraineeCondition.instance + ); ability.addTarget(new TargetCreaturePermanent()); - ability.addCost(new GreenhiltTraineeCost()); this.addAbility(ability); } - public GreenhiltTrainee (final GreenhiltTrainee card) { + public GreenhiltTrainee(final GreenhiltTrainee card) { super(card); } @@ -49,35 +48,17 @@ public GreenhiltTrainee copy() { } } -class GreenhiltTraineeCost extends CostImpl { - - public GreenhiltTraineeCost() { - this.text = "Activate this ability only if Greenhilt Trainee's power is 4 or greater"; - } - - public GreenhiltTraineeCost(final GreenhiltTraineeCost cost) { - super(cost); - } - - @Override - public GreenhiltTraineeCost copy() { - return new GreenhiltTraineeCost(this); - } +enum GreenhiltTraineeCondition implements Condition { + instance; @Override - public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - if (permanent.getPower().getValue() >= 4) { - return true; - } - } - return false; + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + return permanent != null && permanent.getPower().getValue() >= 4; } @Override - public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - this.paid = true; - return paid; + public String toString() { + return "{this}'s power is 4 or greater"; } } diff --git a/Mage.Sets/src/mage/cards/g/GrenzoHavocRaiser.java b/Mage.Sets/src/mage/cards/g/GrenzoHavocRaiser.java index 17622c5f0360..dd8e1dcd8469 100644 --- a/Mage.Sets/src/mage/cards/g/GrenzoHavocRaiser.java +++ b/Mage.Sets/src/mage/cards/g/GrenzoHavocRaiser.java @@ -157,7 +157,7 @@ public boolean apply(Game game, Ability source) { if (card.getSpellAbility() != null) { // allow to cast the card // and you may spend mana as though it were mana of any color to cast it - CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.EndOfTurn); + CardUtil.makeCardPlayable(game, source, card, Duration.EndOfTurn, true); } } return true; diff --git a/Mage.Sets/src/mage/cards/g/GriffinGuide.java b/Mage.Sets/src/mage/cards/g/GriffinGuide.java index c99cfc81b301..f6cdb2ea84bb 100644 --- a/Mage.Sets/src/mage/cards/g/GriffinGuide.java +++ b/Mage.Sets/src/mage/cards/g/GriffinGuide.java @@ -1,4 +1,3 @@ - package mage.cards.g; import java.util.UUID; @@ -34,10 +33,12 @@ public GriffinGuide(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new AttachEffect(Outcome.AddAbility)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); + // Enchanted creature gets +2/+2 and has flying. ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(2, 2, Duration.WhileOnBattlefield)); ability.addEffect(new GainAbilityAttachedEffect(FlyingAbility.getInstance(), AttachmentType.AURA, Duration.WhileOnBattlefield, "and has flying")); this.addAbility(ability); + // When enchanted creature dies, create a 2/2 white Griffin creature token with flying. this.addAbility(new DiesAttachedTriggeredAbility(new CreateTokenEffect(new GriffinToken()), "enchanted creature")); } diff --git a/Mage.Sets/src/mage/cards/g/GrimHaruspex.java b/Mage.Sets/src/mage/cards/g/GrimHaruspex.java index 456c598def72..9fff4ca0b375 100644 --- a/Mage.Sets/src/mage/cards/g/GrimHaruspex.java +++ b/Mage.Sets/src/mage/cards/g/GrimHaruspex.java @@ -15,7 +15,7 @@ import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; /** diff --git a/Mage.Sets/src/mage/cards/g/GrimPoppet.java b/Mage.Sets/src/mage/cards/g/GrimPoppet.java index 78d8b5082ae4..02acd7107389 100644 --- a/Mage.Sets/src/mage/cards/g/GrimPoppet.java +++ b/Mage.Sets/src/mage/cards/g/GrimPoppet.java @@ -16,7 +16,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/g/GrimReminder.java b/Mage.Sets/src/mage/cards/g/GrimReminder.java index 988697164147..40699dcfa6e6 100644 --- a/Mage.Sets/src/mage/cards/g/GrimReminder.java +++ b/Mage.Sets/src/mage/cards/g/GrimReminder.java @@ -12,6 +12,7 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalActivatedAbility; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToHandEffect; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.cards.Card; import mage.cards.CardImpl; @@ -46,7 +47,7 @@ public GrimReminder(UUID ownerId, CardSetInfo setInfo) { // {B}{B}: Return Grim Reminder from your graveyard to your hand. Activate this ability only during your upkeep. this.addAbility(new ConditionalActivatedAbility( Zone.GRAVEYARD, - new ReturnToHandSourceEffect(), + new ReturnSourceFromGraveyardToHandEffect(), new ManaCostsImpl("{B}{B}"), new IsStepCondition(PhaseStep.UPKEEP), null @@ -69,7 +70,7 @@ class GrimReminderEffect extends OneShotEffect { super(Outcome.Benefit); this.staticText = "Search your library for a nonland card and reveal it. " + "Each opponent who cast a spell this turn with the same name as that card loses 6 life. " - + "Then shuffle your library."; + + "Then shuffle."; } GrimReminderEffect(final GrimReminderEffect effect) { diff --git a/Mage.Sets/src/mage/cards/g/GrimReturn.java b/Mage.Sets/src/mage/cards/g/GrimReturn.java index e2571eebef2d..074589c7ce34 100644 --- a/Mage.Sets/src/mage/cards/g/GrimReturn.java +++ b/Mage.Sets/src/mage/cards/g/GrimReturn.java @@ -1,24 +1,15 @@ - package mage.cards.g; -import mage.MageObjectReference; -import mage.abilities.Ability; -import mage.abilities.effects.Effect; 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.common.FilterCreatureCard; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardIdPredicate; -import mage.game.Game; +import mage.filter.predicate.card.PutIntoGraveFromBattlefieldThisTurnPredicate; import mage.target.common.TargetCardInGraveyard; -import mage.target.targetadjustment.TargetAdjuster; import mage.watchers.common.CardsPutIntoGraveyardWatcher; -import java.util.ArrayList; -import java.util.List; import java.util.UUID; /** @@ -26,16 +17,22 @@ */ public final class GrimReturn extends CardImpl { - private static final String textFilter = "creature card in a graveyard that was put there from the battlefield this turn"; + private static final FilterCard filter = new FilterCreatureCard( + "creature card in a graveyard that was put there from the battlefield this turn" + ); + + static { + filter.add(PutIntoGraveFromBattlefieldThisTurnPredicate.instance); + } public GrimReturn(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}"); // Choose target creature card in a graveyard that was put there from the battlefield this turn. Put that card onto the battlefield under your control. - Effect effect = new ReturnFromGraveyardToBattlefieldTargetEffect(); - effect.setText("Choose target creature card in a graveyard that was put there from the battlefield this turn. Put that card onto the battlefield under your control"); - this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); - this.getSpellAbility().setTargetAdjuster(GrimReturnAdjuster.instance); + this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect() + .setText("Choose target creature card in a graveyard that was put there from the " + + "battlefield this turn. Put that card onto the battlefield under your control")); + this.getSpellAbility().addTarget(new TargetCardInGraveyard(filter)); this.getSpellAbility().addWatcher(new CardsPutIntoGraveyardWatcher()); } @@ -48,26 +45,3 @@ public GrimReturn copy() { return new GrimReturn(this); } } - -enum GrimReturnAdjuster implements TargetAdjuster { - instance; - private static final String textFilter = "creature card in a graveyard that was put there from the battlefield this turn"; - - @Override - public void adjustTargets(Ability ability, Game game) { - CardsPutIntoGraveyardWatcher watcher = game.getState().getWatcher(CardsPutIntoGraveyardWatcher.class); - if (watcher == null) { - return; - } - FilterCard filter = new FilterCreatureCard(textFilter); - List uuidPredicates = new ArrayList<>(); - for (MageObjectReference mor : watcher.getCardsPutToGraveyardFromBattlefield()) { - if (mor.zoneCounterIsCurrent(game)) { - uuidPredicates.add(new CardIdPredicate(mor.getSourceId())); - } - } - filter.add(Predicates.or(uuidPredicates)); - ability.getTargets().clear(); - ability.addTarget(new TargetCardInGraveyard(filter)); - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/g/GrimTutor.java b/Mage.Sets/src/mage/cards/g/GrimTutor.java index 8b3dab01f84f..4ca8bd99de4e 100644 --- a/Mage.Sets/src/mage/cards/g/GrimTutor.java +++ b/Mage.Sets/src/mage/cards/g/GrimTutor.java @@ -21,7 +21,7 @@ public GrimTutor(UUID ownerId, CardSetInfo setInfo) { // Search your library for a card and put that card into your hand, then shuffle your library. TargetCardInLibrary target = new TargetCardInLibrary(); - this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(target).setText("search your library for a card and put that card into your hand, then shuffle your library")); + this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(target).setText("search your library for a card and put that card into your hand, then shuffle")); // You lose 3 life. this.getSpellAbility().addEffect(new LoseLifeSourceControllerEffect(3)); diff --git a/Mage.Sets/src/mage/cards/g/GrinningTotem.java b/Mage.Sets/src/mage/cards/g/GrinningTotem.java index 79e671dd60da..3c99291d0e25 100644 --- a/Mage.Sets/src/mage/cards/g/GrinningTotem.java +++ b/Mage.Sets/src/mage/cards/g/GrinningTotem.java @@ -59,7 +59,7 @@ class GrinningTotemSearchAndExileEffect extends OneShotEffect { public GrinningTotemSearchAndExileEffect() { super(Outcome.Benefit); - this.staticText = "Search target opponent's library for a card and exile it. Then that player shuffles their library. " + + this.staticText = "Search target opponent's library for a card and exile it. Then that player shuffles. " + "Until the beginning of your next upkeep, you may play that card. " + "At the beginning of your next upkeep, if you haven't played it, put it into its owner's graveyard"; } diff --git a/Mage.Sets/src/mage/cards/g/GrixisGrimblade.java b/Mage.Sets/src/mage/cards/g/GrixisGrimblade.java index 731cc87e4c25..3592ce751b91 100644 --- a/Mage.Sets/src/mage/cards/g/GrixisGrimblade.java +++ b/Mage.Sets/src/mage/cards/g/GrixisGrimblade.java @@ -18,7 +18,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.MulticoloredPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/g/GrizzledOutcasts.java b/Mage.Sets/src/mage/cards/g/GrizzledOutcasts.java index 87a139270354..947f486a369a 100644 --- a/Mage.Sets/src/mage/cards/g/GrizzledOutcasts.java +++ b/Mage.Sets/src/mage/cards/g/GrizzledOutcasts.java @@ -1,20 +1,14 @@ - package mage.cards.g; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** * @author nantuko @@ -34,8 +28,7 @@ public GrizzledOutcasts(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each upkeep, if no spells were cast last turn, transform Grizzled Outcasts. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private GrizzledOutcasts(final GrizzledOutcasts card) { diff --git a/Mage.Sets/src/mage/cards/g/GrothamaAllDevouring.java b/Mage.Sets/src/mage/cards/g/GrothamaAllDevouring.java index 56bdc5976bfe..3a1703b9a1ff 100644 --- a/Mage.Sets/src/mage/cards/g/GrothamaAllDevouring.java +++ b/Mage.Sets/src/mage/cards/g/GrothamaAllDevouring.java @@ -21,7 +21,7 @@ import mage.constants.WatcherScope; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -176,12 +176,12 @@ class GrothamaAllDevouringWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { - if (event.getType() != GameEvent.EventType.DAMAGED_CREATURE) { + if (event.getType() != GameEvent.EventType.DAMAGED_PERMANENT) { return; } UUID damageControllerId = game.getControllerId(event.getSourceId()); Permanent damaged = game.getPermanentOrLKIBattlefield(event.getTargetId()); - if (damaged == null || damageControllerId == null) { + if (damaged == null || !damaged.isCreature() || damageControllerId == null) { return; } MageObjectReference mor = new MageObjectReference(damaged, game); diff --git a/Mage.Sets/src/mage/cards/g/GroundlingPouncer.java b/Mage.Sets/src/mage/cards/g/GroundlingPouncer.java index 8eec92d2623a..0a8d43381e9e 100644 --- a/Mage.Sets/src/mage/cards/g/GroundlingPouncer.java +++ b/Mage.Sets/src/mage/cards/g/GroundlingPouncer.java @@ -51,7 +51,7 @@ public GroundlingPouncer(UUID ownerId, CardSetInfo setInfo) { new BoostSourceEffect(1, 3, Duration.EndOfTurn), new ManaCostsImpl("{G/U}"), new OpponentControlsPermanentCondition(filter), - "{G/U}: {this} gets +1/+3 and gains flying until end of turn. Activate this ability only once each turn and only if an opponent controls a creature with flying."); + "{G/U}: {this} gets +1/+3 and gains flying until end of turn. Activate only once each turn and only if an opponent controls a creature with flying."); ability.addEffect(new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.EndOfTurn, false, true)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/g/GrowFromTheAshes.java b/Mage.Sets/src/mage/cards/g/GrowFromTheAshes.java index cb0831eb527e..447c667b1178 100644 --- a/Mage.Sets/src/mage/cards/g/GrowFromTheAshes.java +++ b/Mage.Sets/src/mage/cards/g/GrowFromTheAshes.java @@ -29,7 +29,7 @@ public GrowFromTheAshes(UUID ownerId, CardSetInfo setInfo) { new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 2, StaticFilters.FILTER_CARD_BASIC_LAND), false, true), new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 1, StaticFilters.FILTER_CARD_BASIC_LAND), false, true), KickedCondition.instance, - "Search your library for a basic land card, put it onto the battlefield, then shuffle your library. If this spell was kicked, instead search your library for two basic land cards, put them onto the battlefield, then shuffle your library.")); + "Search your library for a basic land card, put it onto the battlefield, then shuffle. If this spell was kicked, instead search your library for two basic land cards, put them onto the battlefield, then shuffle.")); } private GrowFromTheAshes(final GrowFromTheAshes card) { diff --git a/Mage.Sets/src/mage/cards/g/Grozoth.java b/Mage.Sets/src/mage/cards/g/Grozoth.java index f5910cf48b59..efd51bc413e0 100644 --- a/Mage.Sets/src/mage/cards/g/Grozoth.java +++ b/Mage.Sets/src/mage/cards/g/Grozoth.java @@ -19,7 +19,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; @@ -67,12 +67,12 @@ class GrozothEffect extends SearchEffect { private static final FilterCard filter = new FilterCard(); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, 9)); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, 9)); } public GrozothEffect() { super(new TargetCardInLibrary(0, Integer.MAX_VALUE, filter), Outcome.DrawCard); - staticText = "you may search your library for any number of cards that have converted mana cost 9, reveal them, and put them into your hand. If you do, shuffle your library"; + staticText = "search your library for any number of cards that have mana value 9, reveal them, put them into your hand, then shuffle"; } public GrozothEffect(final GrozothEffect effect) { diff --git a/Mage.Sets/src/mage/cards/g/GrudgeKeeper.java b/Mage.Sets/src/mage/cards/g/GrudgeKeeper.java new file mode 100644 index 000000000000..67cfabe22cca --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GrudgeKeeper.java @@ -0,0 +1,112 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.VotedEvent; +import mage.players.Player; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GrudgeKeeper extends CardImpl { + + public GrudgeKeeper(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.ZOMBIE); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Whenever players finish voting, each opponent who voted for a choice you didn't vote for loses 2 life. + this.addAbility(new GrudgeKeeperTriggeredAbility()); + } + + private GrudgeKeeper(final GrudgeKeeper card) { + super(card); + } + + @Override + public GrudgeKeeper copy() { + return new GrudgeKeeper(this); + } +} + +class GrudgeKeeperTriggeredAbility extends TriggeredAbilityImpl { + + GrudgeKeeperTriggeredAbility() { + super(Zone.BATTLEFIELD, null, false); + } + + private GrudgeKeeperTriggeredAbility(final GrudgeKeeperTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.VOTED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + VotedEvent votedEvent = (VotedEvent) event; + this.getEffects().clear(); + this.addEffect(new GrudgeKeeperEffect(votedEvent.getDidntVote(getControllerId()))); + return true; + } + + @Override + public GrudgeKeeperTriggeredAbility copy() { + return new GrudgeKeeperTriggeredAbility(this); + } + + @Override + public String getRule() { + return "Whenever players finish voting, each opponent who voted for a choice you didn't vote for loses 2 life."; + } +} + +class GrudgeKeeperEffect extends OneShotEffect { + + private final Set playerIds = new HashSet<>(); + + GrudgeKeeperEffect(Set playerIds) { + super(Outcome.Benefit); + this.playerIds.addAll(playerIds); + } + + private GrudgeKeeperEffect(final GrudgeKeeperEffect effect) { + super(effect); + this.playerIds.addAll(effect.playerIds); + } + + @Override + public GrudgeKeeperEffect copy() { + return new GrudgeKeeperEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (UUID playerId : playerIds) { + Player player = game.getPlayer(playerId); + if (player != null && player.hasOpponent(source.getControllerId(), game)) { + player.loseLife(2, game, source, false); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GruesomeEncore.java b/Mage.Sets/src/mage/cards/g/GruesomeEncore.java index 086d893415e2..121c55b10857 100644 --- a/Mage.Sets/src/mage/cards/g/GruesomeEncore.java +++ b/Mage.Sets/src/mage/cards/g/GruesomeEncore.java @@ -1,7 +1,5 @@ - package mage.cards.g; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; @@ -27,8 +25,9 @@ import mage.target.common.TargetCardInOpponentsGraveyard; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author North */ public final class GruesomeEncore extends CardImpl { @@ -40,6 +39,7 @@ public GruesomeEncore(UUID ownerId, CardSetInfo setInfo) { // Put target creature card from an opponent's graveyard onto the battlefield under your control. It gains haste. this.getSpellAbility().addEffect(new GruesomeEncoreEffect()); + // Exile it at the beginning of the next end step. If that creature would leave the battlefield, exile it instead of putting it anywhere else. this.getSpellAbility().addEffect(new GruesomeEncoreReplacementEffect()); this.getSpellAbility().addTarget(new TargetCardInOpponentsGraveyard(filter)); @@ -57,12 +57,12 @@ public GruesomeEncore copy() { class GruesomeEncoreEffect extends OneShotEffect { - public GruesomeEncoreEffect() { + GruesomeEncoreEffect() { super(Outcome.PutCreatureInPlay); this.staticText = "Put target creature card from an opponent's graveyard onto the battlefield under your control. It gains haste. Exile it at the beginning of the next end step"; } - public GruesomeEncoreEffect(final GruesomeEncoreEffect effect) { + private GruesomeEncoreEffect(final GruesomeEncoreEffect effect) { super(effect); } @@ -104,7 +104,7 @@ class GruesomeEncoreReplacementEffect extends ReplacementEffectImpl { staticText = "If that creature would leave the battlefield, exile it instead of putting it anywhere else"; } - GruesomeEncoreReplacementEffect(final GruesomeEncoreReplacementEffect effect) { + private GruesomeEncoreReplacementEffect(final GruesomeEncoreReplacementEffect effect) { super(effect); } @@ -115,11 +115,9 @@ public GruesomeEncoreReplacementEffect copy() { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player player = game.getPlayer(source.getSourceId()); Card card = game.getCard(source.getFirstTarget()); - if (card != null) { - card.moveToExile(null, "", source, game); - } - return true; + return player != null && card != null && player.moveCards(card, Zone.EXILED, source, game); } @Override diff --git a/Mage.Sets/src/mage/cards/g/GruesomeMenagerie.java b/Mage.Sets/src/mage/cards/g/GruesomeMenagerie.java index 6eeeaf9391d5..ee7310ed3494 100644 --- a/Mage.Sets/src/mage/cards/g/GruesomeMenagerie.java +++ b/Mage.Sets/src/mage/cards/g/GruesomeMenagerie.java @@ -14,7 +14,7 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.Target; @@ -46,23 +46,23 @@ public GruesomeMenagerie copy() { class GruesomeMenagerieEffect extends OneShotEffect { private static final FilterCard filter1 - = new FilterCreatureCard("creature card with converted mana cost 1"); + = new FilterCreatureCard("creature card with mana value 1"); private static final FilterCard filter2 - = new FilterCreatureCard("creature card with converted mana cost 2"); + = new FilterCreatureCard("creature card with mana value 2"); private static final FilterCard filter3 - = new FilterCreatureCard("creature card with converted mana cost 3"); + = new FilterCreatureCard("creature card with mana value 3"); static { - filter1.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, 1)); - filter2.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, 2)); - filter3.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, 3)); + filter1.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, 1)); + filter2.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, 2)); + filter3.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, 3)); } public GruesomeMenagerieEffect() { super(Outcome.Benefit); - this.staticText = "Choose a creature card with converted mana cost 1 " + this.staticText = "Choose a creature card with mana value 1 " + "in your graveyard, then do the same for creature cards " - + "with converted mana costs 2 and 3. " + + "with mana values 2 and 3. " + "Return those cards to the battlefield."; } diff --git a/Mage.Sets/src/mage/cards/g/GrunnTheLonelyKing.java b/Mage.Sets/src/mage/cards/g/GrunnTheLonelyKing.java index a46c3216f26c..60ccea44218e 100644 --- a/Mage.Sets/src/mage/cards/g/GrunnTheLonelyKing.java +++ b/Mage.Sets/src/mage/cards/g/GrunnTheLonelyKing.java @@ -40,8 +40,7 @@ public GrunnTheLonelyKing(UUID ownerId, CardSetInfo setInfo) { //Whenever Grunn attacks alone, double its power and toughness until end of turn. SourcePermanentPowerCount power = new SourcePermanentPowerCount(); - SourcePermanentToughnessValue toughness = new SourcePermanentToughnessValue(); - Effect effect = new BoostSourceEffect(power, toughness, Duration.EndOfTurn, true); + Effect effect = new BoostSourceEffect(power, SourcePermanentToughnessValue.getInstance(), Duration.EndOfTurn, true); effect.setText("double its power and toughness until end of turn"); this.addAbility(new AttacksAloneTriggeredAbility(effect)); } diff --git a/Mage.Sets/src/mage/cards/g/GruulBeastmaster.java b/Mage.Sets/src/mage/cards/g/GruulBeastmaster.java index bd475efdee59..ec4fc4bd8a61 100644 --- a/Mage.Sets/src/mage/cards/g/GruulBeastmaster.java +++ b/Mage.Sets/src/mage/cards/g/GruulBeastmaster.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/g/GryffsBoon.java b/Mage.Sets/src/mage/cards/g/GryffsBoon.java index d74c17ddbd78..a98d486874f5 100644 --- a/Mage.Sets/src/mage/cards/g/GryffsBoon.java +++ b/Mage.Sets/src/mage/cards/g/GryffsBoon.java @@ -66,7 +66,7 @@ class GryffsBoonEffect extends OneShotEffect { public GryffsBoonEffect() { super(Outcome.PutCardInPlay); - staticText = "Return {this} from your graveyard to the battlefield attached to target creature. Activate this ability only any time you could cast a sorcery."; + staticText = "Return {this} from your graveyard to the battlefield attached to target creatur"; } public GryffsBoonEffect(final GryffsBoonEffect effect) { diff --git a/Mage.Sets/src/mage/cards/g/GuardianArchon.java b/Mage.Sets/src/mage/cards/g/GuardianArchon.java new file mode 100644 index 000000000000..e2669d72f0e8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GuardianArchon.java @@ -0,0 +1,141 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.RevealSecretOpponentCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ChooseSecretOpponentEffect; +import mage.abilities.effects.common.continuous.GainAbilityControllerEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.ProtectionAbility; +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.FilterCard; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.game.stack.StackObject; +import mage.players.Player; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GuardianArchon extends CardImpl { + + public GuardianArchon(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}{W}"); + + this.subtype.add(SubType.ARCHON); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // As Guardian Archon enters the battlefield, secretly choose an opponent. + this.addAbility(new AsEntersBattlefieldAbility(new ChooseSecretOpponentEffect())); + + // Reveal the player you chose: You and target permanent you control each gain protection from the chosen player until end of turn. Activate only once. + Ability ability = new SimpleActivatedAbility(new GuardianArchonEffect(), new RevealSecretOpponentCost()); + ability.addTarget(new TargetControlledPermanent()); + this.addAbility(ability); + } + + private GuardianArchon(final GuardianArchon card) { + super(card); + } + + @Override + public GuardianArchon copy() { + return new GuardianArchon(this); + } +} + +class GuardianArchonEffect extends OneShotEffect { + + GuardianArchonEffect() { + super(Outcome.Benefit); + staticText = "you and target permanent you control each gain protection " + + "from the chosen player until end of turn. Activate only once"; + } + + private GuardianArchonEffect(final GuardianArchonEffect effect) { + super(effect); + } + + @Override + public GuardianArchonEffect copy() { + return new GuardianArchonEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(ChooseSecretOpponentEffect.getChosenPlayer(source, game)); + if (player == null) { + return false; + } + game.addEffect(new GainAbilityTargetEffect( + new GuardianArchonProtectionAbility(player.getId()), Duration.EndOfTurn + ), source); + game.addEffect(new GainAbilityControllerEffect( + new GuardianArchonProtectionAbility(player.getId()), Duration.EndOfTurn + ), source); + return true; + } +} + +class GuardianArchonProtectionAbility extends ProtectionAbility { + + private final UUID playerId; + + public GuardianArchonProtectionAbility(UUID playerId) { + super(new FilterCard()); + this.playerId = playerId; + } + + public GuardianArchonProtectionAbility(final GuardianArchonProtectionAbility ability) { + super(ability); + this.playerId = ability.playerId; + } + + @Override + public GuardianArchonProtectionAbility copy() { + return new GuardianArchonProtectionAbility(this); + } + + @Override + public String getRule() { + return "{this} has protection from the chosen player."; + } + + @Override + public boolean canTarget(MageObject source, Game game) { + if (playerId != null && source != null) { + if (source instanceof Permanent) { + return !((Permanent) source).isControlledBy(playerId); + } + if (source instanceof Spell) { + return !((Spell) source).isControlledBy(playerId); + } + if (source instanceof StackObject) { + return !((StackObject) source).isControlledBy(playerId); + } + if (source instanceof Card) { // e.g. for Vengeful Pharaoh + return !((Card) source).isOwnedBy(playerId); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GuardianAugmenter.java b/Mage.Sets/src/mage/cards/g/GuardianAugmenter.java new file mode 100644 index 000000000000..a64e1fc92c99 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GuardianAugmenter.java @@ -0,0 +1,63 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.FlashAbility; +import mage.abilities.keyword.HexproofAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.CommanderPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GuardianAugmenter extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("commander creatures"); + private static final FilterPermanent filter2 = new FilterPermanent("commanders"); + + static { + filter.add(CommanderPredicate.instance); + filter2.add(CommanderPredicate.instance); + } + + public GuardianAugmenter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.TROLL); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Commander creatures you control get +2/+2. + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 2, 2, Duration.WhileOnBattlefield, filter + ))); + + // Commanders you control have hexproof. + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + HexproofAbility.getInstance(), Duration.WhileOnBattlefield, filter2 + ))); + } + + private GuardianAugmenter(final GuardianAugmenter card) { + super(card); + } + + @Override + public GuardianAugmenter copy() { + return new GuardianAugmenter(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GuardianProject.java b/Mage.Sets/src/mage/cards/g/GuardianProject.java index a1473a844797..54face9a2e38 100644 --- a/Mage.Sets/src/mage/cards/g/GuardianProject.java +++ b/Mage.Sets/src/mage/cards/g/GuardianProject.java @@ -15,7 +15,7 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardIdPredicate; import mage.filter.predicate.mageobject.NamePredicate; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; diff --git a/Mage.Sets/src/mage/cards/g/GuardianShieldBearer.java b/Mage.Sets/src/mage/cards/g/GuardianShieldBearer.java index b79fb3c202ea..39e07e9915b6 100644 --- a/Mage.Sets/src/mage/cards/g/GuardianShieldBearer.java +++ b/Mage.Sets/src/mage/cards/g/GuardianShieldBearer.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/g/GuardiansOfKoilos.java b/Mage.Sets/src/mage/cards/g/GuardiansOfKoilos.java index e93875afe33f..56573bd95689 100644 --- a/Mage.Sets/src/mage/cards/g/GuardiansOfKoilos.java +++ b/Mage.Sets/src/mage/cards/g/GuardiansOfKoilos.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.cards.g; import java.util.UUID; @@ -15,7 +10,7 @@ import mage.constants.SubType; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.HistoricPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/g/GuidedPassage.java b/Mage.Sets/src/mage/cards/g/GuidedPassage.java index b8b9c2c1b4d1..ee90202993f8 100644 --- a/Mage.Sets/src/mage/cards/g/GuidedPassage.java +++ b/Mage.Sets/src/mage/cards/g/GuidedPassage.java @@ -56,7 +56,7 @@ class GuidedPassageEffect extends OneShotEffect { GuidedPassageEffect() { super(Outcome.Benefit); - this.staticText = "Reveal the cards in your library. An opponent chooses from among them a creature card, a land card, and a noncreature, nonland card. You put the chosen cards into your hand. Then shuffle your library."; + this.staticText = "Reveal the cards in your library. An opponent chooses from among them a creature card, a land card, and a noncreature, nonland card. You put the chosen cards into your hand. Then shuffle."; } GuidedPassageEffect(final GuidedPassageEffect effect) { diff --git a/Mage.Sets/src/mage/cards/g/GuidingVoice.java b/Mage.Sets/src/mage/cards/g/GuidingVoice.java new file mode 100644 index 000000000000..70097043a0ce --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GuidingVoice.java @@ -0,0 +1,37 @@ +package mage.cards.g; + +import mage.abilities.effects.common.LearnEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GuidingVoice extends CardImpl { + + public GuidingVoice(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{W}"); + + // Put a +1/+1 counter on target creature. + this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + + // Learn (You may reveal a Lesson card you own from outside the game and p + this.getSpellAbility().addEffect(new LearnEffect().concatBy("
")); + } + + private GuidingVoice(final GuidingVoice card) { + super(card); + } + + @Override + public GuidingVoice copy() { + return new GuidingVoice(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/Guile.java b/Mage.Sets/src/mage/cards/g/Guile.java index 12c03cedc8f0..d4ddadcd01ce 100644 --- a/Mage.Sets/src/mage/cards/g/Guile.java +++ b/Mage.Sets/src/mage/cards/g/Guile.java @@ -1,7 +1,5 @@ - package mage.cards.g; -import java.util.UUID; import mage.ApprovingObject; import mage.MageInt; import mage.abilities.Ability; @@ -13,20 +11,16 @@ 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.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.stack.Spell; import mage.game.stack.StackObject; import mage.players.Player; +import java.util.UUID; + /** - * * @author emerald000 */ public final class Guile extends CardImpl { @@ -85,12 +79,12 @@ public boolean apply(Game game, Ability source) { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Spell spell = game.getStack().getSpell(event.getTargetId()); Player controller = game.getPlayer(source.getControllerId()); - if (spell != null + if (spell != null && controller != null) { controller.moveCards(spell, Zone.EXILED, source, game); - if (!spell.isCopy()) { + if (!spell.isCopy()) { // copies doesn't exists in exile zone Card spellCard = spell.getCard(); - if (spellCard != null + if (spellCard != null && controller.chooseUse(Outcome.PlayForFree, "Play " + spellCard.getIdName() + " for free?", source, game)) { controller.playCard(spellCard, game, true, true, new ApprovingObject(source, game)); } diff --git a/Mage.Sets/src/mage/cards/g/GuiseOfFire.java b/Mage.Sets/src/mage/cards/g/GuiseOfFire.java index 067d2a9f9982..2f543c31b3c1 100644 --- a/Mage.Sets/src/mage/cards/g/GuiseOfFire.java +++ b/Mage.Sets/src/mage/cards/g/GuiseOfFire.java @@ -34,7 +34,7 @@ public GuiseOfFire(UUID ownerId, CardSetInfo setInfo) { // Enchanted creature gets +1/-1 and attacks each turn if able. ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(1, -1, Duration.WhileOnBattlefield)); Effect effect = new AttacksIfAbleAttachedEffect(Duration.WhileOnBattlefield, AttachmentType.AURA); - effect.setText("and attacks each turn if able"); + effect.setText("and attacks each combat if able"); ability.addEffect(effect); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/g/GyomeMasterChef.java b/Mage.Sets/src/mage/cards/g/GyomeMasterChef.java new file mode 100644 index 000000000000..d8a3d1d25123 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GyomeMasterChef.java @@ -0,0 +1,132 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.IndestructibleAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.PermanentToken; +import mage.game.permanent.token.FoodToken; +import mage.target.common.TargetControlledPermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GyomeMasterChef extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.FOOD, "a Food"); + private static final Hint hint = new ValueHint("Nontoken creatures entered this turn", GyomeMasterChefValue.instance); + + public GyomeMasterChef(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.TROLL); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(5); + this.toughness = new MageInt(3); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // At the beginning of your end step, create a number of Food tokens equal to the number of nontoken creatures you had enter the battlefield under your control this turn. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + new CreateTokenEffect( + new FoodToken(), GyomeMasterChefValue.instance + ).setText("create a number of Food tokens equal to the number of nontoken creatures " + + "you had enter the battlefield under your control this turn"), + TargetController.YOU, false + ).addHint(hint), new GyomeMasterChefWatcher()); + + // {1}, Sacrifice a Food: Target creature gains indestructible until end of turn. Tap it. + Ability ability = new SimpleActivatedAbility(new GainAbilityTargetEffect( + IndestructibleAbility.getInstance(), Duration.EndOfTurn + ), new GenericManaCost(1)); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter))); + ability.addEffect(new TapTargetEffect().setText("it")); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private GyomeMasterChef(final GyomeMasterChef card) { + super(card); + } + + @Override + public GyomeMasterChef copy() { + return new GyomeMasterChef(this); + } +} + +enum GyomeMasterChefValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return GyomeMasterChefWatcher.getValue(sourceAbility.getControllerId(), game); + } + + @Override + public GyomeMasterChefValue copy() { + return instance; + } + + @Override + public String getMessage() { + return ""; + } +} + +class GyomeMasterChefWatcher extends Watcher { + + private final Map playerMap = new HashMap<>(); + + GyomeMasterChefWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD + && !(((EntersTheBattlefieldEvent) event).getTarget() instanceof PermanentToken)) { + playerMap.compute(event.getPlayerId(), (u, i) -> i != null ? Integer.sum(i, 1) : 1); + } + } + + @Override + public void reset() { + super.reset(); + playerMap.clear(); + } + + static int getValue(UUID playerId, Game game) { + GyomeMasterChefWatcher watcher = game.getState().getWatcher(GyomeMasterChefWatcher.class); + if (watcher == null) { + return 0; + } + return watcher != null ? watcher.playerMap.getOrDefault(playerId, 0) : 0; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GyrudaDoomOfDepths.java b/Mage.Sets/src/mage/cards/g/GyrudaDoomOfDepths.java index effc47bf9a7a..aec97874ad39 100644 --- a/Mage.Sets/src/mage/cards/g/GyrudaDoomOfDepths.java +++ b/Mage.Sets/src/mage/cards/g/GyrudaDoomOfDepths.java @@ -11,7 +11,7 @@ import mage.constants.*; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostParityPredicate; +import mage.filter.predicate.mageobject.ManaValueParityPredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; @@ -56,14 +56,14 @@ enum GyrudaDoomOfDepthsCompanionCondition implements CompanionCondition { @Override public String getRule() { - return "Your starting deck contains only cards with even converted mana costs."; + return "Your starting deck contains only cards with even mana values."; } @Override public boolean isLegal(Set deck, int startingSize) { return deck .stream() - .mapToInt(MageObject::getConvertedManaCost) + .mapToInt(MageObject::getManaValue) .map(i -> i % 2) .allMatch(i -> i == 0); } @@ -72,15 +72,15 @@ public boolean isLegal(Set deck, int startingSize) { class GyrudaDoomOfDepthsEffect extends OneShotEffect { private static final FilterCard filter - = new FilterCreatureCard("creature card with an even converted mana cost"); + = new FilterCreatureCard("creature card with an even mana value"); static { - filter.add(ConvertedManaCostParityPredicate.EVEN); + filter.add(ManaValueParityPredicate.EVEN); } GyrudaDoomOfDepthsEffect() { super(Outcome.Benefit); - staticText = "each player mills four cards. Put a creature card with an even converted mana cost " + + staticText = "each player mills four cards. Put a creature card with an even mana value " + "from among the milled cards onto the battlefield under your control"; } diff --git a/Mage.Sets/src/mage/cards/g/GyrusWakerOfCorpses.java b/Mage.Sets/src/mage/cards/g/GyrusWakerOfCorpses.java index bb683b6c8787..48dc31882cd9 100644 --- a/Mage.Sets/src/mage/cards/g/GyrusWakerOfCorpses.java +++ b/Mage.Sets/src/mage/cards/g/GyrusWakerOfCorpses.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.cards.g; import mage.MageInt; diff --git a/Mage.Sets/src/mage/cards/h/HakimLoreweaver.java b/Mage.Sets/src/mage/cards/h/HakimLoreweaver.java index 8e7b4f64de5e..8828f590c576 100644 --- a/Mage.Sets/src/mage/cards/h/HakimLoreweaver.java +++ b/Mage.Sets/src/mage/cards/h/HakimLoreweaver.java @@ -18,7 +18,7 @@ import mage.filter.FilterCard; import mage.filter.FilterPermanent; import mage.filter.predicate.mageobject.CardIdPredicate; -import mage.filter.predicate.other.AuraCardCanAttachToPermanentId; +import mage.filter.predicate.card.AuraCardCanAttachToPermanentId; import mage.filter.predicate.permanent.AttachedToPredicate; import mage.game.Game; import mage.game.permanent.Permanent; @@ -149,6 +149,6 @@ public boolean apply(Game game, Ability source) { @Override public String toString() { - return "only during your upkeep and only if {this} isn't enchanted"; + return "during your upkeep and only if {this} isn't enchanted"; } } diff --git a/Mage.Sets/src/mage/cards/h/HaktosTheUnscarred.java b/Mage.Sets/src/mage/cards/h/HaktosTheUnscarred.java index d5f0960f3b2c..3a585e8ee91f 100644 --- a/Mage.Sets/src/mage/cards/h/HaktosTheUnscarred.java +++ b/Mage.Sets/src/mage/cards/h/HaktosTheUnscarred.java @@ -53,7 +53,7 @@ public HaktosTheUnscarred(UUID ownerId, CardSetInfo setInfo) { ability.setRuleVisible(false); this.addAbility(new SimpleStaticAbility( new GainAbilitySourceEffect(ability, Duration.WhileOnBattlefield) - .setText("{this} has protection from each converted mana cost other than the chosen number") + .setText("{this} has protection from each mana value other than the chosen number") )); } @@ -111,6 +111,6 @@ public boolean apply(ObjectSourcePlayer input, Game game) { return false; } int num = (int) obj; - return input.getObject().getConvertedManaCost() != num; + return input.getObject().getManaValue() != num; } } diff --git a/Mage.Sets/src/mage/cards/h/HaldanAvidArcanist.java b/Mage.Sets/src/mage/cards/h/HaldanAvidArcanist.java index ef1cb70e8cae..45662193debf 100644 --- a/Mage.Sets/src/mage/cards/h/HaldanAvidArcanist.java +++ b/Mage.Sets/src/mage/cards/h/HaldanAvidArcanist.java @@ -38,7 +38,7 @@ public HaldanAvidArcanist(UUID ownerId, CardSetInfo setInfo) { // exiled them, and you may spend mana as though it were mana of any color to cast those spells. Ability ability = new SimpleStaticAbility(new HaldanAvidArcanistCastFromExileEffect()); ability.addEffect(new HaldanAvidArcanistSpendAnyManaEffect()); - this.addAbility(ability); + this.addAbility(ability, PakoArcaneRetriever.createWatcher()); } private HaldanAvidArcanist(final HaldanAvidArcanist card) { @@ -49,18 +49,6 @@ private HaldanAvidArcanist(final HaldanAvidArcanist card) { public HaldanAvidArcanist copy() { return new HaldanAvidArcanist(this); } - - static boolean checkCard(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (!PakoArcaneRetriever.checkWatcher(affectedControllerId, game.getCard(objectId), game) - || !source.isControlledBy(affectedControllerId) - || game.getState().getZone(objectId) != Zone.EXILED) { - return false; - } - Card card = game.getCard(objectId); - return card != null - && !card.isCreature() - && card.getCounters(game).containsKey(CounterType.FETCH); - } } class HaldanAvidArcanistCastFromExileEffect extends AsThoughEffectImpl { @@ -86,7 +74,13 @@ public HaldanAvidArcanistCastFromExileEffect copy() { @Override public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { - return HaldanAvidArcanist.checkCard(sourceId, source, affectedControllerId, game); + Card card = game.getCard(sourceId); + if (card == null || !source.isControlledBy(affectedControllerId) + || game.getState().getZone(sourceId) != Zone.EXILED + || !PakoArcaneRetriever.checkWatcher(affectedControllerId, card, game)) { + return false; + } + return !card.isCreature() && card.getCounters(game).containsKey(CounterType.FETCH); } } @@ -113,7 +107,17 @@ public HaldanAvidArcanistSpendAnyManaEffect copy() { @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - return true; + // The card may be on the stack, in which case it will not have a fetch counter + // on it. In this case, we will have to rely on the watcher to tell us whether + // or not it is a valid card to apply the effect to. + Zone zone; + Card card = game.getCard(objectId); + if (card == null || !source.isControlledBy(affectedControllerId) + || (zone = game.getState().getZone(objectId)) != Zone.EXILED && zone != Zone.STACK + || !PakoArcaneRetriever.checkWatcher(affectedControllerId, card, game)) { + return false; + } + return !card.isCreature() && (card.getCounters(game).containsKey(CounterType.FETCH) || zone == Zone.STACK); } @Override diff --git a/Mage.Sets/src/mage/cards/h/Halfdane.java b/Mage.Sets/src/mage/cards/h/Halfdane.java index 1a37e6e35966..723b55fc0885 100644 --- a/Mage.Sets/src/mage/cards/h/Halfdane.java +++ b/Mage.Sets/src/mage/cards/h/Halfdane.java @@ -12,7 +12,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/h/HallMonitor.java b/Mage.Sets/src/mage/cards/h/HallMonitor.java new file mode 100644 index 000000000000..e08315c4da82 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HallMonitor.java @@ -0,0 +1,52 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.combat.CantBlockTargetEffect; +import mage.abilities.keyword.HasteAbility; +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 TheElk801 + */ +public final class HallMonitor extends CardImpl { + + public HallMonitor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}"); + + this.subtype.add(SubType.LIZARD); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // {1}{R}, {T}: Target creature can't block this turn. + Ability ability = new SimpleActivatedAbility( + new CantBlockTargetEffect(Duration.EndOfTurn), new ManaCostsImpl("{1}{R}") + ); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private HallMonitor(final HallMonitor card) { + super(card); + } + + @Override + public HallMonitor copy() { + return new HallMonitor(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HallOfOracles.java b/Mage.Sets/src/mage/cards/h/HallOfOracles.java new file mode 100644 index 000000000000..f2eb88b2096c --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HallOfOracles.java @@ -0,0 +1,82 @@ +package mage.cards.h; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.ActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.decorator.ConditionalActivatedAbility; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.mana.AnyColorManaAbility; +import mage.abilities.mana.ColorlessManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TimingRule; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.target.common.TargetCreaturePermanent; +import mage.watchers.common.SpellsCastWatcher; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HallOfOracles extends CardImpl { + + public HallOfOracles(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // {T}: Add {C}. + this.addAbility(new ColorlessManaAbility()); + + // {1}, {T}: Add one mana of any color. + ActivatedAbility ability = new AnyColorManaAbility(new GenericManaCost(1)); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + + // {T}: Put a +1/+1 counter on target creature. Activate only as a sorcery and only if you've cast an instant or sorcery spell this turn. + ability = new ConditionalActivatedAbility( + Zone.BATTLEFIELD, new AddCountersTargetEffect(CounterType.P1P1.createInstance()), new TapSourceCost(), + HallOfOraclesCondition.instance, "{T}: Put a +1/+1 counter on target creature. " + + "Activate only as a sorcery and only if you've cast an instant or sorcery spell this turn." + ); + ability.addTarget(new TargetCreaturePermanent()); + ability.setTiming(TimingRule.SORCERY); + this.addAbility(ability, new SpellsCastWatcher()); + } + + private HallOfOracles(final HallOfOracles card) { + super(card); + } + + @Override + public HallOfOracles copy() { + return new HallOfOracles(this); + } +} + +enum HallOfOraclesCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class); + if (watcher == null) { + return false; + } + List spells = watcher.getSpellsCastThisTurn(source.getControllerId()); + return spells != null && spells + .stream() + .filter(Objects::nonNull) + .filter(MageObject::isInstantOrSorcery) + .map(Spell::getSourceId) + .anyMatch(source.getSourceId()::equals); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HalvarGodOfBattle.java b/Mage.Sets/src/mage/cards/h/HalvarGodOfBattle.java index bc2c801e63cc..c02133ae506f 100644 --- a/Mage.Sets/src/mage/cards/h/HalvarGodOfBattle.java +++ b/Mage.Sets/src/mage/cards/h/HalvarGodOfBattle.java @@ -189,7 +189,8 @@ public HalvarGodOfBattlePredicate(FilterPermanent filter) { @Override public boolean apply(ObjectSourcePlayer input, Game game) { UUID attachedTo = input.getObject().getAttachedTo(); - return attachedTo != null && filter.match(game.getPermanent(attachedTo), input.getSourceId(), input.getPlayerId(), game); + Permanent permanent = game.getPermanent(attachedTo); + return permanent != null && filter.match(permanent, input.getSourceId(), input.getPlayerId(), game); } @Override diff --git a/Mage.Sets/src/mage/cards/h/HammerMage.java b/Mage.Sets/src/mage/cards/h/HammerMage.java index 7a55a0617a6d..43bbc3f775c4 100644 --- a/Mage.Sets/src/mage/cards/h/HammerMage.java +++ b/Mage.Sets/src/mage/cards/h/HammerMage.java @@ -17,7 +17,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.common.FilterArtifactPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; @@ -55,7 +55,7 @@ class HammerMageEffect extends OneShotEffect { public HammerMageEffect() { super(Outcome.DestroyPermanent); - staticText = "Destroy all artifacts with converted mana cost X or less"; + staticText = "Destroy all artifacts with mana value X or less"; } public HammerMageEffect(final HammerMageEffect effect) { @@ -70,7 +70,7 @@ public HammerMageEffect copy() { @Override public boolean apply(Game game, Ability source) { FilterArtifactPermanent filter = new FilterArtifactPermanent(); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, source.getManaCostsToPay().getX() + 1)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, source.getManaCostsToPay().getX() + 1)); for(Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { permanent.destroy(source, game, false); } diff --git a/Mage.Sets/src/mage/cards/h/HanSoloScrumrat.java b/Mage.Sets/src/mage/cards/h/HanSoloScrumrat.java index f498fd6df10b..3d963281ef31 100644 --- a/Mage.Sets/src/mage/cards/h/HanSoloScrumrat.java +++ b/Mage.Sets/src/mage/cards/h/HanSoloScrumrat.java @@ -14,7 +14,7 @@ import mage.cards.CardSetInfo; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -80,9 +80,8 @@ public HanSoloScrumratTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE - || event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT + || event.getType() == GameEvent.EventType.DAMAGED_PLAYER; } @Override diff --git a/Mage.Sets/src/mage/cards/h/HanweirGarrison.java b/Mage.Sets/src/mage/cards/h/HanweirGarrison.java index aad17ff02fe8..670b6be286f7 100644 --- a/Mage.Sets/src/mage/cards/h/HanweirGarrison.java +++ b/Mage.Sets/src/mage/cards/h/HanweirGarrison.java @@ -31,7 +31,7 @@ public HanweirGarrison(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new AttacksTriggeredAbility(new CreateTokenEffect(new RedHumanToken(), 2, true, true), false)); // (Melds with Hanweir Battlements.) - this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("(Melds with Hannweir Battlements.)"))); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("(Melds with Hanweir Battlements.)"))); } private HanweirGarrison(final HanweirGarrison card) { diff --git a/Mage.Sets/src/mage/cards/h/HanweirWatchkeep.java b/Mage.Sets/src/mage/cards/h/HanweirWatchkeep.java index 2e8f6104f056..cd550520ccab 100644 --- a/Mage.Sets/src/mage/cards/h/HanweirWatchkeep.java +++ b/Mage.Sets/src/mage/cards/h/HanweirWatchkeep.java @@ -1,21 +1,15 @@ - package mage.cards.h; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.keyword.DefenderAbility; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** * @author nantuko @@ -37,8 +31,7 @@ public HanweirWatchkeep(UUID ownerId, CardSetInfo setInfo) { this.addAbility(DefenderAbility.getInstance()); // At the beginning of each upkeep, if no spells were cast last turn, transform Hanweir Watchkeep. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private HanweirWatchkeep(final HanweirWatchkeep card) { diff --git a/Mage.Sets/src/mage/cards/h/HarbingerOfTheHunt.java b/Mage.Sets/src/mage/cards/h/HarbingerOfTheHunt.java index 8be63b22f53d..c1834f55ac2b 100644 --- a/Mage.Sets/src/mage/cards/h/HarbingerOfTheHunt.java +++ b/Mage.Sets/src/mage/cards/h/HarbingerOfTheHunt.java @@ -15,7 +15,7 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/h/HarmonicConvergence.java b/Mage.Sets/src/mage/cards/h/HarmonicConvergence.java index 4deb3cae3bd8..95dccfa4bcd9 100644 --- a/Mage.Sets/src/mage/cards/h/HarmonicConvergence.java +++ b/Mage.Sets/src/mage/cards/h/HarmonicConvergence.java @@ -15,7 +15,7 @@ import java.util.*; import mage.cards.Cards; import mage.filter.common.FilterEnchantmentPermanent; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/h/HarnessInfinity.java b/Mage.Sets/src/mage/cards/h/HarnessInfinity.java new file mode 100644 index 000000000000..d68cb4398736 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HarnessInfinity.java @@ -0,0 +1,71 @@ +package mage.cards.h; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileSpellEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HarnessInfinity extends CardImpl { + + public HarnessInfinity(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{B}{B}{B}{G}{G}{G}"); + + // Exchange your hand and graveyard. + this.getSpellAbility().addEffect(new HarnessInfinityEffect()); + + // Exile Harness Infinity. + this.getSpellAbility().addEffect(new ExileSpellEffect()); + } + + private HarnessInfinity(final HarnessInfinity card) { + super(card); + } + + @Override + public HarnessInfinity copy() { + return new HarnessInfinity(this); + } +} + +class HarnessInfinityEffect extends OneShotEffect { + + HarnessInfinityEffect() { + super(Outcome.Benefit); + staticText = "Exchange your hand and graveyard.
"; + } + + private HarnessInfinityEffect(final HarnessInfinityEffect effect) { + super(effect); + } + + @Override + public HarnessInfinityEffect copy() { + return new HarnessInfinityEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards hand = new CardsImpl(player.getHand()); + Cards graveyard = new CardsImpl(player.getGraveyard()); + player.moveCards(hand, Zone.GRAVEYARD, source, game); + player.moveCards(graveyard, Zone.HAND, source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/h/HarvestSeason.java b/Mage.Sets/src/mage/cards/h/HarvestSeason.java index 7470f15aacd3..4e189e3ab3e3 100644 --- a/Mage.Sets/src/mage/cards/h/HarvestSeason.java +++ b/Mage.Sets/src/mage/cards/h/HarvestSeason.java @@ -55,7 +55,7 @@ class HarvestSeasonEffect extends OneShotEffect { HarvestSeasonEffect() { super(Outcome.Benefit); this.staticText = "Search your library for up to X basic land cards, where X is the number of tapped creatures you control," - + " and put those cards onto the battlefield tapped, then shuffle your library."; + + " and put those cards onto the battlefield tapped, then shuffle."; } HarvestSeasonEffect(final HarvestSeasonEffect effect) { diff --git a/Mage.Sets/src/mage/cards/h/HarvesterOfSouls.java b/Mage.Sets/src/mage/cards/h/HarvesterOfSouls.java index bdc3d8f5bec2..c6c05e0fe4cc 100644 --- a/Mage.Sets/src/mage/cards/h/HarvesterOfSouls.java +++ b/Mage.Sets/src/mage/cards/h/HarvesterOfSouls.java @@ -12,7 +12,7 @@ import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; /** diff --git a/Mage.Sets/src/mage/cards/h/HasranOgress.java b/Mage.Sets/src/mage/cards/h/HasranOgress.java index e417146375b1..124d238d007e 100644 --- a/Mage.Sets/src/mage/cards/h/HasranOgress.java +++ b/Mage.Sets/src/mage/cards/h/HasranOgress.java @@ -62,8 +62,7 @@ public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { Cost cost = new ManaCostsImpl("{2}"); - String message = "Would you like to pay {2} to prevent taking 3 damage from Hasran Ogress?"; - if (!(controller.chooseUse(Outcome.Benefit, message, source, game) + if (!(controller.chooseUse(Outcome.Benefit, "Pay {2}?", source, game) && cost.pay(source, game, source, controller.getId(), false, null))) { controller.damage(3, source.getSourceId(), source, game); } diff --git a/Mage.Sets/src/mage/cards/h/HatcherySpider.java b/Mage.Sets/src/mage/cards/h/HatcherySpider.java index 36c954bbc28a..350c6ef753a9 100644 --- a/Mage.Sets/src/mage/cards/h/HatcherySpider.java +++ b/Mage.Sets/src/mage/cards/h/HatcherySpider.java @@ -22,7 +22,7 @@ import mage.filter.StaticFilters; import mage.filter.common.FilterPermanentCard; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; @@ -67,7 +67,7 @@ public HatcherySpiderEffect() { super(Outcome.Benefit); this.staticText = "reveal the top X cards of your library, " + "where X is the number of creature cards in your graveyard. " - + "You may put a green permanent card with converted mana cost " + + "You may put a green permanent card with mana value " + "X or less from among them onto the battlefield. " + "Put the rest on the bottom of your library " + "in a random order."; @@ -92,11 +92,11 @@ public boolean apply(Game game, Ability source) { StaticFilters.FILTER_CARD_CREATURE ).calculate(game, source, this); FilterCard filter = new FilterPermanentCard( - "green permanent card with converted mana cost " + "green permanent card with mana value " + xValue + " or less" ); filter.add(new ColorPredicate(ObjectColor.GREEN)); - filter.add(new ConvertedManaCostPredicate( + filter.add(new ManaValuePredicate( ComparisonType.FEWER_THAN, xValue + 1 )); TargetCard target = new TargetCardInLibrary(filter); diff --git a/Mage.Sets/src/mage/cards/h/HauntedFengraf.java b/Mage.Sets/src/mage/cards/h/HauntedFengraf.java index b8e35600a521..9ab1ed054a2d 100644 --- a/Mage.Sets/src/mage/cards/h/HauntedFengraf.java +++ b/Mage.Sets/src/mage/cards/h/HauntedFengraf.java @@ -1,6 +1,5 @@ package mage.cards.h; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; @@ -8,7 +7,6 @@ import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.mana.ColorlessManaAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -17,10 +15,12 @@ import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; -import mage.util.RandomUtil; +import mage.target.TargetCard; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; /** - * * @author North */ public final class HauntedFengraf extends CardImpl { @@ -30,8 +30,9 @@ public HauntedFengraf(UUID ownerId, CardSetInfo setInfo) { // {tap}: Add {C}. this.addAbility(new ColorlessManaAbility()); + // {3}, {tap}, Sacrifice Haunted Fengraf: Return a creature card at random from your graveyard to your hand. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new HauntedFengrafEffect(), new GenericManaCost(3)); + Ability ability = new SimpleActivatedAbility(new HauntedFengrafEffect(), new GenericManaCost(3)); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); @@ -49,12 +50,12 @@ public HauntedFengraf copy() { class HauntedFengrafEffect extends OneShotEffect { - public HauntedFengrafEffect() { + HauntedFengrafEffect() { super(Outcome.ReturnToHand); this.staticText = "Return a creature card at random from your graveyard to your hand"; } - public HauntedFengrafEffect(final HauntedFengrafEffect effect) { + private HauntedFengrafEffect(final HauntedFengrafEffect effect) { super(effect); } @@ -66,15 +67,13 @@ public HauntedFengrafEffect copy() { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - Card[] cards = player.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURE, game).toArray(new Card[0]); - if (cards.length > 0) { - Card card = cards[RandomUtil.nextInt(cards.length)]; - card.moveToZone(Zone.HAND, source, game, true); - game.informPlayers(card.getName() + " returned to the hand of " + player.getLogName()); - return true; - } + if (player == null || player.getGraveyard().count(StaticFilters.FILTER_CARD_CREATURE, game) < 1) { + return false; } - return false; + TargetCard target = new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE); + target.setNotTarget(true); + target.setRandom(true); + player.chooseTarget(outcome, target, source, game); + return player.moveCards(game.getCard(target.getFirstTarget()), Zone.HAND, source, game); } } diff --git a/Mage.Sets/src/mage/cards/h/HauntedPlateMail.java b/Mage.Sets/src/mage/cards/h/HauntedPlateMail.java index ce0b49416e63..f614277e4c2b 100644 --- a/Mage.Sets/src/mage/cards/h/HauntedPlateMail.java +++ b/Mage.Sets/src/mage/cards/h/HauntedPlateMail.java @@ -9,7 +9,7 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalActivatedAbility; import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; -import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; import mage.abilities.keyword.EquipAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -33,14 +33,14 @@ public HauntedPlateMail(UUID ownerId, CardSetInfo setInfo) { this.subtype.add(SubType.EQUIPMENT); // Equipped creature gets +4/+4. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(4, 4))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(4, 4))); // {0}: Until end of turn, Haunted Plate Mail becomes a 4/4 Spirit artifact creature that's no longer an Equipment. Activate this ability only if you control no creatures. Ability ability = new ConditionalActivatedAbility( Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new HauntedPlateMailToken(), "", Duration.EndOfTurn), new ManaCostsImpl("{0}"), new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_PERMANENT_CREATURE, ComparisonType.EQUAL_TO, 0), - "{0}: Until end of turn, Haunted Plate Mail becomes a 4/4 Spirit artifact creature that's no longer an Equipment. Activate this ability only if you control no creatures."); + "{0}: Until end of turn, Haunted Plate Mail becomes a 4/4 Spirit artifact creature that's no longer an Equipment. Activate only if you control no creatures."); this.addAbility(ability); // Equip {4} this.addAbility(new EquipAbility(Outcome.BoostCreature, new ManaCostsImpl("{4}"))); diff --git a/Mage.Sets/src/mage/cards/h/HauntingEchoes.java b/Mage.Sets/src/mage/cards/h/HauntingEchoes.java index 98aea73adc32..1fe9a2e261c5 100644 --- a/Mage.Sets/src/mage/cards/h/HauntingEchoes.java +++ b/Mage.Sets/src/mage/cards/h/HauntingEchoes.java @@ -2,21 +2,19 @@ import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; +import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.StaticFilters; -import mage.filter.predicate.mageobject.NamePredicate; +import mage.filter.predicate.Predicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; import mage.target.common.TargetCardInLibrary; import mage.util.CardUtil; -import java.util.List; +import java.util.Objects; import java.util.UUID; /** @@ -27,8 +25,8 @@ public final class HauntingEchoes extends CardImpl { public HauntingEchoes(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}{B}"); - this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addEffect(new HauntingEchoesEffect()); + this.getSpellAbility().addTarget(new TargetPlayer()); } private HauntingEchoes(final HauntingEchoes card) { @@ -43,50 +41,59 @@ public HauntingEchoes copy() { class HauntingEchoesEffect extends OneShotEffect { - public HauntingEchoesEffect() { + HauntingEchoesEffect() { super(Outcome.Detriment); - staticText = "Exile all cards from target player's graveyard other than basic land cards. For each card exiled this way, search that player's library for all cards with the same name as that card and exile them. Then that player shuffles their library"; + staticText = "Exile all cards from target player's graveyard other than basic land cards. " + + "For each card exiled this way, search that player's library for all cards " + + "with the same name as that card and exile them. Then that player shuffles"; } - public HauntingEchoesEffect(final HauntingEchoesEffect effect) { + private HauntingEchoesEffect(final HauntingEchoesEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - Player targetPlayer = game.getPlayer(source.getFirstTarget()); - if (targetPlayer != null && player != null) { - for (Card card : targetPlayer.getGraveyard().getCards(game)) { - if (!StaticFilters.FILTER_CARD_BASIC_LAND.match(card, game)) { - card.moveToExile(null, "", source, game); - - String nameToSearch = CardUtil.getCardNameForSameNameSearch(card); - FilterCard filterCard = new FilterCard("cards named " + nameToSearch); - filterCard.add(new NamePredicate(nameToSearch)); - - int count = targetPlayer.getLibrary().count(filterCard, game); - TargetCardInLibrary target = new TargetCardInLibrary(count, count, filterCard); - - player.searchLibrary(target, source, game, targetPlayer.getId()); - List targets = target.getTargets(); - for (UUID cardId : targets) { - Card libraryCard = game.getCard(cardId); - if (libraryCard != null) { - libraryCard.moveToExile(null, "", source, game); - } - } - } - } - targetPlayer.shuffleLibrary(source, game); - return true; + Player controller = game.getPlayer(source.getControllerId()); + Player player = game.getPlayer(source.getFirstTarget()); + if (controller == null || player == null) { + return false; } - return false; + Cards cards = new CardsImpl(); + player.getGraveyard() + .getCards(game) + .stream() + .filter(Objects::nonNull) + .filter(card -> !card.isBasic() || !card.isLand()) + .forEach(cards::add); + controller.moveCards(cards, Zone.EXILED, source, game); + cards.removeIf(uuid -> game.getState().getZone(uuid) != Zone.EXILED); + FilterCard filter = new FilterCard("cards with the same name as a card exiled this way"); + filter.add(new HauntingEchoesPredicate(cards)); + TargetCardInLibrary target = new TargetCardInLibrary(0, Integer.MAX_VALUE, filter); + controller.searchLibrary(target, source, game, player.getId()); + cards.clear(); + cards.addAll(target.getTargets()); + controller.moveCards(cards, Zone.EXILED, source, game); + player.shuffleLibrary(source, game); + return true; } @Override public HauntingEchoesEffect copy() { return new HauntingEchoesEffect(this); } +} +class HauntingEchoesPredicate implements Predicate { + private final Cards cards = new CardsImpl(); + + HauntingEchoesPredicate(Cards cards) { + this.cards.addAll(cards); + } + + @Override + public boolean apply(Card input, Game game) { + return cards.getCards(game).stream().anyMatch(card -> CardUtil.haveSameNames(input, card)); + } } diff --git a/Mage.Sets/src/mage/cards/h/HavengulSkaab.java b/Mage.Sets/src/mage/cards/h/HavengulSkaab.java index 41a49d63b1d0..ed0613210110 100644 --- a/Mage.Sets/src/mage/cards/h/HavengulSkaab.java +++ b/Mage.Sets/src/mage/cards/h/HavengulSkaab.java @@ -11,10 +11,9 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.target.common.TargetControlledCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/h/HazoretsUndyingFury.java b/Mage.Sets/src/mage/cards/h/HazoretsUndyingFury.java index e2b23ffb49b9..8a4fe475afca 100644 --- a/Mage.Sets/src/mage/cards/h/HazoretsUndyingFury.java +++ b/Mage.Sets/src/mage/cards/h/HazoretsUndyingFury.java @@ -9,7 +9,7 @@ import mage.filter.FilterCard; import mage.filter.common.FilterControlledLandPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.ExileZone; import mage.game.Game; import mage.players.Player; @@ -51,18 +51,18 @@ public HazoretsUndyingFury copy() { class HazoretsUndyingFuryEffect extends OneShotEffect { private static final FilterCard filter = new FilterCard( - "nonland cards with converted mana cost 5 or less"); + "nonland cards with mana value 5 or less"); static { filter.add(Predicates.not(CardType.LAND.getPredicate())); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 6)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 6)); } public HazoretsUndyingFuryEffect() { super(Outcome.PlayForFree); this.staticText = "Shuffle your library, then exile the top four cards. " - + "You may cast any number of nonland cards with converted mana " - + "cost 5 or less from among them without paying their mana costs"; + + "You may cast any number of nonland cards with mana value " + + "5 or less from among them without paying their mana costs"; } public HazoretsUndyingFuryEffect(final HazoretsUndyingFuryEffect effect) { diff --git a/Mage.Sets/src/mage/cards/h/HeadstrongBrute.java b/Mage.Sets/src/mage/cards/h/HeadstrongBrute.java index 37beaadabf9f..9dedfc41b47c 100644 --- a/Mage.Sets/src/mage/cards/h/HeadstrongBrute.java +++ b/Mage.Sets/src/mage/cards/h/HeadstrongBrute.java @@ -16,7 +16,7 @@ import mage.constants.Duration; import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/h/HealingTechnique.java b/Mage.Sets/src/mage/cards/h/HealingTechnique.java new file mode 100644 index 000000000000..2c7b81806546 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HealingTechnique.java @@ -0,0 +1,76 @@ +package mage.cards.h; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileSpellEffect; +import mage.abilities.keyword.DemonstrateAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HealingTechnique extends CardImpl { + + public HealingTechnique(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{G}"); + + + // Demonstrate + this.addAbility(new DemonstrateAbility()); + + // Return target card from your graveyard to your hand. You gain life equal to that card's mana value. Exile Healing Technique. + this.getSpellAbility().addEffect(new HealingTechniqueEffect()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard()); + } + + private HealingTechnique(final HealingTechnique card) { + super(card); + } + + @Override + public HealingTechnique copy() { + return new HealingTechnique(this); + } +} + +class HealingTechniqueEffect extends OneShotEffect { + + HealingTechniqueEffect() { + super(Outcome.Benefit); + staticText = "return target card from your graveyard to your hand. " + + "You gain life equal to that card's mana value"; + } + + private HealingTechniqueEffect(final HealingTechniqueEffect effect) { + super(effect); + } + + @Override + public HealingTechniqueEffect copy() { + return new HealingTechniqueEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(source.getFirstTarget()); + if (player == null || card == null) { + return false; + } + int manaValue = card.getManaValue(); + player.moveCards(card, Zone.HAND, source, game); + player.gainLife(manaValue, game, source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/h/HearthKami.java b/Mage.Sets/src/mage/cards/h/HearthKami.java index dd92ca799a60..33d4fb047b12 100644 --- a/Mage.Sets/src/mage/cards/h/HearthKami.java +++ b/Mage.Sets/src/mage/cards/h/HearthKami.java @@ -21,7 +21,7 @@ */ public final class HearthKami extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact with converted mana cost X"); + private static final FilterPermanent filter = new FilterPermanent("artifact with mana value X"); static { filter.add(CardType.ARTIFACT.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/h/HeatedDebate.java b/Mage.Sets/src/mage/cards/h/HeatedDebate.java new file mode 100644 index 000000000000..1d7d169afa7e --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HeatedDebate.java @@ -0,0 +1,40 @@ +package mage.cards.h; + +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.CantBeCounteredSourceEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.target.common.TargetCreatureOrPlaneswalker; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HeatedDebate extends CardImpl { + + public HeatedDebate(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); + + // This spell can't be countered. + this.addAbility(new SimpleStaticAbility(Zone.STACK, new CantBeCounteredSourceEffect() + .setText("This spell can't be countered. (This includes by the ward ability.)") + )); + + // Heated Debate deals 4 damage to target creature or planeswalker. + this.getSpellAbility().addEffect(new DamageTargetEffect(4)); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); + } + + private HeatedDebate(final HeatedDebate card) { + super(card); + } + + @Override + public HeatedDebate copy() { + return new HeatedDebate(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HeavenlyQilin.java b/Mage.Sets/src/mage/cards/h/HeavenlyQilin.java index 3eb61db0c126..47cca7c3eb52 100644 --- a/Mage.Sets/src/mage/cards/h/HeavenlyQilin.java +++ b/Mage.Sets/src/mage/cards/h/HeavenlyQilin.java @@ -12,7 +12,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/h/HedonistsTrove.java b/Mage.Sets/src/mage/cards/h/HedonistsTrove.java index 7bd3f9117a99..a101b8fd719d 100644 --- a/Mage.Sets/src/mage/cards/h/HedonistsTrove.java +++ b/Mage.Sets/src/mage/cards/h/HedonistsTrove.java @@ -1,7 +1,7 @@ - package mage.cards.h; import mage.MageObject; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; @@ -13,17 +13,17 @@ import mage.constants.*; import mage.game.ExileZone; import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetOpponent; import mage.util.CardUtil; +import mage.watchers.Watcher; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; +import java.util.*; /** - * - * @author LevelX2 + * @author TheElk801 */ public final class HedonistsTrove extends CardImpl { @@ -36,11 +36,10 @@ public HedonistsTrove(UUID ownerId, CardSetInfo setInfo) { this.addAbility(ability); // You may play land cards exiled by Hedonist's Trove. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new HedonistsTrovePlayLandEffect())); + this.addAbility(new SimpleStaticAbility(new HedonistsTrovePlayLandEffect())); // You may cast nonland cards exiled with Hedonist's Trove. You can't cast more than one spell this way each turn. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new HedonistsTroveCastNonlandCardsEffect())); - + this.addAbility(new SimpleStaticAbility(new HedonistsTroveCastNonlandCardsEffect()), new HedonistsTroveWatcher()); } private HedonistsTrove(final HedonistsTrove card) { @@ -55,44 +54,43 @@ public HedonistsTrove copy() { class HedonistsTroveExileEffect extends OneShotEffect { - public HedonistsTroveExileEffect() { + HedonistsTroveExileEffect() { super(Outcome.Exile); staticText = "exile all cards from target opponent's graveyard"; } + private HedonistsTroveExileEffect(final HedonistsTroveExileEffect effect) { + super(effect); + } + @Override public HedonistsTroveExileEffect copy() { - return new HedonistsTroveExileEffect(); + return new HedonistsTroveExileEffect(this); } @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Player targetPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source)); + Player targetPlayer = game.getPlayer(source.getFirstTarget()); MageObject sourceObject = source.getSourceObject(game); - if (controller != null && targetPlayer != null && sourceObject != null) { - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); - List graveyard = new ArrayList<>(targetPlayer.getGraveyard()); - for (UUID cardId : graveyard) { - Card card = game.getCard(cardId); - if (card != null) { - controller.moveCardToExileWithInfo(card, exileId, sourceObject.getIdName(), source, game, Zone.GRAVEYARD, true); - } - } - return true; - } - return false; + return controller != null + && targetPlayer != null + && sourceObject != null + && controller.moveCardsToExile( + targetPlayer.getGraveyard().getCards(game), source, game, true, + CardUtil.getExileZoneId(game, source), sourceObject.getIdName() + ); } } class HedonistsTrovePlayLandEffect extends AsThoughEffectImpl { - public HedonistsTrovePlayLandEffect() { + HedonistsTrovePlayLandEffect() { super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "You may play land cards exiled with {this}"; + staticText = "You may play lands from among cards exiled with {this}"; } - public HedonistsTrovePlayLandEffect(final HedonistsTrovePlayLandEffect effect) { + private HedonistsTrovePlayLandEffect(final HedonistsTrovePlayLandEffect effect) { super(effect); } @@ -108,35 +106,25 @@ public HedonistsTrovePlayLandEffect copy() { @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (affectedControllerId.equals(source.getControllerId())) { - Card card = game.getCard(objectId); - MageObject sourceObject = source.getSourceObject(game); - if (card != null && card.isLand() && sourceObject != null) { - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); - if (exileId != null) { - ExileZone exileZone = game.getState().getExile().getExileZone(exileId); - return exileZone != null && exileZone.contains(objectId); - } - } + Card cardToCheck = game.getCard(objectId); + if (cardToCheck == null || !cardToCheck.isLand() || !source.isControlledBy(affectedControllerId)) { + return false; } - return false; + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + return exileZone != null && exileZone.contains(cardToCheck.getMainCard()); } } class HedonistsTroveCastNonlandCardsEffect extends AsThoughEffectImpl { - private int turnNumber; - private UUID cardId; - - public HedonistsTroveCastNonlandCardsEffect() { + HedonistsTroveCastNonlandCardsEffect() { super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "You may cast nonland cards exiled with {this}. You can't cast more than one spell this way each turn"; + staticText = "You may cast spells from among cards exiled with {this}. " + + "You can't cast more than one spell this way each turn."; } - public HedonistsTroveCastNonlandCardsEffect(final HedonistsTroveCastNonlandCardsEffect effect) { + private HedonistsTroveCastNonlandCardsEffect(final HedonistsTroveCastNonlandCardsEffect effect) { super(effect); - this.turnNumber = effect.turnNumber; - this.cardId = effect.cardId; } @Override @@ -151,29 +139,52 @@ public HedonistsTroveCastNonlandCardsEffect copy() { @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (affectedControllerId.equals(source.getControllerId())) { - Card card = game.getCard(objectId); - MageObject sourceObject = source.getSourceObject(game); - if (card != null && !card.isLand() && sourceObject != null) { - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); - if (exileId != null) { - ExileZone exileZone = game.getState().getExile().getExileZone(exileId); - if (exileZone != null && exileZone.contains(objectId)) { - if (game.getTurnNum() == turnNumber) { - if (!exileZone.contains(cardId)) { - // last checked card this turn is no longer exiled, so you can't cast another with this effect - // TODO: Handle if card was cast/removed from exile with effect from another card. - // If so, this effect could prevent player from casting although they should be able to use it - return false; - } - } - this.turnNumber = game.getTurnNum(); - this.cardId = objectId; - return true; - } - } - } + HedonistsTroveWatcher watcher = game.getState().getWatcher(HedonistsTroveWatcher.class); + if (watcher == null || !watcher.checkPlayer(affectedControllerId, source, game)) { + return false; } - return false; + Card cardToCheck = game.getCard(objectId); + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + return cardToCheck != null + && !cardToCheck.isLand() + && source.isControlledBy(affectedControllerId) + && exileZone != null + && exileZone.contains(cardToCheck.getMainCard()); + } +} + +class HedonistsTroveWatcher extends Watcher { + + private static final Set emptySet = new HashSet<>(); + private final Map> playerMap = new HashMap<>(); + + HedonistsTroveWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.SPELL_CAST || event.getAdditionalReference() == null) { + return; + } + playerMap + .computeIfAbsent(event.getPlayerId(), x -> new HashSet<>()) + .add(event.getAdditionalReference().getApprovingMageObjectReference()); + playerMap.get(event.getPlayerId()).removeIf(Objects::isNull); + } + + boolean checkPlayer(UUID playerId, Ability source, Game game) { + Permanent permanent = source.getSourcePermanentOrLKI(game); + return permanent != null + && playerMap + .getOrDefault(playerId, emptySet) + .stream() + .noneMatch(mor -> mor.refersTo(permanent, game)); + } + + @Override + public void reset() { + super.reset(); + playerMap.clear(); } } diff --git a/Mage.Sets/src/mage/cards/h/HedronFieldPurists.java b/Mage.Sets/src/mage/cards/h/HedronFieldPurists.java index b99c6fe902bb..9e3bc541d4b1 100644 --- a/Mage.Sets/src/mage/cards/h/HedronFieldPurists.java +++ b/Mage.Sets/src/mage/cards/h/HedronFieldPurists.java @@ -88,9 +88,9 @@ public boolean applies(GameEvent event, Ability source, Game game) { return super.applies(event, source, game); } - if (event.getType() == GameEvent.EventType.DAMAGE_CREATURE) { + if (event.getType() == GameEvent.EventType.DAMAGE_PERMANENT) { Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.isControlledBy(source.getControllerId())) { + if (permanent != null && permanent.isCreature() && permanent.isControlledBy(source.getControllerId())) { return super.applies(event, source, game); } } diff --git a/Mage.Sets/src/mage/cards/h/HedronMatrix.java b/Mage.Sets/src/mage/cards/h/HedronMatrix.java index 4e93283e38d0..9ddf64852117 100644 --- a/Mage.Sets/src/mage/cards/h/HedronMatrix.java +++ b/Mage.Sets/src/mage/cards/h/HedronMatrix.java @@ -48,7 +48,7 @@ class HedronMatrixEffect extends ContinuousEffectImpl { public HedronMatrixEffect() { super(Duration.WhileOnBattlefield, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.BoostCreature); - staticText = "Equipped creature gets +X/+X, where X is its converted mana cost"; + staticText = "Equipped creature gets +X/+X, where X is its mana value"; } public HedronMatrixEffect(final HedronMatrixEffect effect) { @@ -66,8 +66,8 @@ public boolean apply(Game game, Ability source) { if (equipment != null && equipment.getAttachedTo() != null) { Permanent creature = game.getPermanent(equipment.getAttachedTo()); if (creature != null) { - creature.addPower(creature.getConvertedManaCost()); - creature.addToughness(creature.getConvertedManaCost()); + creature.addPower(creature.getManaValue()); + creature.addToughness(creature.getManaValue()); } } return true; diff --git a/Mage.Sets/src/mage/cards/h/HeedTheMists.java b/Mage.Sets/src/mage/cards/h/HeedTheMists.java index 0601a371dcc3..ff1e6d2b0397 100644 --- a/Mage.Sets/src/mage/cards/h/HeedTheMists.java +++ b/Mage.Sets/src/mage/cards/h/HeedTheMists.java @@ -40,7 +40,7 @@ private static class HeedTheMistsEffect extends OneShotEffect { public HeedTheMistsEffect() { super(Outcome.DrawCard); - staticText = "Mill a card, then draw cards equal to that card's converted mana cost"; + staticText = "Mill a card, then draw cards equal to that card's mana value"; } public HeedTheMistsEffect(HeedTheMistsEffect effect) { @@ -55,7 +55,7 @@ public boolean apply(Game game, Ability source) { .millCards(1, source, game) .getCards(game) .stream() - .mapToInt(MageObject::getConvertedManaCost) + .mapToInt(MageObject::getManaValue) .sum(); controller.drawCards(totalCMC, source, game); return true; diff --git a/Mage.Sets/src/mage/cards/h/HeliodSunCrowned.java b/Mage.Sets/src/mage/cards/h/HeliodSunCrowned.java index 10a2e80d5c84..8991ab0a0aaf 100644 --- a/Mage.Sets/src/mage/cards/h/HeliodSunCrowned.java +++ b/Mage.Sets/src/mage/cards/h/HeliodSunCrowned.java @@ -23,7 +23,7 @@ import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/h/HeliodsPilgrim.java b/Mage.Sets/src/mage/cards/h/HeliodsPilgrim.java index 7bbb6d6ac0ba..74ea14fa65de 100644 --- a/Mage.Sets/src/mage/cards/h/HeliodsPilgrim.java +++ b/Mage.Sets/src/mage/cards/h/HeliodsPilgrim.java @@ -18,7 +18,7 @@ */ public final class HeliodsPilgrim extends CardImpl { - private static final FilterCard filter = new FilterCard("Aura card"); + private static final FilterCard filter = new FilterCard("an Aura card"); static { filter.add(CardType.ENCHANTMENT.getPredicate()); @@ -34,7 +34,7 @@ public HeliodsPilgrim(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(2); // When Heliod's Pilgrim enters the battlefield, you may search your library for an Aura card, reveal it, put it into your hand, then shuffle your library. - this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true, false), true)); + this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true, true), true)); } diff --git a/Mage.Sets/src/mage/cards/h/HellholeRats.java b/Mage.Sets/src/mage/cards/h/HellholeRats.java index a7b28885847c..962781ea5102 100644 --- a/Mage.Sets/src/mage/cards/h/HellholeRats.java +++ b/Mage.Sets/src/mage/cards/h/HellholeRats.java @@ -53,7 +53,7 @@ class HellholeRatsEffect extends OneShotEffect { public HellholeRatsEffect() { super(Outcome.Damage); - this.staticText = "target player discards a card. {this} deals damage to that player equal to that card's converted mana cost"; + this.staticText = "target player discards a card. {this} deals damage to that player equal to that card's mana value"; } public HellholeRatsEffect(final HellholeRatsEffect effect) { @@ -73,7 +73,7 @@ public boolean apply(Game game, Ability source) { Cards cards = targetPlayer.discard(1, false, false, source, game); if (!cards.isEmpty()) { for (Card card : cards.getCards(game)) { - damage = card.getConvertedManaCost(); + damage = card.getManaValue(); } targetPlayer.damage(damage, source.getSourceId(), source, game); } diff --git a/Mage.Sets/src/mage/cards/h/HellkiteCourser.java b/Mage.Sets/src/mage/cards/h/HellkiteCourser.java index 5b5d8ba8fad2..a0948290de02 100644 --- a/Mage.Sets/src/mage/cards/h/HellkiteCourser.java +++ b/Mage.Sets/src/mage/cards/h/HellkiteCourser.java @@ -14,7 +14,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterCard; -import mage.filter.predicate.permanent.CommanderPredicate; +import mage.filter.predicate.mageobject.CommanderPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/h/HeraldOfDromoka.java b/Mage.Sets/src/mage/cards/h/HeraldOfDromoka.java index ec8758ec2988..3d9ec22cb205 100644 --- a/Mage.Sets/src/mage/cards/h/HeraldOfDromoka.java +++ b/Mage.Sets/src/mage/cards/h/HeraldOfDromoka.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/h/HeraldOfLeshrac.java b/Mage.Sets/src/mage/cards/h/HeraldOfLeshrac.java index ffbe38422681..3e22e1bc98dc 100644 --- a/Mage.Sets/src/mage/cards/h/HeraldOfLeshrac.java +++ b/Mage.Sets/src/mage/cards/h/HeraldOfLeshrac.java @@ -19,7 +19,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledLandPermanent; import mage.filter.common.FilterLandPermanent; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/h/HeraldOfTheSun.java b/Mage.Sets/src/mage/cards/h/HeraldOfTheSun.java index 3c3975e8bad8..784aa47c7211 100644 --- a/Mage.Sets/src/mage/cards/h/HeraldOfTheSun.java +++ b/Mage.Sets/src/mage/cards/h/HeraldOfTheSun.java @@ -14,7 +14,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/h/HerdGnarr.java b/Mage.Sets/src/mage/cards/h/HerdGnarr.java index a125ce2f9810..a966e80e38fb 100644 --- a/Mage.Sets/src/mage/cards/h/HerdGnarr.java +++ b/Mage.Sets/src/mage/cards/h/HerdGnarr.java @@ -12,7 +12,7 @@ import mage.constants.Duration; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/h/HerdchaserDragon.java b/Mage.Sets/src/mage/cards/h/HerdchaserDragon.java index 6d0720bd38ab..7378bfdfeba6 100644 --- a/Mage.Sets/src/mage/cards/h/HerdchaserDragon.java +++ b/Mage.Sets/src/mage/cards/h/HerdchaserDragon.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/h/HereticsPunishment.java b/Mage.Sets/src/mage/cards/h/HereticsPunishment.java index 23d7442e3e2a..8736fcebb19c 100644 --- a/Mage.Sets/src/mage/cards/h/HereticsPunishment.java +++ b/Mage.Sets/src/mage/cards/h/HereticsPunishment.java @@ -45,7 +45,7 @@ class HereticsPunishmentEffect extends OneShotEffect { public HereticsPunishmentEffect() { super(Outcome.Damage); - staticText = "Choose any target, then mill three cards. {this} deals damage to that permanent or player equal to the highest converted mana cost among the milled cards"; + staticText = "Choose any target, then mill three cards. {this} deals damage to that permanent or player equal to the highest mana value among the milled cards"; } public HereticsPunishmentEffect(final HereticsPunishmentEffect effect) { @@ -63,7 +63,7 @@ public boolean apply(Game game, Ability source) { .getCards(game) .stream() .filter(Objects::nonNull) - .mapToInt(MageObject::getConvertedManaCost) + .mapToInt(MageObject::getManaValue) .max() .orElse(0); Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source)); diff --git a/Mage.Sets/src/mage/cards/h/HermitOfTheNatterknolls.java b/Mage.Sets/src/mage/cards/h/HermitOfTheNatterknolls.java index 7c1da1c1c214..5b2becc29b48 100644 --- a/Mage.Sets/src/mage/cards/h/HermitOfTheNatterknolls.java +++ b/Mage.Sets/src/mage/cards/h/HermitOfTheNatterknolls.java @@ -1,23 +1,19 @@ package mage.cards.h; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SpellCastOpponentTriggeredAbility; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.condition.common.MyTurnCondition; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.hint.common.MyTurnHint; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import java.util.UUID; @@ -38,15 +34,14 @@ public HermitOfTheNatterknolls(UUID ownerId, CardSetInfo setInfo) { // Whenever an opponent casts a spell during your turn, draw a card. this.addAbility(new ConditionalTriggeredAbility( - new SpellCastOpponentTriggeredAbility(new DrawCardSourceControllerEffect(1), new FilterSpell("a spell"), true), + new SpellCastOpponentTriggeredAbility(new DrawCardSourceControllerEffect(1), StaticFilters.FILTER_SPELL_A, true), MyTurnCondition.instance, "Whenever an opponent casts a spell during your turn, draw a card." ).addHint(MyTurnHint.instance)); // At the beginning of each upkeep, if no spells were cast last turn, transform Hermit of the Natterknolls. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private HermitOfTheNatterknolls(final HermitOfTheNatterknolls card) { diff --git a/Mage.Sets/src/mage/cards/h/Hesitation.java b/Mage.Sets/src/mage/cards/h/Hesitation.java index d4d91bf2647b..8bda0f713177 100644 --- a/Mage.Sets/src/mage/cards/h/Hesitation.java +++ b/Mage.Sets/src/mage/cards/h/Hesitation.java @@ -12,6 +12,7 @@ import mage.constants.CardType; import mage.constants.SetTargetPointer; import mage.filter.FilterSpell; +import mage.filter.StaticFilters; /** * @@ -23,7 +24,7 @@ public Hesitation(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{U}"); // When a player casts a spell, sacrifice Hesitation and counter that spell. - Ability ability = new SpellCastAllTriggeredAbility(new SacrificeSourceEffect(), new FilterSpell("a spell"), false, SetTargetPointer.SPELL); + Ability ability = new SpellCastAllTriggeredAbility(new SacrificeSourceEffect(), StaticFilters.FILTER_SPELL_A, false, SetTargetPointer.SPELL); Effect effect = new CounterTargetEffect(); effect.setText("and counter that spell"); ability.addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/h/HibernationsEnd.java b/Mage.Sets/src/mage/cards/h/HibernationsEnd.java index a4a4b7ebac21..b2edd626124f 100644 --- a/Mage.Sets/src/mage/cards/h/HibernationsEnd.java +++ b/Mage.Sets/src/mage/cards/h/HibernationsEnd.java @@ -1,7 +1,6 @@ package mage.cards.h; -import java.util.Objects; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; @@ -16,10 +15,9 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInLibrary; @@ -84,7 +82,7 @@ class HibernationsEndEffect extends OneShotEffect { public HibernationsEndEffect() { super(Outcome.Benefit); - this.staticText = "search your library for a creature card with converted mana cost equal to the number of age counters on {this} and put it onto the battlefield. If you do, shuffle your library."; + this.staticText = "search your library for a creature card with mana value equal to the number of age counters on {this} and put it onto the battlefield. If you do, shuffle."; } public HibernationsEndEffect(final HibernationsEndEffect effect) { @@ -102,8 +100,8 @@ public boolean apply(Game game, Ability source) { Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (sourcePermanent != null && player != null) { int newConvertedCost = sourcePermanent.getCounters(game).getCount("age"); - FilterCard filter = new FilterCard("creature card with converted mana cost " + newConvertedCost); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, newConvertedCost)); + FilterCard filter = new FilterCard("creature card with mana value " + newConvertedCost); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, newConvertedCost)); filter.add(CardType.CREATURE.getPredicate()); TargetCardInLibrary target = new TargetCardInLibrary(filter); return new SearchLibraryPutInPlayEffect(target).apply(game, source); diff --git a/Mage.Sets/src/mage/cards/h/HiddenPredators.java b/Mage.Sets/src/mage/cards/h/HiddenPredators.java index 1b12439e2f02..238b72434fda 100644 --- a/Mage.Sets/src/mage/cards/h/HiddenPredators.java +++ b/Mage.Sets/src/mage/cards/h/HiddenPredators.java @@ -78,10 +78,10 @@ public boolean canTrigger(Game game) { } @Override - public void trigger(Game game, UUID controllerId) { + public void trigger(Game game, UUID controllerId, GameEvent triggeringEvent) { //20100716 - 603.8 game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.TRUE); - super.trigger(game, controllerId); + super.trigger(game, controllerId, triggeringEvent); } @Override diff --git a/Mage.Sets/src/mage/cards/h/HideSeek.java b/Mage.Sets/src/mage/cards/h/HideSeek.java index c90aa13f8e40..632d508671b0 100644 --- a/Mage.Sets/src/mage/cards/h/HideSeek.java +++ b/Mage.Sets/src/mage/cards/h/HideSeek.java @@ -55,7 +55,7 @@ class SeekEffect extends OneShotEffect { public SeekEffect() { super(Outcome.GainLife); - staticText = "Search target opponent's library for a card and exile it. You gain life equal to its converted mana cost. Then that player shuffles their library"; + staticText = "Search target opponent's library for a card and exile it. You gain life equal to its mana value. Then that player shuffles"; } public SeekEffect(final SeekEffect effect) { @@ -79,7 +79,7 @@ public boolean apply(Game game, Ability source) { Card card = opponent.getLibrary().remove(targetId, game); if (card != null) { player.moveCardToExileWithInfo(card, null, null, source, game, Zone.LIBRARY, true); - int cmc = card.getConvertedManaCost(); + int cmc = card.getManaValue(); if (cmc > 0) { player.gainLife(cmc, game, source); } diff --git a/Mage.Sets/src/mage/cards/h/HighPriestOfPenance.java b/Mage.Sets/src/mage/cards/h/HighPriestOfPenance.java index ee90375f11e0..740ab3f56873 100644 --- a/Mage.Sets/src/mage/cards/h/HighPriestOfPenance.java +++ b/Mage.Sets/src/mage/cards/h/HighPriestOfPenance.java @@ -60,7 +60,7 @@ public HighPriestOfPenanceTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/h/HighSentinelsOfArashin.java b/Mage.Sets/src/mage/cards/h/HighSentinelsOfArashin.java index 6b7f7243a982..6c5681f93b9d 100644 --- a/Mage.Sets/src/mage/cards/h/HighSentinelsOfArashin.java +++ b/Mage.Sets/src/mage/cards/h/HighSentinelsOfArashin.java @@ -20,7 +20,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/h/HinterlandDrake.java b/Mage.Sets/src/mage/cards/h/HinterlandDrake.java index 81d09b577e76..df9ac61ec051 100644 --- a/Mage.Sets/src/mage/cards/h/HinterlandDrake.java +++ b/Mage.Sets/src/mage/cards/h/HinterlandDrake.java @@ -31,7 +31,7 @@ public HinterlandDrake(UUID ownerId, CardSetInfo setInfo) { this.addAbility(FlyingAbility.getInstance()); // Hinterland Drake can't block artifact creatures. - Effect effect = new CantBlockCreaturesSourceEffect(StaticFilters.FILTER_ARTIFACT_CREATURE_PERMANENT); + Effect effect = new CantBlockCreaturesSourceEffect(StaticFilters.FILTER_PERMANENT_ARTIFACT_CREATURE); effect.setText("{this} can't block artifact creatures"); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); } diff --git a/Mage.Sets/src/mage/cards/h/HinterlandHermit.java b/Mage.Sets/src/mage/cards/h/HinterlandHermit.java index 8e05eb12be5a..f1d428877679 100644 --- a/Mage.Sets/src/mage/cards/h/HinterlandHermit.java +++ b/Mage.Sets/src/mage/cards/h/HinterlandHermit.java @@ -1,28 +1,22 @@ - package mage.cards.h; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** - * * @author BetaSteward */ public final class HinterlandHermit extends CardImpl { public HinterlandHermit(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WEREWOLF); @@ -34,8 +28,7 @@ public HinterlandHermit(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each upkeep, if no spells were cast last turn, transform Hinterland Hermit. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private HinterlandHermit(final HinterlandHermit card) { diff --git a/Mage.Sets/src/mage/cards/h/HinterlandLogger.java b/Mage.Sets/src/mage/cards/h/HinterlandLogger.java index e9163581ee4f..a8e629532274 100644 --- a/Mage.Sets/src/mage/cards/h/HinterlandLogger.java +++ b/Mage.Sets/src/mage/cards/h/HinterlandLogger.java @@ -1,20 +1,14 @@ - package mage.cards.h; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** * @author fireshoes @@ -33,8 +27,7 @@ public HinterlandLogger(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each upkeep, if no spells were cast last turn, transform Hinterland Logger. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private HinterlandLogger(final HinterlandLogger card) { diff --git a/Mage.Sets/src/mage/cards/h/HinterlandScourge.java b/Mage.Sets/src/mage/cards/h/HinterlandScourge.java index 1ebd9cb81b1d..155deff8108c 100644 --- a/Mage.Sets/src/mage/cards/h/HinterlandScourge.java +++ b/Mage.Sets/src/mage/cards/h/HinterlandScourge.java @@ -1,32 +1,25 @@ - package mage.cards.h; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.effects.common.combat.MustBeBlockedByAtLeastOneSourceEffect; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.TargetController; import mage.constants.Zone; +import java.util.UUID; + /** - * * @author BetaSteward */ public final class HinterlandScourge extends CardImpl { public HinterlandScourge(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.WEREWOLF); this.color.setRed(true); @@ -41,8 +34,7 @@ public HinterlandScourge(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MustBeBlockedByAtLeastOneSourceEffect(Duration.WhileOnBattlefield))); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Hinterland Scourge. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private HinterlandScourge(final HinterlandScourge card) { diff --git a/Mage.Sets/src/mage/cards/h/HiredGiant.java b/Mage.Sets/src/mage/cards/h/HiredGiant.java index 291a3eae1809..050baa467171 100644 --- a/Mage.Sets/src/mage/cards/h/HiredGiant.java +++ b/Mage.Sets/src/mage/cards/h/HiredGiant.java @@ -50,7 +50,7 @@ class HiredGiantEffect extends OneShotEffect { HiredGiantEffect() { super(Outcome.Detriment); - this.staticText = "each other player may search their library for a land card and put that card onto the battlefield. Then each player who searched their library this way shuffles it"; + this.staticText = "each other player may search their library for a land card and put that card onto the battlefield. Then each player who searched their library this way shuffles"; } HiredGiantEffect(final HiredGiantEffect effect) { diff --git a/Mage.Sets/src/mage/cards/h/HisokaMinamoSensei.java b/Mage.Sets/src/mage/cards/h/HisokaMinamoSensei.java index 4d356f937072..013d99f54d2d 100644 --- a/Mage.Sets/src/mage/cards/h/HisokaMinamoSensei.java +++ b/Mage.Sets/src/mage/cards/h/HisokaMinamoSensei.java @@ -108,7 +108,7 @@ public Card getDiscardedCard() { class HisokaMinamoSenseiCounterEffect extends OneShotEffect { HisokaMinamoSenseiCounterEffect() { super(Outcome.Detriment); - staticText = "Counter target spell if it has the same converted mana cost as the discarded card"; + staticText = "Counter target spell if it has the same mana value as the discarded card"; } HisokaMinamoSenseiCounterEffect(final HisokaMinamoSenseiCounterEffect effect) { @@ -120,7 +120,7 @@ public boolean apply(Game game, Ability source) { Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); if (spell != null) { HisokaMinamoSenseiDiscardTargetCost cost = (HisokaMinamoSenseiDiscardTargetCost) source.getCosts().get(0); - if (cost != null && cost.getDiscardedCard().getConvertedManaCost() == spell.getConvertedManaCost()) { + if (cost != null && cost.getDiscardedCard().getManaValue() == spell.getManaValue()) { return game.getStack().counter(targetPointer.getFirst(game, source), source, game); } } diff --git a/Mage.Sets/src/mage/cards/h/HisokasGuard.java b/Mage.Sets/src/mage/cards/h/HisokasGuard.java index 77872c91ca5e..c6769aafd081 100644 --- a/Mage.Sets/src/mage/cards/h/HisokasGuard.java +++ b/Mage.Sets/src/mage/cards/h/HisokasGuard.java @@ -20,7 +20,7 @@ import mage.constants.SubLayer; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.Target; diff --git a/Mage.Sets/src/mage/cards/h/HitRun.java b/Mage.Sets/src/mage/cards/h/HitRun.java index 4d676b8add3f..33924e502d9a 100644 --- a/Mage.Sets/src/mage/cards/h/HitRun.java +++ b/Mage.Sets/src/mage/cards/h/HitRun.java @@ -55,7 +55,7 @@ class HitEffect extends OneShotEffect { public HitEffect() { super(Outcome.DestroyPermanent); - this.staticText = "Target player sacrifices an artifact or creature. Hit deals damage to that player equal to that permanent's converted mana cost"; + this.staticText = "Target player sacrifices an artifact or creature. Hit deals damage to that player equal to that permanent's mana value"; } public HitEffect(final HitEffect effect) { @@ -82,7 +82,7 @@ public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(target.getFirstTarget()); if (permanent != null) { permanent.sacrifice(source, game); - int damage = permanent.getConvertedManaCost(); + int damage = permanent.getManaValue(); if (damage > 0) { targetPlayer.damage(damage, source.getSourceId(), source, game); } diff --git a/Mage.Sets/src/mage/cards/h/HoardSmelterDragon.java b/Mage.Sets/src/mage/cards/h/HoardSmelterDragon.java index cb6a49e05554..96b903d0d728 100644 --- a/Mage.Sets/src/mage/cards/h/HoardSmelterDragon.java +++ b/Mage.Sets/src/mage/cards/h/HoardSmelterDragon.java @@ -58,7 +58,7 @@ class HoardSmelterEffect extends ContinuousEffectImpl { HoardSmelterEffect() { super(Duration.EndOfTurn, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.BoostCreature); - staticText = "{this} gets +X/+0 until end of turn, where X is that artifact's converted mana cost"; + staticText = "{this} gets +X/+0 until end of turn, where X is that artifact's mana value"; } HoardSmelterEffect(final HoardSmelterEffect effect) { @@ -80,7 +80,7 @@ public boolean apply(Game game, Ability source) { public void init(Ability source, Game game) { Card targeted = game.getCard(source.getFirstTarget()); if (targeted != null) { - costValue = targeted.getConvertedManaCost(); + costValue = targeted.getManaValue(); } } diff --git a/Mage.Sets/src/mage/cards/h/HoardingDragon.java b/Mage.Sets/src/mage/cards/h/HoardingDragon.java index 8fc7a81bd6f9..68229a6ade45 100644 --- a/Mage.Sets/src/mage/cards/h/HoardingDragon.java +++ b/Mage.Sets/src/mage/cards/h/HoardingDragon.java @@ -1,8 +1,5 @@ - - package mage.cards.h; -import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -15,24 +12,24 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.common.FilterArtifactCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; +import mage.util.CardUtil; - +import java.util.UUID; /** - * * @author BetaSteward_at_googlemail.com */ public final class HoardingDragon extends CardImpl { public HoardingDragon(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}"); this.subtype.add(SubType.DRAGON); this.power = new MageInt(4); @@ -41,10 +38,11 @@ public HoardingDragon(UUID ownerId, CardSetInfo setInfo) { this.addAbility(FlyingAbility.getInstance()); // When Hoarding Dragon enters the battlefield, you may search your library for an artifact card, exile it, then shuffle your library. - this.addAbility(new EntersBattlefieldTriggeredAbility(new HoardingDragonEffect(this.getId()), true)); + this.addAbility(new EntersBattlefieldTriggeredAbility(new HoardingDragonEffect(), true)); // When Hoarding Dragon dies, you may put the exiled card into its owner's hand. - this.addAbility(new DiesSourceTriggeredAbility(new ReturnFromExileEffect(this.getId(), Zone.HAND), false)); + this.addAbility(new DiesSourceTriggeredAbility(new ReturnFromExileEffect(Zone.HAND) + .setText("put the exiled card into its owner's hand"), true)); } private HoardingDragon(final HoardingDragon card) { @@ -60,42 +58,37 @@ public HoardingDragon copy() { class HoardingDragonEffect extends OneShotEffect { - private final UUID exileId; - - public HoardingDragonEffect(UUID exileId) { + HoardingDragonEffect() { super(Outcome.Exile); - this.exileId = exileId; - this.staticText = "you may search your library for an artifact card, exile it, then shuffle your library"; + this.staticText = "search your library for an artifact card, exile it, then shuffle"; } - public HoardingDragonEffect(final HoardingDragonEffect effect) { + private HoardingDragonEffect(final HoardingDragonEffect effect) { super(effect); - this.exileId = effect.exileId; } @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = source.getSourceObject(game); - if (controller != null && sourceObject != null) { - TargetCardInLibrary target = new TargetCardInLibrary(new FilterArtifactCard()); - if (controller.searchLibrary(target, source, game)) { - if (!target.getTargets().isEmpty()) { - Card card = controller.getLibrary().getCard(target.getFirstTarget(), game); - if (card != null) { - controller.moveCardToExileWithInfo(card, exileId, sourceObject.getIdName(), source, game, Zone.LIBRARY, true); - } - } - } - controller.shuffleLibrary(source, game); - return true; - } - return false; + if (controller == null || sourceObject == null) { + return false; + } + TargetCardInLibrary target = new TargetCardInLibrary(StaticFilters.FILTER_CARD_ARTIFACT); + controller.searchLibrary(target, source, game); + Card card = controller.getLibrary().getCard(target.getFirstTarget(), game); + if (card != null) { + controller.moveCardsToExile( + card, source, game, true, + CardUtil.getExileZoneId(game, source), sourceObject.getIdName() + ); + } + controller.shuffleLibrary(source, game); + return true; } @Override public HoardingDragonEffect copy() { return new HoardingDragonEffect(this); } - } diff --git a/Mage.Sets/src/mage/cards/h/HofriGhostforge.java b/Mage.Sets/src/mage/cards/h/HofriGhostforge.java new file mode 100644 index 000000000000..7dcf7cf8a804 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HofriGhostforge.java @@ -0,0 +1,147 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.CompoundAbility; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.ZoneChangeTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HofriGhostforge extends CardImpl { + + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent(SubType.SPIRIT, "Spirits"); + private static final FilterPermanent filter2 + = new FilterPermanent(SubType.SPIRIT, ""); + private static final FilterPermanent filter3 + = new FilterControlledCreaturePermanent("another nontoken creature you control"); + + static { + filter3.add(Predicates.not(TokenPredicate.instance)); + } + + public HofriGhostforge(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.DWARF); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(4); + this.toughness = new MageInt(5); + + // Spirits you control get +1/+1 and have trample and haste. + Ability ability = new SimpleStaticAbility(new BoostControlledEffect( + 1, 1, Duration.WhileOnBattlefield, filter + )); + ability.addEffect(new GainAbilityControlledEffect( + new CompoundAbility( + TrampleAbility.getInstance(), HasteAbility.getInstance() + ), Duration.WhileOnBattlefield, filter2 + ).setText("and have trample and haste")); + this.addAbility(ability); + + // Whenever another nontoken creature you control dies, exile it. If you do, create a token that's a copy of that creature, except it's a Spirit in addition to its other types and it has "When this creature leaves the battlefield, return the exiled card to your graveyard." + this.addAbility(new DiesCreatureTriggeredAbility( + new HofriGhostforgeEffect(), false, filter3, true + )); + } + + private HofriGhostforge(final HofriGhostforge card) { + super(card); + } + + @Override + public HofriGhostforge copy() { + return new HofriGhostforge(this); + } +} + +class HofriGhostforgeEffect extends OneShotEffect { + + HofriGhostforgeEffect() { + super(Outcome.Benefit); + staticText = "exile it. If you do, create a token that's a copy of that creature, " + + "except it's a Spirit in addition to its other types and it has " + + "\"When this creature leaves the battlefield, return the exiled card to its owner's graveyard.\""; + } + + private HofriGhostforgeEffect(final HofriGhostforgeEffect effect) { + super(effect); + } + + @Override + public HofriGhostforgeEffect copy() { + return new HofriGhostforgeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(targetPointer.getFirst(game, source)); + if (player == null || card == null) { + return false; + } + player.moveCards(card, Zone.EXILED, source, game); + CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId()); + effect.setTargetPointer(new FixedTarget(card, game)); + effect.setAdditionalSubType(SubType.SPIRIT); + effect.addAdditionalAbilities(new ZoneChangeTriggeredAbility( + Zone.ALL, Zone.BATTLEFIELD, null, new HofriGhostforgeReturnEffect(card, game), + "When this creature leaves the battlefield, ", false + )); + effect.apply(game, source); + return true; + } +} + +class HofriGhostforgeReturnEffect extends OneShotEffect { + + private final MageObjectReference mor; + + HofriGhostforgeReturnEffect(Card card, Game game) { + super(Outcome.Benefit); + this.mor = new MageObjectReference(card, game); + staticText = "return the exiled card to its owner's graveyard"; + } + + private HofriGhostforgeReturnEffect(final HofriGhostforgeReturnEffect effect) { + super(effect); + this.mor = effect.mor; + } + + @Override + public HofriGhostforgeReturnEffect copy() { + return new HofriGhostforgeReturnEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = mor.getCard(game); + return player != null && card != null && player.moveCards(card, Zone.GRAVEYARD, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HogaakArisenNecropolis.java b/Mage.Sets/src/mage/cards/h/HogaakArisenNecropolis.java index 5aa89d986601..72cb7f4dbf66 100644 --- a/Mage.Sets/src/mage/cards/h/HogaakArisenNecropolis.java +++ b/Mage.Sets/src/mage/cards/h/HogaakArisenNecropolis.java @@ -13,7 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import java.util.UUID; @@ -26,7 +26,7 @@ public final class HogaakArisenNecropolis extends CardImpl { private static final FilterPermanent filter = new FilterPermanent(); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, -1)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, -1)); } public HogaakArisenNecropolis(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/h/HollowOne.java b/Mage.Sets/src/mage/cards/h/HollowOne.java index 0dfbd1af58d6..8ba2e0b81cf5 100644 --- a/Mage.Sets/src/mage/cards/h/HollowOne.java +++ b/Mage.Sets/src/mage/cards/h/HollowOne.java @@ -57,7 +57,7 @@ class HollowOneReductionEffect extends CostModificationEffectImpl { public HollowOneReductionEffect() { super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "{this} costs {2} less to cast for each card you've cycled or discarded this turn"; + staticText = "this spell costs {2} less to cast for each card you've cycled or discarded this turn"; } protected HollowOneReductionEffect(HollowOneReductionEffect effect) { diff --git a/Mage.Sets/src/mage/cards/h/HolyJusticiar.java b/Mage.Sets/src/mage/cards/h/HolyJusticiar.java index fbdf88a701c5..f0e7330b925f 100644 --- a/Mage.Sets/src/mage/cards/h/HolyJusticiar.java +++ b/Mage.Sets/src/mage/cards/h/HolyJusticiar.java @@ -1,7 +1,5 @@ - package mage.cards.h; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -16,16 +14,18 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class HolyJusticiar extends CardImpl { public HolyJusticiar(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.CLERIC); @@ -33,11 +33,10 @@ public HolyJusticiar(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(1); // {2}{W}, {tap}: Tap target creature. If that creature is a Zombie, exile it. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new HolyJusticiarEffect(), new ManaCostsImpl("{2}{W}")); + Ability ability = new SimpleActivatedAbility(new HolyJusticiarEffect(), new ManaCostsImpl("{2}{W}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); - } private HolyJusticiar(final HolyJusticiar card) { @@ -52,28 +51,27 @@ public HolyJusticiar copy() { class HolyJusticiarEffect extends OneShotEffect { - public HolyJusticiarEffect() { + HolyJusticiarEffect() { super(Outcome.Detriment); - staticText = "Tap target creature. If that creature is a Zombie, exile it"; + staticText = "Tap target creature. If that creature is a Zombie, exile it"; } - public HolyJusticiarEffect(final HolyJusticiarEffect effect) { + private HolyJusticiarEffect(final HolyJusticiarEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); Permanent creature = game.getPermanent(source.getFirstTarget()); - if (creature != null) { - if (creature.hasSubtype(SubType.ZOMBIE, game)) { - creature.tap(source, game); - creature.moveToExile(source.getSourceId(), creature.getName(), source, game); - } else { - creature.tap(source, game); - } - return true; + if (player == null || creature == null) { + return false; + } + creature.tap(source, game); + if (creature.hasSubtype(SubType.ZOMBIE, game)) { + player.moveCards(creature, Zone.EXILED, source, game); } - return false; + return true; } @Override @@ -81,4 +79,3 @@ public HolyJusticiarEffect copy() { return new HolyJusticiarEffect(this); } } - diff --git a/Mage.Sets/src/mage/cards/h/HomewardPath.java b/Mage.Sets/src/mage/cards/h/HomewardPath.java index bf3dd475eef2..464a5a49812e 100644 --- a/Mage.Sets/src/mage/cards/h/HomewardPath.java +++ b/Mage.Sets/src/mage/cards/h/HomewardPath.java @@ -19,7 +19,7 @@ import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/h/HonorTheFallen.java b/Mage.Sets/src/mage/cards/h/HonorTheFallen.java index 855afa5cb4a3..f565f3250eef 100644 --- a/Mage.Sets/src/mage/cards/h/HonorTheFallen.java +++ b/Mage.Sets/src/mage/cards/h/HonorTheFallen.java @@ -1,20 +1,21 @@ package mage.cards.h; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.Zone; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreatureCard; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author L_J */ public final class HonorTheFallen extends CardImpl { @@ -34,48 +35,46 @@ private HonorTheFallen(final HonorTheFallen card) { public HonorTheFallen copy() { return new HonorTheFallen(this); } - } class HonorTheFallenEffect extends OneShotEffect { - private static final FilterCreatureCard filter = new FilterCreatureCard(); - - public HonorTheFallenEffect() { + HonorTheFallenEffect() { super(Outcome.Detriment); staticText = "Exile all creature cards from all graveyards. You gain 1 life for each card exiled this way"; } - public HonorTheFallenEffect(final HonorTheFallenEffect effect) { + private HonorTheFallenEffect(final HonorTheFallenEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - int exiledCards = 0; - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - for (Card card : player.getGraveyard().getCards(game)) { - if (StaticFilters.FILTER_CARD_CREATURE.match(card, source.getSourceId(), controller.getId(), game)) { - if (card.moveToExile(null, "", source, game)) { - exiledCards++; - } - } - } - } + if (controller == null) { + return false; + } + Cards cards = new CardsImpl(); + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; } - controller.gainLife(exiledCards, game, source); - return true; + cards.addAll(player.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURE, game)); } - return false; + controller.moveCards(cards, Zone.EXILED, source, game); + int count = cards + .stream() + .map(game.getState()::getZone) + .map(Zone.EXILED::equals) + .mapToInt(x -> x ? 1 : 0) + .sum(); + controller.gainLife(count, game, source); + return true; } @Override public HonorTheFallenEffect copy() { return new HonorTheFallenEffect(this); } - } diff --git a/Mage.Sets/src/mage/cards/h/HonorTroll.java b/Mage.Sets/src/mage/cards/h/HonorTroll.java new file mode 100644 index 000000000000..f905bc7eddd2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HonorTroll.java @@ -0,0 +1,105 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.VigilanceAbility; +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.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HonorTroll extends CardImpl { + + public HonorTroll(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.TROLL); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // If you would gain life, you gain that much life plus 1 instead. + this.addAbility(new SimpleStaticAbility(new HonorTrollEffect())); + + // Honor Troll gets +2/+1 as long as you have 25 or more life. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostSourceEffect(2, 1, Duration.WhileOnBattlefield), + HonorTrollCondition.instance, "{this} gets +2/+1 as long as you have 25 or more life" + ))); + } + + private HonorTroll(final HonorTroll card) { + super(card); + } + + @Override + public HonorTroll copy() { + return new HonorTroll(this); + } +} + +class HonorTrollEffect extends ReplacementEffectImpl { + + HonorTrollEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "If you would gain life, you gain that much life plus 1 instead"; + } + + private HonorTrollEffect(final HonorTrollEffect effect) { + super(effect); + } + + @Override + public HonorTrollEffect copy() { + return new HonorTrollEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + event.setAmount(event.getAmount() + 1); + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.GAIN_LIFE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.isControlledBy(event.getPlayerId()); + } +} + +enum HonorTrollCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + return player != null && player.getLife() >= 25; + } +} diff --git a/Mage.Sets/src/mage/cards/h/HorizonSpellbomb.java b/Mage.Sets/src/mage/cards/h/HorizonSpellbomb.java index f32db6d65135..a85f77ad24da 100644 --- a/Mage.Sets/src/mage/cards/h/HorizonSpellbomb.java +++ b/Mage.Sets/src/mage/cards/h/HorizonSpellbomb.java @@ -1,7 +1,7 @@ package mage.cards.h; -import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.common.PutIntoGraveFromBattlefieldSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; @@ -35,7 +35,7 @@ public HorizonSpellbomb(UUID ownerId, CardSetInfo setInfo) { ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); // When Horizon Spellbomb is put into a graveyard from the battlefield, you may pay {G}. If you do, draw a card. - this.addAbility(new DiesSourceTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{G}")))); + this.addAbility(new PutIntoGraveFromBattlefieldSourceTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{G}")))); } private HorizonSpellbomb(final HorizonSpellbomb card) { diff --git a/Mage.Sets/src/mage/cards/h/HorizonStone.java b/Mage.Sets/src/mage/cards/h/HorizonStone.java index c2c032d3558d..07f6e2efdae8 100644 --- a/Mage.Sets/src/mage/cards/h/HorizonStone.java +++ b/Mage.Sets/src/mage/cards/h/HorizonStone.java @@ -2,14 +2,12 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.ContinuousEffectImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; +import mage.constants.*; import mage.game.Game; -import mage.game.events.GameEvent; +import mage.players.Player; import java.util.UUID; @@ -35,11 +33,11 @@ public HorizonStone copy() { } } -class HorizonStoneEffect extends ReplacementEffectImpl { +class HorizonStoneEffect extends ContinuousEffectImpl { HorizonStoneEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "If you would lose unspent mana, that mana becomes colorless instead."; + super(Duration.WhileOnBattlefield, Layer.RulesEffects, SubLayer.NA, Outcome.Benefit); + staticText = "if you would lose unspent mana, that mana becomes colorless instead"; } private HorizonStoneEffect(final HorizonStoneEffect effect) { @@ -53,21 +51,10 @@ public HorizonStoneEffect copy() { @Override public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player player = game.getPlayer(source.getControllerId()); + if (player != null) { + player.getManaPool().setManaBecomesColorless(true); + } return true; } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.EMPTY_MANA_POOL; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - return event.getPlayerId().equals(source.getControllerId()); - } } diff --git a/Mage.Sets/src/mage/cards/h/HornOfPlenty.java b/Mage.Sets/src/mage/cards/h/HornOfPlenty.java index c5fba150a642..a3c76253bf00 100644 --- a/Mage.Sets/src/mage/cards/h/HornOfPlenty.java +++ b/Mage.Sets/src/mage/cards/h/HornOfPlenty.java @@ -16,6 +16,7 @@ import mage.constants.SetTargetPointer; import mage.constants.TargetController; import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.targetpointer.FixedTarget; @@ -30,7 +31,7 @@ public HornOfPlenty(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{6}"); // Whenever a player casts a spell, they may pay {1}. If that player does, they draw a card at the beginning of the next end step. - this.addAbility(new SpellCastAllTriggeredAbility(new HornOfPlentyEffect(), new FilterSpell("a spell"), false, SetTargetPointer.PLAYER)); + this.addAbility(new SpellCastAllTriggeredAbility(new HornOfPlentyEffect(), StaticFilters.FILTER_SPELL_A, false, SetTargetPointer.PLAYER)); } private HornOfPlenty(final HornOfPlenty card) { diff --git a/Mage.Sets/src/mage/cards/h/HorriblyAwry.java b/Mage.Sets/src/mage/cards/h/HorriblyAwry.java index 10f2cbed72bd..80c556dd4aba 100644 --- a/Mage.Sets/src/mage/cards/h/HorriblyAwry.java +++ b/Mage.Sets/src/mage/cards/h/HorriblyAwry.java @@ -10,7 +10,7 @@ import mage.constants.ComparisonType; import mage.constants.Zone; import mage.filter.common.FilterCreatureSpell; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetSpell; /** @@ -19,10 +19,10 @@ */ public final class HorriblyAwry extends CardImpl { - private static final FilterCreatureSpell filter = new FilterCreatureSpell("creature spell with converted mana cost 4 or less"); + private static final FilterCreatureSpell filter = new FilterCreatureSpell("creature spell with mana value 4 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 5)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 5)); } public HorriblyAwry(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/h/HostageTaker.java b/Mage.Sets/src/mage/cards/h/HostageTaker.java index 315984d792a9..3ea7f86aff17 100644 --- a/Mage.Sets/src/mage/cards/h/HostageTaker.java +++ b/Mage.Sets/src/mage/cards/h/HostageTaker.java @@ -11,7 +11,7 @@ import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -93,7 +93,7 @@ public boolean apply(Game game, Ability source) { UUID exileId = CardUtil.getCardExileZoneId(game, source); controller.moveCardToExileWithInfo(card, exileId, permanent.getIdName(), source, game, Zone.BATTLEFIELD, true); // allow to cast the card and you may spend mana as though it were mana of any color to cast it - CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.Custom); + CardUtil.makeCardPlayable(game, source, card, Duration.Custom, true); return true; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/h/HotSoup.java b/Mage.Sets/src/mage/cards/h/HotSoup.java index 9525ad94fb8e..6121b4f64a31 100644 --- a/Mage.Sets/src/mage/cards/h/HotSoup.java +++ b/Mage.Sets/src/mage/cards/h/HotSoup.java @@ -69,7 +69,7 @@ public HotSoupTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/h/HowlOfTheHorde.java b/Mage.Sets/src/mage/cards/h/HowlOfTheHorde.java index 6832cb11b2f1..a6ccf14b7fd5 100644 --- a/Mage.Sets/src/mage/cards/h/HowlOfTheHorde.java +++ b/Mage.Sets/src/mage/cards/h/HowlOfTheHorde.java @@ -79,7 +79,7 @@ public boolean checkEventType(GameEvent event, Game game) { public boolean checkTrigger(GameEvent event, Game game) { if (event.getPlayerId().equals(this.getControllerId())) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && (spell.isInstant() || spell.isSorcery())) { + if (spell != null && spell.isInstantOrSorcery()) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getTargetId())); } diff --git a/Mage.Sets/src/mage/cards/h/HowlingGolem.java b/Mage.Sets/src/mage/cards/h/HowlingGolem.java index 53945288b0ea..891d1cdce1e0 100644 --- a/Mage.Sets/src/mage/cards/h/HowlingGolem.java +++ b/Mage.Sets/src/mage/cards/h/HowlingGolem.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.cards.h; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/h/HowlpackAlpha.java b/Mage.Sets/src/mage/cards/h/HowlpackAlpha.java index ac54a6358d73..b877500f2524 100644 --- a/Mage.Sets/src/mage/cards/h/HowlpackAlpha.java +++ b/Mage.Sets/src/mage/cards/h/HowlpackAlpha.java @@ -1,45 +1,41 @@ - package mage.cards.h; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.BeginningOfYourEndStepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.common.TransformedCondition; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.decorator.ConditionalTriggeredAbility; -import mage.abilities.effects.Effect; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; -import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.game.permanent.token.WolfToken; +import java.util.UUID; + /** - * * @author North, noxx */ public final class HowlpackAlpha extends CardImpl { private static final String ruleText = "At the beginning of your end step, create a 2/2 green Wolf creature token"; - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Werewolf and Wolf creatures"); + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("Werewolf and Wolf creatures"); static { - filter.add(Predicates.or(SubType.WEREWOLF.getPredicate(), SubType.WOLF.getPredicate())); + filter.add(Predicates.or( + SubType.WEREWOLF.getPredicate(), + SubType.WOLF.getPredicate() + )); } public HowlpackAlpha(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.WEREWOLF); // this card is the second face of double-faced card @@ -51,15 +47,15 @@ public HowlpackAlpha(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(3); // Other Werewolf and Wolf creatures you control get +1/+1. - Effect effect = new ConditionalContinuousEffect(new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filter, true), new TransformedCondition(), null); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 1, 1, Duration.WhileOnBattlefield, filter, true + ))); // At the beginning of your end step, create a 2/2 green Wolf creature token. - this.addAbility(new ConditionalTriggeredAbility(new BeginningOfYourEndStepTriggeredAbility(new CreateTokenEffect(new WolfToken()), false), new TransformedCondition(), ruleText)); + this.addAbility(new BeginningOfYourEndStepTriggeredAbility(new CreateTokenEffect(new WolfToken()), false)); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Howlpack Alpha. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private HowlpackAlpha(final HowlpackAlpha card) { diff --git a/Mage.Sets/src/mage/cards/h/HowlpackOfEstwald.java b/Mage.Sets/src/mage/cards/h/HowlpackOfEstwald.java index 690e0fc94416..ded182d31631 100644 --- a/Mage.Sets/src/mage/cards/h/HowlpackOfEstwald.java +++ b/Mage.Sets/src/mage/cards/h/HowlpackOfEstwald.java @@ -1,19 +1,13 @@ - package mage.cards.h; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; -import mage.abilities.keyword.TransformAbility; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** * @author nantuko @@ -21,7 +15,7 @@ public final class HowlpackOfEstwald extends CardImpl { public HowlpackOfEstwald(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.WEREWOLF); this.color.setGreen(true); @@ -33,8 +27,7 @@ public HowlpackOfEstwald(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(6); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Howlpack of Estwald. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private HowlpackOfEstwald(final HowlpackOfEstwald card) { diff --git a/Mage.Sets/src/mage/cards/h/HowlpackWolf.java b/Mage.Sets/src/mage/cards/h/HowlpackWolf.java index f8f28e4baaee..b5c1038e2fce 100644 --- a/Mage.Sets/src/mage/cards/h/HowlpackWolf.java +++ b/Mage.Sets/src/mage/cards/h/HowlpackWolf.java @@ -12,7 +12,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/h/HumOfTheRadix.java b/Mage.Sets/src/mage/cards/h/HumOfTheRadix.java index b26d4fa4ea42..c603c516cb94 100644 --- a/Mage.Sets/src/mage/cards/h/HumOfTheRadix.java +++ b/Mage.Sets/src/mage/cards/h/HumOfTheRadix.java @@ -4,15 +4,18 @@ import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.common.FilterArtifactPermanent; +import mage.constants.CardType; +import mage.constants.CostModificationType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.StaticFilters; import mage.game.Game; import mage.util.CardUtil; import java.util.UUID; -import mage.cards.Card; /** * @author Pete Rossi @@ -23,8 +26,7 @@ public HumOfTheRadix(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}{G}"); // Each artifact spell costs {1} more to cast for each artifact its controller controls. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new HumOfTheRadixCostIncreaseEffect())); - + this.addAbility(new SimpleStaticAbility(new HumOfTheRadixCostIncreaseEffect())); } private HumOfTheRadix(final HumOfTheRadix card) { @@ -50,20 +52,20 @@ class HumOfTheRadixCostIncreaseEffect extends CostModificationEffectImpl { @Override public boolean apply(Game game, Ability source, Ability abilityToModify) { - int additionalCost = game.getBattlefield().getAllActivePermanents(new FilterArtifactPermanent(), abilityToModify.getControllerId(), game).size(); - CardUtil.increaseCost(abilityToModify, additionalCost); + CardUtil.increaseCost(abilityToModify, game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT, + abilityToModify.getControllerId(), source.getSourceId(), game + ).size()); return true; } @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify instanceof SpellAbility) { - Card spellCard = ((SpellAbility) abilityToModify).getCharacteristics(game); - if (spellCard != null) { - return !spellCard.isArtifact(); - } + if (!(abilityToModify instanceof SpellAbility)) { + return false; } - return false; + Card spellCard = ((SpellAbility) abilityToModify).getCharacteristics(game); + return spellCard != null && spellCard.isArtifact(); } @Override diff --git a/Mage.Sets/src/mage/cards/h/Humiliate.java b/Mage.Sets/src/mage/cards/h/Humiliate.java new file mode 100644 index 000000000000..b372d5f23021 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/Humiliate.java @@ -0,0 +1,82 @@ +package mage.cards.h; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.discard.DiscardCardYouChooseTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Humiliate extends CardImpl { + + public Humiliate(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{W}{B}"); + + // Target opponent reveals their hand. You choose a nonland card from it. That player discards that card. Put a +1/+1 counter on a creature you control. + this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect( + StaticFilters.FILTER_CARD_NON_LAND, TargetController.OPPONENT + )); + this.getSpellAbility().addEffect(new HumiliateEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + } + + private Humiliate(final Humiliate card) { + super(card); + } + + @Override + public Humiliate copy() { + return new Humiliate(this); + } +} + +class HumiliateEffect extends OneShotEffect { + + HumiliateEffect() { + super(Outcome.Benefit); + staticText = "Put a +1/+1 counter on a creature you control"; + } + + private HumiliateEffect(final HumiliateEffect effect) { + super(effect); + } + + @Override + public HumiliateEffect copy() { + return new HumiliateEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null || game.getBattlefield().count( + StaticFilters.FILTER_CONTROLLED_CREATURE, + source.getSourceId(), source.getControllerId(), game + ) < 1) { + return false; + } + TargetPermanent target = new TargetControlledCreaturePermanent(); + target.setNotTarget(true); + target.withChooseHint("+1/+1 counter"); + player.choose(outcome, target, source.getSourceId(), game); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + return permanent != null && permanent.addCounters( + CounterType.P1P1.createInstance(), source.getControllerId(), source, game + ); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HuntForSpecimens.java b/Mage.Sets/src/mage/cards/h/HuntForSpecimens.java new file mode 100644 index 000000000000..649b77a08382 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HuntForSpecimens.java @@ -0,0 +1,35 @@ +package mage.cards.h; + +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.LearnEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.permanent.token.WitherbloomToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HuntForSpecimens extends CardImpl { + + public HuntForSpecimens(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}"); + + // Create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life." + this.getSpellAbility().addEffect(new CreateTokenEffect(new WitherbloomToken())); + + // Learn. + this.getSpellAbility().addEffect(new LearnEffect().concatBy("
")); + } + + private HuntForSpecimens(final HuntForSpecimens card) { + super(card); + } + + @Override + public HuntForSpecimens copy() { + return new HuntForSpecimens(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HuntersInsight.java b/Mage.Sets/src/mage/cards/h/HuntersInsight.java index 87315f363c0d..c15b51194704 100644 --- a/Mage.Sets/src/mage/cards/h/HuntersInsight.java +++ b/Mage.Sets/src/mage/cards/h/HuntersInsight.java @@ -13,7 +13,6 @@ import mage.game.Game; import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; @@ -92,7 +91,7 @@ public HuntersInsightTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + || event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override @@ -101,6 +100,10 @@ public boolean checkTrigger(GameEvent event, Game game) { || !((DamagedEvent) event).isCombatDamage()) { return false; } + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null && !permanent.isPlaneswalker()) { + return false; + } this.getEffects().clear(); this.addEffect(new DrawCardSourceControllerEffect(event.getAmount())); return true; diff --git a/Mage.Sets/src/mage/cards/h/HuntmasterOfTheFells.java b/Mage.Sets/src/mage/cards/h/HuntmasterOfTheFells.java index 5c3beb26396d..a79558e8ae4f 100644 --- a/Mage.Sets/src/mage/cards/h/HuntmasterOfTheFells.java +++ b/Mage.Sets/src/mage/cards/h/HuntmasterOfTheFells.java @@ -1,26 +1,23 @@ - package mage.cards.h; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.TriggeredAbility; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.GainLifeEffect; -import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.permanent.token.WolfToken; +import java.util.UUID; + /** * @author BetaSteward */ @@ -42,8 +39,7 @@ public HuntmasterOfTheFells(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each upkeep, if no spells were cast last turn, transform Huntmaster of the Fells. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private HuntmasterOfTheFells(final HuntmasterOfTheFells card) { diff --git a/Mage.Sets/src/mage/cards/h/HurkylsRecall.java b/Mage.Sets/src/mage/cards/h/HurkylsRecall.java index 384f5c6ec369..4faff28b02a9 100644 --- a/Mage.Sets/src/mage/cards/h/HurkylsRecall.java +++ b/Mage.Sets/src/mage/cards/h/HurkylsRecall.java @@ -10,7 +10,7 @@ import mage.constants.Outcome; import mage.filter.FilterPermanent; import mage.filter.common.FilterArtifactPermanent; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.target.TargetPlayer; diff --git a/Mage.Sets/src/mage/cards/h/HydromorphGuardian.java b/Mage.Sets/src/mage/cards/h/HydromorphGuardian.java index 8977902354a0..d53466e2dfe3 100644 --- a/Mage.Sets/src/mage/cards/h/HydromorphGuardian.java +++ b/Mage.Sets/src/mage/cards/h/HydromorphGuardian.java @@ -16,7 +16,7 @@ import mage.constants.Zone; import mage.filter.FilterSpell; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.other.TargetsPermanentPredicate; +import mage.filter.predicate.mageobject.TargetsPermanentPredicate; import mage.target.TargetSpell; /** diff --git a/Mage.Sets/src/mage/cards/h/HydromorphGull.java b/Mage.Sets/src/mage/cards/h/HydromorphGull.java index 8005db2aa40a..5a5009c07816 100644 --- a/Mage.Sets/src/mage/cards/h/HydromorphGull.java +++ b/Mage.Sets/src/mage/cards/h/HydromorphGull.java @@ -17,7 +17,7 @@ import mage.constants.Zone; import mage.filter.FilterSpell; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.other.TargetsPermanentPredicate; +import mage.filter.predicate.mageobject.TargetsPermanentPredicate; import mage.target.TargetSpell; /** diff --git a/Mage.Sets/src/mage/cards/h/HypnoticSprite.java b/Mage.Sets/src/mage/cards/h/HypnoticSprite.java index 89b513185704..205199389c88 100644 --- a/Mage.Sets/src/mage/cards/h/HypnoticSprite.java +++ b/Mage.Sets/src/mage/cards/h/HypnoticSprite.java @@ -9,7 +9,7 @@ import mage.constants.ComparisonType; import mage.constants.SubType; import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetSpell; import java.util.UUID; @@ -19,10 +19,10 @@ */ public final class HypnoticSprite extends AdventureCard { - private static final FilterSpell filter = new FilterSpell("spell with converted mana cost 3 or less"); + private static final FilterSpell filter = new FilterSpell("spell with mana value 3 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public HypnoticSprite(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/h/Hypnox.java b/Mage.Sets/src/mage/cards/h/Hypnox.java index ee5edcaa0a02..70d7067d384a 100644 --- a/Mage.Sets/src/mage/cards/h/Hypnox.java +++ b/Mage.Sets/src/mage/cards/h/Hypnox.java @@ -1,37 +1,36 @@ - package mage.cards.h; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.ExileZone; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetOpponent; +import mage.util.CardUtil; import mage.watchers.common.CastFromHandWatcher; +import java.util.UUID; + /** - * * @author L_J */ public final class Hypnox extends CardImpl { public Hypnox(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{8}{B}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{8}{B}{B}{B}"); this.subtype.add(SubType.NIGHTMARE); this.subtype.add(SubType.HORROR); this.power = new MageInt(8); @@ -41,10 +40,13 @@ public Hypnox(UUID ownerId, CardSetInfo setInfo) { this.addAbility(FlyingAbility.getInstance()); // When Hypnox enters the battlefield, if you cast it from your hand, exile all cards from target opponent's hand. - TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new HypnoxExileEffect()); + Ability ability = new ConditionalInterveningIfTriggeredAbility( + new EntersBattlefieldTriggeredAbility(new HypnoxExileEffect()), + CastFromHandSourcePermanentCondition.instance, "When {this} enters the battlefield, " + + "if you cast it from your hand, exile all cards from target opponent's hand." + ); ability.addTarget(new TargetOpponent()); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, CastFromHandSourcePermanentCondition.instance, - "When {this} enters the battlefield, if you cast it from your hand, exile all cards from target opponent's hand."), new CastFromHandWatcher()); + this.addAbility(ability, new CastFromHandWatcher()); // When Hypnox leaves the battlefield, return the exiled cards to their owner's hand. this.addAbility(new LeavesBattlefieldTriggeredAbility(new HypnoxReturnEffect(), false)); @@ -61,45 +63,44 @@ public Hypnox copy() { } class HypnoxExileEffect extends OneShotEffect { + HypnoxExileEffect() { super(Outcome.Exile); staticText = "Exile all cards from target opponent's hand"; } - HypnoxExileEffect(final HypnoxExileEffect effect) { + private HypnoxExileEffect(final HypnoxExileEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); Player player = game.getPlayer(source.getFirstTarget()); - if (player != null) { - for (UUID cid : player.getHand().copy()) { - Card c = game.getCard(cid); - if (c != null) { - c.moveToExile(source.getSourceId(), "Hypnox", source, game); - } - } - return true; + Permanent permanent = source.getSourcePermanentOrLKI(game); + if (controller == null || player == null || permanent == null) { + return false; } - return false; + return controller.moveCardsToExile( + player.getHand().getCards(game), source, game, true, + CardUtil.getExileZoneId(game, source), permanent.getIdName() + ); } @Override public HypnoxExileEffect copy() { return new HypnoxExileEffect(this); } +} - } - class HypnoxReturnEffect extends OneShotEffect { - public HypnoxReturnEffect() { + HypnoxReturnEffect() { super(Outcome.ReturnToHand); this.staticText = "return the exiled cards to their owner's hand"; } - public HypnoxReturnEffect(final HypnoxReturnEffect effect) { + private HypnoxReturnEffect(final HypnoxReturnEffect effect) { super(effect); } @@ -111,13 +112,7 @@ public HypnoxReturnEffect copy() { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - ExileZone exZone = game.getExile().getExileZone(source.getSourceId()); - if (exZone != null) { - return controller.moveCards(exZone.getCards(game), Zone.HAND, source, game); - } - return true; - } - return false; + ExileZone exZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + return controller != null && exZone != null && controller.moveCards(exZone, Zone.HAND, source, game); } } diff --git a/Mage.Sets/src/mage/cards/i/IbHalfheartGoblinTactician.java b/Mage.Sets/src/mage/cards/i/IbHalfheartGoblinTactician.java index 08c2971e755f..6f8cb02d7b43 100644 --- a/Mage.Sets/src/mage/cards/i/IbHalfheartGoblinTactician.java +++ b/Mage.Sets/src/mage/cards/i/IbHalfheartGoblinTactician.java @@ -16,7 +16,7 @@ import mage.constants.*; import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.combat.CombatGroup; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/i/IceFangCoatl.java b/Mage.Sets/src/mage/cards/i/IceFangCoatl.java index 9ec9b720b4ac..adcc658fc211 100644 --- a/Mage.Sets/src/mage/cards/i/IceFangCoatl.java +++ b/Mage.Sets/src/mage/cards/i/IceFangCoatl.java @@ -9,7 +9,6 @@ import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.hint.ConditionHint; -import mage.abilities.hint.Hint; import mage.abilities.keyword.DeathtouchAbility; import mage.abilities.keyword.FlashAbility; import mage.abilities.keyword.FlyingAbility; @@ -18,7 +17,7 @@ import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/i/IcebergCancrix.java b/Mage.Sets/src/mage/cards/i/IcebergCancrix.java index 0f2cb53c504c..a73803eabcad 100644 --- a/Mage.Sets/src/mage/cards/i/IcebergCancrix.java +++ b/Mage.Sets/src/mage/cards/i/IcebergCancrix.java @@ -11,7 +11,7 @@ import mage.constants.SuperType; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPlayer; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/i/IcefeatherAven.java b/Mage.Sets/src/mage/cards/i/IcefeatherAven.java index 3f6ebb31bdb0..48c07f245fd4 100644 --- a/Mage.Sets/src/mage/cards/i/IcefeatherAven.java +++ b/Mage.Sets/src/mage/cards/i/IcefeatherAven.java @@ -14,7 +14,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/i/IdentityCrisis.java b/Mage.Sets/src/mage/cards/i/IdentityCrisis.java index 27b113c432fa..d9ec7617d89a 100644 --- a/Mage.Sets/src/mage/cards/i/IdentityCrisis.java +++ b/Mage.Sets/src/mage/cards/i/IdentityCrisis.java @@ -1,34 +1,33 @@ - - package mage.cards.i; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.Zone; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author Loki */ public final class IdentityCrisis extends CardImpl { - public IdentityCrisis (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{W}{W}{B}{B}"); + public IdentityCrisis(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{W}{W}{B}{B}"); - this.getSpellAbility().addEffect(new IdentityCrisisEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); } - public IdentityCrisis (final IdentityCrisis card) { + private IdentityCrisis(final IdentityCrisis card) { super(card); } @@ -36,43 +35,34 @@ public IdentityCrisis (final IdentityCrisis card) { public IdentityCrisis copy() { return new IdentityCrisis(this); } - } class IdentityCrisisEffect extends OneShotEffect { + IdentityCrisisEffect() { super(Outcome.Exile); staticText = "Exile all cards from target player's hand and graveyard"; } - IdentityCrisisEffect(final IdentityCrisisEffect effect) { + private IdentityCrisisEffect(final IdentityCrisisEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getFirstTarget()); Player player = game.getPlayer(source.getFirstTarget()); - if (player != null) { - for (UUID cid : player.getHand().copy()) { - Card c = game.getCard(cid); - if (c != null) { - c.moveToExile(null, null, source, game); - } - } - for (UUID cid : player.getGraveyard().copy()) { - Card c = game.getCard(cid); - if (c != null) { - c.moveToExile(null, null, source, game); - } - } - return true; + if (controller == null || player == null) { + return false; } - return false; + Cards cards = new CardsImpl(); + cards.addAll(player.getHand()); + cards.addAll(player.getGraveyard()); + return controller.moveCards(cards, Zone.EXILED, source, game); } @Override public IdentityCrisisEffect copy() { return new IdentityCrisisEffect(this); } - - } \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/i/IdolOfEndurance.java b/Mage.Sets/src/mage/cards/i/IdolOfEndurance.java index a9570fc12ebe..c11171f6e390 100644 --- a/Mage.Sets/src/mage/cards/i/IdolOfEndurance.java +++ b/Mage.Sets/src/mage/cards/i/IdolOfEndurance.java @@ -13,7 +13,7 @@ import mage.constants.*; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; @@ -58,12 +58,12 @@ class IdolOfEnduranceExileEffect extends OneShotEffect { private static final FilterCard filter = new FilterCreatureCard(); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } IdolOfEnduranceExileEffect() { super(Outcome.Benefit); - staticText = "exile all creature cards with converted mana cost 3 or less from your graveyard until {this} leaves the battlefield"; + staticText = "exile all creature cards with mana value 3 or less from your graveyard until {this} leaves the battlefield"; } private IdolOfEnduranceExileEffect(final IdolOfEnduranceExileEffect effect) { diff --git a/Mage.Sets/src/mage/cards/i/IdyllicGrange.java b/Mage.Sets/src/mage/cards/i/IdyllicGrange.java index 50b1d4bfc632..30c1312b362a 100644 --- a/Mage.Sets/src/mage/cards/i/IdyllicGrange.java +++ b/Mage.Sets/src/mage/cards/i/IdyllicGrange.java @@ -17,7 +17,7 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledCreaturePermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/i/IgneousInspiration.java b/Mage.Sets/src/mage/cards/i/IgneousInspiration.java new file mode 100644 index 000000000000..ab28922c5b6e --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IgneousInspiration.java @@ -0,0 +1,36 @@ +package mage.cards.i; + +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.LearnEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class IgneousInspiration extends CardImpl { + + public IgneousInspiration(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}"); + + // Igneous Inspiration deals 3 damage to any target. + this.getSpellAbility().addEffect(new DamageTargetEffect(3)); + this.getSpellAbility().addTarget(new TargetAnyTarget()); + + // Learn. + this.getSpellAbility().addEffect(new LearnEffect().concatBy("
")); + } + + private IgneousInspiration(final IgneousInspiration card) { + super(card); + } + + @Override + public IgneousInspiration copy() { + return new IgneousInspiration(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/IgniteMemories.java b/Mage.Sets/src/mage/cards/i/IgniteMemories.java index 474dac244ef2..6d3228201055 100644 --- a/Mage.Sets/src/mage/cards/i/IgniteMemories.java +++ b/Mage.Sets/src/mage/cards/i/IgniteMemories.java @@ -42,7 +42,7 @@ class IgniteMemoriesEffect extends OneShotEffect { public IgniteMemoriesEffect() { super(Outcome.Damage); - staticText = "Target player reveals a card at random from their hand. {this} deals damage to that player equal to that card's converted mana cost"; + staticText = "Target player reveals a card at random from their hand. {this} deals damage to that player equal to that card's mana value"; } public IgniteMemoriesEffect(final IgniteMemoriesEffect effect) { @@ -60,7 +60,7 @@ public boolean apply(Game game, Ability source) { if (card != null) { revealed.add(card); controller.revealCards(sourceObject.getIdName(), revealed, game); - controller.damage(card.getConvertedManaCost(), source.getSourceId(), source, game); + controller.damage(card.getManaValue(), source.getSourceId(), source, game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/i/IgnorantBliss.java b/Mage.Sets/src/mage/cards/i/IgnorantBliss.java index f0b65fc5e5d6..6f22f86c8647 100644 --- a/Mage.Sets/src/mage/cards/i/IgnorantBliss.java +++ b/Mage.Sets/src/mage/cards/i/IgnorantBliss.java @@ -1,27 +1,27 @@ - package mage.cards.i; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.ReturnFromExileEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; -import mage.game.ExileZone; import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; +import java.util.Objects; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class IgnorantBliss extends CardImpl { @@ -30,9 +30,7 @@ public IgnorantBliss(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); // Exile all cards from your hand face down. At the beginning of the next end step, return those cards to your hand, then draw a card. - this.getSpellAbility().addEffect(new IgnorantBlissExileEffect()); - this.getSpellAbility().addEffect(new IgnorantBlissReturnEffect()); - + this.getSpellAbility().addEffect(new IgnorantBlissEffect()); } private IgnorantBliss(final IgnorantBliss card) { @@ -45,71 +43,40 @@ public IgnorantBliss copy() { } } -class IgnorantBlissExileEffect extends OneShotEffect { +class IgnorantBlissEffect extends OneShotEffect { - IgnorantBlissExileEffect() { + IgnorantBlissEffect() { super(Outcome.Exile); - this.staticText = "Exile all cards from your hand face down"; + this.staticText = "Exile all cards from your hand face down. At the beginning of the next end step, " + + "return those cards to your hand, then draw a card"; } - IgnorantBlissExileEffect(final IgnorantBlissExileEffect effect) { + private IgnorantBlissEffect(final IgnorantBlissEffect effect) { super(effect); } @Override - public IgnorantBlissExileEffect copy() { - return new IgnorantBlissExileEffect(this); + public IgnorantBlissEffect copy() { + return new IgnorantBlissEffect(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) { - Cards hand = controller.getHand(); - hand.getCards(game).stream().filter((card) -> (card != null)).map((card) -> { - UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), 0); - controller.moveCardsToExile(card, source, game, false, exileZoneId, sourceObject.getIdName()); - return card; - }).forEachOrdered((card) -> { - card.setFaceDown(true, game); - }); - return true; - } - return false; - } -} - -class IgnorantBlissReturnEffect extends OneShotEffect { - - IgnorantBlissReturnEffect() { - super(Outcome.DrawCard); - this.staticText = "At the beginning of the next end step, return those cards to your hand, then draw a card"; - } - - IgnorantBlissReturnEffect(final IgnorantBlissReturnEffect effect) { - super(effect); - } - - @Override - public IgnorantBlissReturnEffect copy() { - return new IgnorantBlissReturnEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), 0)); - if (exileZone != null) { - Effect effect = new ReturnFromExileEffect(exileZone.getId(), Zone.HAND); - AtTheBeginOfNextEndStepDelayedTriggeredAbility ability = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); - ability.addEffect(new DrawCardSourceControllerEffect(1)); - game.addDelayedTriggeredAbility(ability, source); - return true; - } + if (controller == null || sourceObject == null) { + return false; } - return false; + Cards hand = new CardsImpl(controller.getHand()); + controller.moveCardsToExile(hand.getCards(game), source, game, false, CardUtil.getExileZoneId(game, source), sourceObject.getIdName()); + hand.getCards(game) + .stream() + .filter(Objects::nonNull) + .filter(card -> game.getState().getZone(card.getId()) == Zone.EXILED) + .forEach(card -> card.setFaceDown(true, game)); + DelayedTriggeredAbility ability = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnFromExileEffect(Zone.HAND)); + ability.addEffect(new DrawCardSourceControllerEffect(1)); + game.addDelayedTriggeredAbility(ability, source); + return true; } } diff --git a/Mage.Sets/src/mage/cards/i/IllGottenGains.java b/Mage.Sets/src/mage/cards/i/IllGottenGains.java index 8d99828eb5e3..707325a27c7b 100644 --- a/Mage.Sets/src/mage/cards/i/IllGottenGains.java +++ b/Mage.Sets/src/mage/cards/i/IllGottenGains.java @@ -28,7 +28,7 @@ public IllGottenGains(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{B}{B}"); // Exile Ill-Gotten Gains. - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); // Each player discards their hand, this.getSpellAbility().addEffect(new DiscardHandAllEffect()); diff --git a/Mage.Sets/src/mage/cards/i/IllicitAuction.java b/Mage.Sets/src/mage/cards/i/IllicitAuction.java index dfc42805a51f..667434c85c6e 100644 --- a/Mage.Sets/src/mage/cards/i/IllicitAuction.java +++ b/Mage.Sets/src/mage/cards/i/IllicitAuction.java @@ -78,7 +78,9 @@ public void init(Ability source, Game game) { if (currentPlayer.canRespond() && currentPlayer.chooseUse(Outcome.GainControl, text, source, game)) { int newBid = 0; - if (!currentPlayer.isHuman()) {//AI will evaluate the creature and bid + if (currentPlayer.isComputer()) { + // AI hint + // AI will evaluate the creature and bid CreatureEvaluator eval = new CreatureEvaluator(); int computerLife = currentPlayer.getLife(); int creatureValue = eval.evaluate(targetCreature, game); diff --git a/Mage.Sets/src/mage/cards/i/IlluminateHistory.java b/Mage.Sets/src/mage/cards/i/IlluminateHistory.java new file mode 100644 index 000000000000..073de314ddf2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IlluminateHistory.java @@ -0,0 +1,44 @@ +package mage.cards.i; + +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.CardsInControllerGraveyardCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.discard.DiscardAndDrawThatManyEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.LoreholdToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class IlluminateHistory extends CardImpl { + + private static final Condition condition = new CardsInControllerGraveyardCondition(7); + + public IlluminateHistory(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}{R}"); + + this.subtype.add(SubType.LESSON); + + // Discard any number of cards, then draw that many cards. Then if there are seven or more cards in your graveyard, create a 3/2 red and white Spirit creature token. + this.getSpellAbility().addEffect(new DiscardAndDrawThatManyEffect(Integer.MAX_VALUE)); + this.getSpellAbility().addEffect(new ConditionalOneShotEffect( + new CreateTokenEffect(new LoreholdToken()), condition, "Then if there are seven or more " + + "cards in your graveyard, create a 3/2 red and white Spirit creature token" + )); + } + + private IlluminateHistory(final IlluminateHistory card) { + super(card); + } + + @Override + public IlluminateHistory copy() { + return new IlluminateHistory(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/Illumination.java b/Mage.Sets/src/mage/cards/i/Illumination.java index eba22be94674..5894d6b6f81f 100644 --- a/Mage.Sets/src/mage/cards/i/Illumination.java +++ b/Mage.Sets/src/mage/cards/i/Illumination.java @@ -55,7 +55,7 @@ class IlluminationEffect extends OneShotEffect { public IlluminationEffect() { super(Outcome.GainLife); - staticText = "Its controller gains life equal to its converted mana cost"; + staticText = "Its controller gains life equal to its mana value"; } public IlluminationEffect(final IlluminationEffect effect) { @@ -81,7 +81,7 @@ public boolean apply(Game game, Ability source) { } if (controller != null) { Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); - int cost = spell.getConvertedManaCost(); + int cost = spell.getManaValue(); Player player = game.getPlayer(spell.getControllerId()); if (player != null) { player.gainLife(cost, game, source); diff --git a/Mage.Sets/src/mage/cards/i/IllusionOfChoice.java b/Mage.Sets/src/mage/cards/i/IllusionOfChoice.java new file mode 100644 index 000000000000..3c382b0bf989 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IllusionOfChoice.java @@ -0,0 +1,67 @@ +package mage.cards.i; + +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.events.GameEvent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class IllusionOfChoice extends CardImpl { + + public IllusionOfChoice(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}"); + + // You choose how each player votes this turn. + this.getSpellAbility().addEffect(new IllusionOfChoiceReplacementEffect()); + + // Draw a card. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + } + + private IllusionOfChoice(final IllusionOfChoice card) { + super(card); + } + + @Override + public IllusionOfChoice copy() { + return new IllusionOfChoice(this); + } +} + +class IllusionOfChoiceReplacementEffect extends ContinuousRuleModifyingEffectImpl { + + IllusionOfChoiceReplacementEffect() { + super(Duration.EndOfTurn, Outcome.Benefit); + staticText = "you choose how each player votes this turn"; + } + + private IllusionOfChoiceReplacementEffect(final IllusionOfChoiceReplacementEffect effect) { + super(effect); + } + + @Override + public IllusionOfChoiceReplacementEffect copy() { + return new IllusionOfChoiceReplacementEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.VOTE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + event.setPlayerId(source.getControllerId()); + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/i/IllusionistsBracers.java b/Mage.Sets/src/mage/cards/i/IllusionistsBracers.java index d27889829efe..9f4d9f1cc008 100644 --- a/Mage.Sets/src/mage/cards/i/IllusionistsBracers.java +++ b/Mage.Sets/src/mage/cards/i/IllusionistsBracers.java @@ -1,10 +1,8 @@ package mage.cards.i; -import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CopyStackAbilityEffect; import mage.abilities.keyword.EquipAbility; import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.cards.CardImpl; @@ -15,10 +13,8 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.game.stack.StackAbility; -import mage.players.Player; import java.util.UUID; @@ -51,10 +47,10 @@ public IllusionistsBracers copy() { class IllusionistsBracersTriggeredAbility extends TriggeredAbilityImpl { IllusionistsBracersTriggeredAbility() { - super(Zone.BATTLEFIELD, new CopyActivatedAbilityEffect()); + super(Zone.BATTLEFIELD, new CopyStackAbilityEffect()); } - IllusionistsBracersTriggeredAbility(final IllusionistsBracersTriggeredAbility ability) { + private IllusionistsBracersTriggeredAbility(final IllusionistsBracersTriggeredAbility ability) { super(ability); } @@ -71,49 +67,20 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { Permanent equipment = game.getPermanent(this.getSourceId()); - if (equipment != null && equipment.isAttachedTo(event.getSourceId())) { - StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); - if (!(stackAbility.getStackAbility() instanceof ActivatedManaAbilityImpl)) { - Effect effect = this.getEffects().get(0); - effect.setValue("stackAbility", stackAbility); - return true; - } + if (equipment == null || !equipment.isAttachedTo(event.getSourceId())) { + return false; } - return false; + StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); + if (stackAbility == null || stackAbility.getStackAbility() instanceof ActivatedManaAbilityImpl) { + return false; + } + this.getEffects().setValue("stackAbility", stackAbility); + return true; } @Override public String getRule() { - return "Whenever an ability of equipped creature is activated, if it isn't a mana ability, copy that ability. You may choose new targets for the copy."; - } -} - -class CopyActivatedAbilityEffect extends OneShotEffect { - - public CopyActivatedAbilityEffect() { - super(Outcome.Benefit); - this.staticText = "copy that ability. You may choose new targets for the copy"; - } - - public CopyActivatedAbilityEffect(final CopyActivatedAbilityEffect effect) { - super(effect); - } - - @Override - public CopyActivatedAbilityEffect copy() { - return new CopyActivatedAbilityEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - StackAbility ability = (StackAbility) getValue("stackAbility"); - Player controller = game.getPlayer(source.getControllerId()); - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (ability != null && controller != null && sourcePermanent != null) { - ability.createCopyOnStack(game, source, source.getControllerId(), true); - game.informPlayers(sourcePermanent.getName() + ": " + controller.getLogName() + " copied activated ability"); - return true; - } - return false; + return "Whenever an ability of equipped creature is activated, if it isn't a mana ability, " + + "copy that ability. You may choose new targets for the copy."; } } diff --git a/Mage.Sets/src/mage/cards/i/IllusionistsGambit.java b/Mage.Sets/src/mage/cards/i/IllusionistsGambit.java index 7a18c38a32f8..0f09869fd7cc 100644 --- a/Mage.Sets/src/mage/cards/i/IllusionistsGambit.java +++ b/Mage.Sets/src/mage/cards/i/IllusionistsGambit.java @@ -48,7 +48,7 @@ class IllusionistsGambitRemoveFromCombatEffect extends OneShotEffect { public IllusionistsGambitRemoveFromCombatEffect() { super(Outcome.Benefit); - this.staticText = "Remove all attacking creatures from combat and untap them. After this phase, there is an additional combat phase. Each of those creatures attacks that combat if able. They can't attack you or a planeswalker you control that combat"; + this.staticText = "Remove all attacking creatures from combat and untap them. After this phase, there is an additional combat phase. Each of those creatures attacks that combat if able. They can't attack you or planeswalkers you control that combat"; } public IllusionistsGambitRemoveFromCombatEffect(final IllusionistsGambitRemoveFromCombatEffect effect) { @@ -142,7 +142,7 @@ public IllusionistsGambitRestrictionEffect(List attackers, Phase phase) { super(Duration.Custom, Outcome.Benefit); this.attackers = attackers; this.phase = phase; - staticText = "They can't attack you or a planeswalker you control that combat"; + staticText = "They can't attack you or planeswalkers you control that combat"; } public IllusionistsGambitRestrictionEffect(final IllusionistsGambitRestrictionEffect effect) { diff --git a/Mage.Sets/src/mage/cards/i/IllustriousHistorian.java b/Mage.Sets/src/mage/cards/i/IllustriousHistorian.java new file mode 100644 index 000000000000..d7428f202702 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IllustriousHistorian.java @@ -0,0 +1,50 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.ExileSourceFromGraveCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.permanent.token.LoreholdToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class IllustriousHistorian extends CardImpl { + + public IllustriousHistorian(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // {5}, Exile Illustrious Historian from your graveyard: Create a tapped 3/2 red and white Spirit creature token. + Ability ability = new SimpleActivatedAbility( + Zone.GRAVEYARD, + new CreateTokenEffect( + new LoreholdToken(), 1, true, false + ), new GenericManaCost(5) + ); + ability.addCost(new ExileSourceFromGraveCost()); + this.addAbility(ability); + } + + private IllustriousHistorian(final IllustriousHistorian card) { + super(card); + } + + @Override + public IllustriousHistorian copy() { + return new IllustriousHistorian(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/ImminentDoom.java b/Mage.Sets/src/mage/cards/i/ImminentDoom.java index 56be45926fb4..f12f45763c3a 100644 --- a/Mage.Sets/src/mage/cards/i/ImminentDoom.java +++ b/Mage.Sets/src/mage/cards/i/ImminentDoom.java @@ -31,7 +31,7 @@ public ImminentDoom(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); // Imminent Doom enters the battlefield with a doom counter on it. - this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.DOOM.createInstance(1)))); + this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.DOOM.createInstance(1)), "with a doom counter on it")); // Whenever you cast a spell with converted mana cost equal to the number of doom counters on Imminent Doom, Imminent Doom deals that much damage to any target. Then put a doom counter on Imminent Doom. Ability ability = new ImminentDoomTriggeredAbility(); @@ -52,7 +52,7 @@ public ImminentDoom copy() { class ImminentDoomTriggeredAbility extends TriggeredAbilityImpl { - private String rule = "Whenever you cast a spell with converted mana cost equal to the number of doom counters on {this}, {this} deals that much damage to any target. Then put a doom counter on {this}."; + private String rule = "Whenever you cast a spell with mana value equal to the number of doom counters on {this}, {this} deals that much damage to any target. Then put a doom counter on {this}."; public ImminentDoomTriggeredAbility() { super(Zone.BATTLEFIELD, new ImminentDoomEffect()); @@ -79,7 +79,7 @@ public boolean checkTrigger(GameEvent event, Game game) { Spell spell = game.getStack().getSpell(event.getTargetId()); if (spell != null && imminentDoom != null - && spell.getConvertedManaCost() == imminentDoom.getCounters(game).getCount(CounterType.DOOM)) { + && spell.getManaValue() == imminentDoom.getCounters(game).getCount(CounterType.DOOM)) { game.getState().setValue("ImminentDoomCount" + getSourceId().toString(), imminentDoom.getCounters(game).getCount(CounterType.DOOM)); // store its current value return true; } diff --git a/Mage.Sets/src/mage/cards/i/ImmortalCoil.java b/Mage.Sets/src/mage/cards/i/ImmortalCoil.java index 0934c0693d27..d8e9fb6086be 100644 --- a/Mage.Sets/src/mage/cards/i/ImmortalCoil.java +++ b/Mage.Sets/src/mage/cards/i/ImmortalCoil.java @@ -6,18 +6,16 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.ExileFromGraveCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.PreventionEffectImpl; import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.abilities.effects.common.SacrificeSourceEffect; -import mage.cards.Card; +import mage.abilities.effects.common.LoseGameSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.DamageEvent; import mage.game.events.GameEvent; @@ -37,11 +35,13 @@ public ImmortalCoil(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{B}{B}"); // {tap}, Exile two cards from your graveyard: Draw a card. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new TapSourceCost()); - ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(2, new FilterCard("cards from your graveyard")))); + Ability ability = new SimpleActivatedAbility(new DrawCardSourceControllerEffect(1), new TapSourceCost()); + ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(2, StaticFilters.FILTER_CARD_CARDS))); this.addAbility(ability); + // If damage would be dealt to you, prevent that damage. Exile a card from your graveyard for each 1 damage prevented this way. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PreventAllDamageToControllerEffect())); + this.addAbility(new SimpleStaticAbility(new ImmortalCoilPreventionEffect())); + // When there are no cards in your graveyard, you lose the game. this.addAbility(new ImmortalCoilAbility()); } @@ -58,12 +58,11 @@ public ImmortalCoil copy() { class ImmortalCoilAbility extends StateTriggeredAbility { - public ImmortalCoilAbility() { - super(Zone.BATTLEFIELD, new SacrificeSourceEffect()); - this.addEffect(new LoseGameEffect()); + ImmortalCoilAbility() { + super(Zone.BATTLEFIELD, new LoseGameSourceControllerEffect()); } - public ImmortalCoilAbility(final ImmortalCoilAbility ability) { + private ImmortalCoilAbility(final ImmortalCoilAbility ability) { super(ability); } @@ -82,50 +81,23 @@ public boolean checkTrigger(GameEvent event, Game game) { public String getRule() { return "When there are no cards in your graveyard, you lose the game"; } - } -class LoseGameEffect extends OneShotEffect { - - public LoseGameEffect() { - super(Outcome.Neutral); - staticText = "you lose the game"; - } - - public LoseGameEffect(final LoseGameEffect effect) { - super(effect); - } - - @Override - public LoseGameEffect copy() { - return new LoseGameEffect(this); - } +class ImmortalCoilPreventionEffect extends PreventionEffectImpl { - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - player.lost(game); - return true; - } - return false; - } -} - -class PreventAllDamageToControllerEffect extends PreventionEffectImpl { - - public PreventAllDamageToControllerEffect() { + ImmortalCoilPreventionEffect() { super(Duration.WhileOnBattlefield); - staticText = "If damage would be dealt to you, prevent that damage. Exile a card from your graveyard for each 1 damage prevented this way"; + staticText = "If damage would be dealt to you, prevent that damage. " + + "Exile a card from your graveyard for each 1 damage prevented this way"; } - public PreventAllDamageToControllerEffect(final PreventAllDamageToControllerEffect effect) { + private ImmortalCoilPreventionEffect(final ImmortalCoilPreventionEffect effect) { super(effect); } @Override - public PreventAllDamageToControllerEffect copy() { - return new PreventAllDamageToControllerEffect(this); + public ImmortalCoilPreventionEffect copy() { + return new ImmortalCoilPreventionEffect(this); } @Override @@ -135,33 +107,30 @@ public boolean apply(Game game, Ability source) { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - GameEvent preventEvent = new PreventDamageEvent(event.getTargetId(), source.getSourceId(), source, source.getControllerId(), event.getAmount(), ((DamageEvent) event).isCombatDamage()); - if (!game.replaceEvent(preventEvent)) { - int damage = event.getAmount(); - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - TargetCardInYourGraveyard target = new TargetCardInYourGraveyard(Math.min(damage, player.getGraveyard().size()), new FilterCard()); - if (target.choose(Outcome.Exile, source.getControllerId(), source.getSourceId(), game)) { - for (UUID targetId : target.getTargets()) { - Card card = player.getGraveyard().get(targetId, game); - if (card != null) { - card.moveToZone(Zone.EXILED, source, game, false); - } - } - } - } - event.setAmount(0); - game.fireEvent(new PreventedDamageEvent(event.getTargetId(), source.getSourceId(), source, source.getControllerId(), damage)); + if (game.replaceEvent(new PreventDamageEvent( + event.getTargetId(), source.getSourceId(), source, source.getControllerId(), + event.getAmount(), ((DamageEvent) event).isCombatDamage() + ))) { + return false; } + int damage = event.getAmount(); + Player player = game.getPlayer(source.getControllerId()); + if (player != null) { + TargetCardInYourGraveyard target = new TargetCardInYourGraveyard(Math.min(damage, player.getGraveyard().size()), StaticFilters.FILTER_CARD); + target.setNotTarget(true); + player.choose(outcome, target, source.getSourceId(), game); + player.moveCards(new CardsImpl(target.getTargets()), Zone.EXILED, source, game); + } + event.setAmount(0); + game.fireEvent(new PreventedDamageEvent( + event.getTargetId(), source.getSourceId(), source, source.getControllerId(), damage + )); return false; } @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (super.applies(event, source, game)) { - return event.getTargetId().equals(source.getControllerId()); - } - return false; + return super.applies(event, source, game) + && event.getTargetId().equals(source.getControllerId()); } - } diff --git a/Mage.Sets/src/mage/cards/i/ImmortalServitude.java b/Mage.Sets/src/mage/cards/i/ImmortalServitude.java index dea07510c8d2..5704d4df4a22 100644 --- a/Mage.Sets/src/mage/cards/i/ImmortalServitude.java +++ b/Mage.Sets/src/mage/cards/i/ImmortalServitude.java @@ -1,7 +1,5 @@ package mage.cards.i; -import java.util.Set; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; @@ -14,8 +12,11 @@ import mage.game.Game; import mage.players.Player; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class ImmortalServitude extends CardImpl { @@ -39,12 +40,12 @@ public ImmortalServitude copy() { class ImmortalServitudeEffect extends OneShotEffect { - public ImmortalServitudeEffect() { + ImmortalServitudeEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "Return each creature card with converted mana cost X from your graveyard to the battlefield"; + this.staticText = "Return each creature card with mana value X from your graveyard to the battlefield"; } - public ImmortalServitudeEffect(final ImmortalServitudeEffect effect) { + private ImmortalServitudeEffect(final ImmortalServitudeEffect effect) { super(effect); } @@ -56,13 +57,14 @@ public ImmortalServitudeEffect copy() { @Override public boolean apply(Game game, Ability source) { Player you = game.getPlayer(source.getControllerId()); + if (you == null) { + return false; + } int count = source.getManaCostsToPay().getX(); Set cards = you.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURE, game); - for (Card card : cards) { - if (card != null && card.getConvertedManaCost() == count) { - card.moveToZone(Zone.BATTLEFIELD, source, game, false); - } - } - return true; + cards.removeIf(Objects::isNull); + cards.removeIf(card -> !card.isCreature()); + cards.removeIf(card -> card.getManaValue() != count); + return you.moveCards(cards, Zone.BATTLEFIELD, source, game); } } diff --git a/Mage.Sets/src/mage/cards/i/ImotiCelebrantOfBounty.java b/Mage.Sets/src/mage/cards/i/ImotiCelebrantOfBounty.java index 51015c50ced0..0ea33ab8a56b 100644 --- a/Mage.Sets/src/mage/cards/i/ImotiCelebrantOfBounty.java +++ b/Mage.Sets/src/mage/cards/i/ImotiCelebrantOfBounty.java @@ -11,7 +11,7 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.FilterObject; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import java.util.UUID; @@ -21,10 +21,10 @@ public final class ImotiCelebrantOfBounty extends CardImpl { private static final FilterObject filter - = new FilterObject("Spells you cast with converted mana cost 6 or greater"); + = new FilterObject("Spells you cast with mana value 6 or greater"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 5)); + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 5)); } public ImotiCelebrantOfBounty(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/i/ImpactResonance.java b/Mage.Sets/src/mage/cards/i/ImpactResonance.java index 8003e787778a..ab458592a25f 100644 --- a/Mage.Sets/src/mage/cards/i/ImpactResonance.java +++ b/Mage.Sets/src/mage/cards/i/ImpactResonance.java @@ -96,8 +96,7 @@ public GreatestAmountOfDamageWatcher() { @Override public void watch(GameEvent event, Game game) { switch(event.getType()) { - case DAMAGED_CREATURE: - case DAMAGED_PLANESWALKER: + case DAMAGED_PERMANENT: case DAMAGED_PLAYER: if (event.getAmount() > damageAmount) { damageAmount = event.getAmount(); diff --git a/Mage.Sets/src/mage/cards/i/ImpassionedOrator.java b/Mage.Sets/src/mage/cards/i/ImpassionedOrator.java index ff27dbdd290f..46940915ab8e 100644 --- a/Mage.Sets/src/mage/cards/i/ImpassionedOrator.java +++ b/Mage.Sets/src/mage/cards/i/ImpassionedOrator.java @@ -9,7 +9,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/i/ImpelledGiant.java b/Mage.Sets/src/mage/cards/i/ImpelledGiant.java index 7d1e815750d0..e7ce36bad96d 100644 --- a/Mage.Sets/src/mage/cards/i/ImpelledGiant.java +++ b/Mage.Sets/src/mage/cards/i/ImpelledGiant.java @@ -21,7 +21,7 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/i/ImperialAerosaur.java b/Mage.Sets/src/mage/cards/i/ImperialAerosaur.java index 28cc550bdacc..350bbcbffde4 100644 --- a/Mage.Sets/src/mage/cards/i/ImperialAerosaur.java +++ b/Mage.Sets/src/mage/cards/i/ImperialAerosaur.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/i/ImperialHellkite.java b/Mage.Sets/src/mage/cards/i/ImperialHellkite.java index 4cff0fe3027c..5302c3337b1b 100644 --- a/Mage.Sets/src/mage/cards/i/ImperialHellkite.java +++ b/Mage.Sets/src/mage/cards/i/ImperialHellkite.java @@ -36,7 +36,7 @@ public ImperialHellkite(UUID ownerId, CardSetInfo setInfo) { // When Imperial Hellkite is turned face up, you may search your library for a Dragon card, reveal it, and put it into your hand. If you do, shuffle your library. Effect effect = new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 1, new FilterBySubtypeCard(SubType.DRAGON)), true, true); - effect.setText("you may search your library for a Dragon card, reveal it, and put it into your hand. If you do, shuffle your library"); + effect.setText("you may search your library for a Dragon card, reveal it, and put it into your hand. If you do, shuffle"); this.addAbility(new TurnedFaceUpSourceTriggeredAbility(effect)); } diff --git a/Mage.Sets/src/mage/cards/i/Imperiosaur.java b/Mage.Sets/src/mage/cards/i/Imperiosaur.java index f98024e3354c..0a82b2ddba9c 100644 --- a/Mage.Sets/src/mage/cards/i/Imperiosaur.java +++ b/Mage.Sets/src/mage/cards/i/Imperiosaur.java @@ -64,7 +64,7 @@ public ImperiosaurStaticAbility copy() { @Override public String getRule() { - return "Spend only mana produced by basic lands to cast {this}."; + return "Spend only mana produced by basic lands to cast this spell."; } } diff --git a/Mage.Sets/src/mage/cards/i/ImprisonedInTheMoon.java b/Mage.Sets/src/mage/cards/i/ImprisonedInTheMoon.java index 2271a71ada72..40abc9d36145 100644 --- a/Mage.Sets/src/mage/cards/i/ImprisonedInTheMoon.java +++ b/Mage.Sets/src/mage/cards/i/ImprisonedInTheMoon.java @@ -1,6 +1,5 @@ package mage.cards.i; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousEffectImpl; @@ -9,21 +8,16 @@ import mage.abilities.mana.ColorlessManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class ImprisonedInTheMoon extends CardImpl { @@ -31,9 +25,11 @@ public final class ImprisonedInTheMoon extends CardImpl { private static final FilterPermanent filter = new FilterPermanent("creature, land, or planeswalker"); static { - filter.add(Predicates.or(CardType.CREATURE.getPredicate(), + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), CardType.LAND.getPredicate(), - CardType.PLANESWALKER.getPredicate())); + CardType.PLANESWALKER.getPredicate() + )); } public ImprisonedInTheMoon(UUID ownerId, CardSetInfo setInfo) { @@ -48,7 +44,7 @@ public ImprisonedInTheMoon(UUID ownerId, CardSetInfo setInfo) { this.addAbility(ability); // Enchanted permanent is a colorless land with "{T}: Add {C}" and loses all other card types and abilities. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BecomesColorlessLandEffect())); + this.addAbility(new SimpleStaticAbility(new ImprisonedInTheMoonEffect())); } private ImprisonedInTheMoon(final ImprisonedInTheMoon card) { @@ -61,14 +57,15 @@ public ImprisonedInTheMoon copy() { } } -class BecomesColorlessLandEffect extends ContinuousEffectImpl { +class ImprisonedInTheMoonEffect extends ContinuousEffectImpl { - public BecomesColorlessLandEffect() { + ImprisonedInTheMoonEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); - this.staticText = "Enchanted permanent is a colorless land with \"{T}: Add {C}\" and loses all other card types and abilities"; + this.staticText = "Enchanted permanent is a colorless land " + + "with \"{T}: Add {C}\" and loses all other card types and abilities"; } - public BecomesColorlessLandEffect(final BecomesColorlessLandEffect effect) { + private ImprisonedInTheMoonEffect(final ImprisonedInTheMoonEffect effect) { super(effect); } @@ -78,47 +75,49 @@ public boolean apply(Game game, Ability source) { } @Override - public BecomesColorlessLandEffect copy() { - return new BecomesColorlessLandEffect(this); + public ImprisonedInTheMoonEffect copy() { + return new ImprisonedInTheMoonEffect(this); } @Override public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - Permanent enchantment = game.getPermanent(source.getSourceId()); - if (enchantment != null - && enchantment.getAttachedTo() != null) { - Permanent permanent = game.getPermanent(enchantment.getAttachedTo()); - if (permanent != null) { - switch (layer) { - case TypeChangingEffects_4: - // 305.7 Note that this doesn't remove any abilities that were granted to the land by other effects - // So the ability removing has to be done before Layer 6 - permanent.removeAllAbilities(source.getSourceId(), game); - permanent.getCardType().clear(); - permanent.addCardType(CardType.LAND); - break; - case ColorChangingEffects_5: - permanent.getColor(game).setWhite(false); - permanent.getColor(game).setGreen(false); - permanent.getColor(game).setBlack(false); - permanent.getColor(game).setBlue(false); - permanent.getColor(game).setRed(false); - break; - case AbilityAddingRemovingEffects_6: - permanent.removeAllAbilities(source.getSourceId(), game); - permanent.addAbility(new ColorlessManaAbility(), source.getSourceId(), game); - break; - } - return true; - } + Permanent enchantment = source.getSourcePermanentIfItStillExists(game); + if (enchantment == null + || enchantment.getAttachedTo() == null) { + return false; } - return false; + Permanent permanent = game.getPermanent(enchantment.getAttachedTo()); + if (permanent == null) { + return false; + } + switch (layer) { + case TypeChangingEffects_4: + // 305.7 Note that this doesn't remove any abilities that were granted to the land by other effects + // So the ability removing has to be done before Layer 6 + permanent.removeAllAbilities(source.getSourceId(), game); + permanent.getCardType().clear(); + permanent.addCardType(CardType.LAND); + permanent.retainAllLandSubTypes(game); + break; + case ColorChangingEffects_5: + permanent.getColor(game).setWhite(false); + permanent.getColor(game).setBlue(false); + permanent.getColor(game).setBlack(false); + permanent.getColor(game).setRed(false); + permanent.getColor(game).setGreen(false); + break; + case AbilityAddingRemovingEffects_6: + permanent.removeAllAbilities(source.getSourceId(), game); + permanent.addAbility(new ColorlessManaAbility(), source.getSourceId(), game); + break; + } + return true; } @Override public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6 - || layer == Layer.ColorChangingEffects_5 + return layer == Layer.AbilityAddingRemovingEffects_6 + || layer == Layer.ColorChangingEffects_5 || layer == Layer.TypeChangingEffects_4; } } diff --git a/Mage.Sets/src/mage/cards/i/ImpsMischief.java b/Mage.Sets/src/mage/cards/i/ImpsMischief.java index c3c5dbc7be11..68c4809ce467 100644 --- a/Mage.Sets/src/mage/cards/i/ImpsMischief.java +++ b/Mage.Sets/src/mage/cards/i/ImpsMischief.java @@ -10,7 +10,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.NumberOfTargetsPredicate; +import mage.filter.predicate.other.NumberOfTargetsPredicate; import mage.game.Game; import mage.game.stack.Spell; import mage.players.Player; @@ -54,7 +54,7 @@ class ImpsMischiefLoseLifeEffect extends OneShotEffect { public ImpsMischiefLoseLifeEffect() { super(Outcome.LoseLife); - staticText = "You lose life equal to that spell's converted mana cost"; + staticText = "You lose life equal to that spell's mana value"; } public ImpsMischiefLoseLifeEffect(final ImpsMischiefLoseLifeEffect effect) { @@ -72,7 +72,7 @@ public boolean apply(Game game, Ability source) { if (spell != null) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { - player.loseLife(spell.getConvertedManaCost(), game, source, false); + player.loseLife(spell.getManaValue(), game, source, false); return true; } } diff --git a/Mage.Sets/src/mage/cards/i/ImpulsiveManeuvers.java b/Mage.Sets/src/mage/cards/i/ImpulsiveManeuvers.java index e0183c6a8365..125626947ff6 100644 --- a/Mage.Sets/src/mage/cards/i/ImpulsiveManeuvers.java +++ b/Mage.Sets/src/mage/cards/i/ImpulsiveManeuvers.java @@ -68,8 +68,7 @@ public ImpulsiveManeuversEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE || - event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER || + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT || event.getType() == GameEvent.EventType.DAMAGE_PLAYER; } diff --git a/Mage.Sets/src/mage/cards/i/InSearchOfGreatness.java b/Mage.Sets/src/mage/cards/i/InSearchOfGreatness.java index 8ba640a75b9b..66b78be6045c 100644 --- a/Mage.Sets/src/mage/cards/i/InSearchOfGreatness.java +++ b/Mage.Sets/src/mage/cards/i/InSearchOfGreatness.java @@ -12,7 +12,7 @@ import mage.constants.*; import mage.filter.common.FilterPermanentCard; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -49,8 +49,8 @@ class InSearchOfGreatnessEffect extends OneShotEffect { public InSearchOfGreatnessEffect() { super(Outcome.PlayForFree); - staticText = "you may cast a permanent spell from your hand with converted mana cost " - + "equal to 1 plus the highest converted mana cost among other permanents you control " + staticText = "you may cast a permanent spell from your hand with mana value " + + "equal to 1 plus the highest mana value among other permanents you control " + "without paying its mana cost. If you don't, scry 1"; } @@ -73,7 +73,7 @@ public boolean apply(Game game, Ability source) { UUID permId = source.getSourceId(); for (Permanent permanent : game.getBattlefield().getAllActivePermanents(controller.getId())) { if (permanent != null && !permanent.getId().equals(permId)) { - int permCmc = permanent.getConvertedManaCost(); + int permCmc = permanent.getManaValue(); if (permCmc > cmc) { cmc = permCmc; } @@ -83,7 +83,7 @@ public boolean apply(Game game, Ability source) { + ++cmc + "?", source, game)) { FilterPermanentCard filter = new FilterPermanentCard("permanent spell from your hand"); filter.add(Predicates.not(CardType.LAND.getPredicate())); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, cmc)); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, cmc)); TargetCardInHand target = new TargetCardInHand(filter); if (controller.chooseTarget(outcome, target, source, game)) { Card card = game.getCard(target.getFirstTarget()); diff --git a/Mage.Sets/src/mage/cards/i/InTheEyeOfChaos.java b/Mage.Sets/src/mage/cards/i/InTheEyeOfChaos.java index c79881788a17..fef374ce4191 100644 --- a/Mage.Sets/src/mage/cards/i/InTheEyeOfChaos.java +++ b/Mage.Sets/src/mage/cards/i/InTheEyeOfChaos.java @@ -49,7 +49,7 @@ class InTheEyeOfChaosEffect extends OneShotEffect { InTheEyeOfChaosEffect() { super(Outcome.Detriment); - this.staticText = "counter it unless that player pays {X}, where X is its converted mana cost"; + this.staticText = "counter it unless that player pays {X}, where X is its mana value"; } InTheEyeOfChaosEffect(final InTheEyeOfChaosEffect effect) { @@ -67,7 +67,7 @@ public boolean apply(Game game, Ability source) { if (spell != null) { Player player = game.getPlayer(spell.getControllerId()); if (player != null) { - Cost cost = ManaUtil.createManaCost(spell.getConvertedManaCost(), true); + Cost cost = ManaUtil.createManaCost(spell.getManaValue(), true); if (!cost.pay(source, game, source, player.getId(), false)) { game.getStack().counter(spell.getId(), source, game); } diff --git a/Mage.Sets/src/mage/cards/i/InallaArchmageRitualist.java b/Mage.Sets/src/mage/cards/i/InallaArchmageRitualist.java index 8faff27d1866..e0c00bf45801 100644 --- a/Mage.Sets/src/mage/cards/i/InallaArchmageRitualist.java +++ b/Mage.Sets/src/mage/cards/i/InallaArchmageRitualist.java @@ -28,7 +28,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TappedPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; diff --git a/Mage.Sets/src/mage/cards/i/InameAsOne.java b/Mage.Sets/src/mage/cards/i/InameAsOne.java index be05f87a2f77..9df76aa7fdff 100644 --- a/Mage.Sets/src/mage/cards/i/InameAsOne.java +++ b/Mage.Sets/src/mage/cards/i/InameAsOne.java @@ -52,7 +52,7 @@ public InameAsOne(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new ConditionalInterveningIfTriggeredAbility( new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 1, filter)), true), CastFromHandSourcePermanentCondition.instance, - "When {this} enters the battlefield, if you cast it from your hand, you may search your library for a Spirit permanent card, put it onto the battlefield, then shuffle your library."), + "When {this} enters the battlefield, if you cast it from your hand, you may search your library for a Spirit permanent card, put it onto the battlefield, then shuffle."), new CastFromHandWatcher()); // When Iname as One dies, you may exile it. If you do, return target Spirit permanent card from your graveyard to the battlefield. diff --git a/Mage.Sets/src/mage/cards/i/InameDeathAspect.java b/Mage.Sets/src/mage/cards/i/InameDeathAspect.java index 128ad8df0800..14b22005a890 100644 --- a/Mage.Sets/src/mage/cards/i/InameDeathAspect.java +++ b/Mage.Sets/src/mage/cards/i/InameDeathAspect.java @@ -51,7 +51,7 @@ class InameDeathAspectEffect extends SearchEffect { public InameDeathAspectEffect() { super(new TargetCardInLibrary(0, Integer.MAX_VALUE, filter), Outcome.Neutral); - staticText = "search your library for any number of Spirit cards and put them into your graveyard. If you do, shuffle your library"; + staticText = "search your library for any number of Spirit cards, put them into your graveyard, then shuffle"; } public InameDeathAspectEffect(final InameDeathAspectEffect effect) { diff --git a/Mage.Sets/src/mage/cards/i/IncarnationTechnique.java b/Mage.Sets/src/mage/cards/i/IncarnationTechnique.java new file mode 100644 index 000000000000..3834d8b8aa6e --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IncarnationTechnique.java @@ -0,0 +1,80 @@ +package mage.cards.i; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.DemonstrateAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class IncarnationTechnique extends CardImpl { + + public IncarnationTechnique(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}"); + + // Demonstrate + this.addAbility(new DemonstrateAbility()); + + // Mill five cards, then return a creature card from your graveyard to the battlefield. + this.getSpellAbility().addEffect(new IncarnationTechniqueEffect()); + } + + private IncarnationTechnique(final IncarnationTechnique card) { + super(card); + } + + @Override + public IncarnationTechnique copy() { + return new IncarnationTechnique(this); + } +} + +class IncarnationTechniqueEffect extends OneShotEffect { + + IncarnationTechniqueEffect() { + super(Outcome.Benefit); + staticText = "mill five cards, then return a creature card from your graveyard to the battlefield"; + } + + private IncarnationTechniqueEffect(final IncarnationTechniqueEffect effect) { + super(effect); + } + + @Override + public IncarnationTechniqueEffect copy() { + return new IncarnationTechniqueEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + player.millCards(5, source, game); + TargetCard target = new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD); + target.setNotTarget(true); + if (!target.canChoose(source.getSourceId(), source.getControllerId(), game)) { + return true; + } + player.choose(outcome, target, source.getSourceId(), game); + Card card = game.getCard(target.getFirstTarget()); + if (card != null) { + player.moveCards(card, Zone.BATTLEFIELD, source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/i/Incoming.java b/Mage.Sets/src/mage/cards/i/Incoming.java index d244352ea79b..6ffb8f959443 100644 --- a/Mage.Sets/src/mage/cards/i/Incoming.java +++ b/Mage.Sets/src/mage/cards/i/Incoming.java @@ -53,7 +53,7 @@ class IncomingEffect extends OneShotEffect { public IncomingEffect() { super(Outcome.Detriment); - this.staticText = "Each player searches their library for any number of artifact, creature, enchantment, and/or land cards, puts them onto the battlefield, then shuffles their library"; + this.staticText = "Each player searches their library for any number of artifact, creature, enchantment, and/or land cards, puts them onto the battlefield, then shuffles"; } public IncomingEffect(final IncomingEffect effect) { diff --git a/Mage.Sets/src/mage/cards/i/IncreasingAmbition.java b/Mage.Sets/src/mage/cards/i/IncreasingAmbition.java index c843e75e54f0..eae6ce33edc8 100644 --- a/Mage.Sets/src/mage/cards/i/IncreasingAmbition.java +++ b/Mage.Sets/src/mage/cards/i/IncreasingAmbition.java @@ -1,36 +1,40 @@ - package mage.cards.i; -import java.util.UUID; import mage.abilities.Ability; +import mage.abilities.condition.Condition; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.SearchEffect; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.abilities.keyword.FlashbackAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.TimingRule; import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.stack.Spell; -import mage.players.Player; import mage.target.common.TargetCardInLibrary; +import java.util.UUID; + /** - * * @author BetaSteward */ public final class IncreasingAmbition extends CardImpl { public IncreasingAmbition(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}"); // Search your library for a card and put that card into your hand. If this spell was cast from a graveyard, instead search your library for two cards and put those cards into your hand. Then shuffle your library. - this.getSpellAbility().addEffect(new IncreasingAmbitionEffect()); + this.getSpellAbility().addEffect(new ConditionalOneShotEffect( + new SearchLibraryPutInHandEffect(new TargetCardInLibrary(2, StaticFilters.FILTER_CARD)), + new SearchLibraryPutInHandEffect(new TargetCardInLibrary()), + IncreasingAmbitionCondition.instance, "Search your library for a card " + + "and put that card into your hand. If this spell was cast from a graveyard, " + + "instead search your library for two cards and put those cards into your hand. " + + "Then shuffle." + )); // Flashback {7}{B} this.addAbility(new FlashbackAbility(new ManaCostsImpl("{7}{B}"), TimingRule.SORCERY)); @@ -46,50 +50,12 @@ public IncreasingAmbition copy() { } } -class IncreasingAmbitionEffect extends SearchEffect { - - public IncreasingAmbitionEffect() { - super(new TargetCardInLibrary(), Outcome.DrawCard); - staticText = "Search your library for a card and put that card into your hand. If this spell was cast from a graveyard, instead search your library for two cards and put those cards into your hand. Then shuffle your library"; - } - - public IncreasingAmbitionEffect(final IncreasingAmbitionEffect effect) { - super(effect); - } - - @Override - public IncreasingAmbitionEffect copy() { - return new IncreasingAmbitionEffect(this); - } +enum IncreasingAmbitionCondition implements Condition { + instance; @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - Spell spell = (Spell) game.getStack().getStackObject(source.getSourceId()); - if (spell != null) { - if (spell.getFromZone() == Zone.GRAVEYARD) { - target = new TargetCardInLibrary(2, new FilterCard()); - } - else { - target = new TargetCardInLibrary(); - } - if (player.searchLibrary(target, source, game)) { - if (!target.getTargets().isEmpty()) { - for (UUID cardId: target.getTargets()) { - Card card = player.getLibrary().remove(cardId, game); - if (card != null) { - card.moveToZone(Zone.HAND, source, game, false); - } - } - } - } - // shuffle anyway - player.shuffleLibrary(source, game); - return true; - } - } - return false; + Spell spell = game.getSpell(source.getSourceId()); + return spell != null && spell.getFromZone() == Zone.GRAVEYARD; } - } diff --git a/Mage.Sets/src/mage/cards/i/IncreasingVengeance.java b/Mage.Sets/src/mage/cards/i/IncreasingVengeance.java index 2c3df4e3abf7..166fef9d0236 100644 --- a/Mage.Sets/src/mage/cards/i/IncreasingVengeance.java +++ b/Mage.Sets/src/mage/cards/i/IncreasingVengeance.java @@ -12,7 +12,6 @@ import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.stack.Spell; -import mage.game.stack.StackObject; import mage.players.Player; import mage.target.Target; import mage.target.TargetSpell; @@ -81,10 +80,7 @@ public boolean apply(Game game, Ability source) { if (sourceSpell != null && sourceSpell.getFromZone() == Zone.GRAVEYARD) { copies++; } - StackObject stackObjectCopy = spell.createCopyOnStack(game, source, source.getControllerId(), true, copies); - if (stackObjectCopy instanceof Spell) { - game.informPlayers(controller.getLogName() + ((Spell) stackObjectCopy).getActivatedMessage(game)); - } + spell.createCopyOnStack(game, source, source.getControllerId(), true, copies); return true; } diff --git a/Mage.Sets/src/mage/cards/i/IncrementalBlight.java b/Mage.Sets/src/mage/cards/i/IncrementalBlight.java index c539174f4f1e..379969847c0b 100644 --- a/Mage.Sets/src/mage/cards/i/IncrementalBlight.java +++ b/Mage.Sets/src/mage/cards/i/IncrementalBlight.java @@ -36,7 +36,7 @@ import mage.constants.Outcome; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.Target; diff --git a/Mage.Sets/src/mage/cards/i/IncrementalGrowth.java b/Mage.Sets/src/mage/cards/i/IncrementalGrowth.java index b78edfac8cb4..fa61258c9077 100644 --- a/Mage.Sets/src/mage/cards/i/IncrementalGrowth.java +++ b/Mage.Sets/src/mage/cards/i/IncrementalGrowth.java @@ -9,7 +9,7 @@ import mage.constants.Outcome; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.Target; diff --git a/Mage.Sets/src/mage/cards/i/IndomitableCreativity.java b/Mage.Sets/src/mage/cards/i/IndomitableCreativity.java index 840f72fad91a..3d911d0f4421 100644 --- a/Mage.Sets/src/mage/cards/i/IndomitableCreativity.java +++ b/Mage.Sets/src/mage/cards/i/IndomitableCreativity.java @@ -71,7 +71,7 @@ public IndomitableCreativityEffect() { "For each permanent destroyed this way, " + "its controller reveals cards from the top of their library" + " until an artifact or creature card is revealed and exiles that card. " + - "Those players put the exiled card onto the battlefield, then shuffle their libraries"; + "Those players put the exiled card onto the battlefield, then shuffle"; } public IndomitableCreativityEffect(final IndomitableCreativityEffect effect) { diff --git a/Mage.Sets/src/mage/cards/i/InduceDespair.java b/Mage.Sets/src/mage/cards/i/InduceDespair.java index 3cdab1c3a97d..2b87a2c8ed6b 100644 --- a/Mage.Sets/src/mage/cards/i/InduceDespair.java +++ b/Mage.Sets/src/mage/cards/i/InduceDespair.java @@ -51,7 +51,7 @@ class InduceDespairEffect extends OneShotEffect { public InduceDespairEffect() { super(Outcome.UnboostCreature); - staticText = "Target creature gets -X/-X until end of turn, where X is the revealed card's converted mana cost"; + staticText = "Target creature gets -X/-X until end of turn, where X is the revealed card's mana value"; } public InduceDespairEffect(InduceDespairEffect effect) { @@ -63,7 +63,7 @@ public boolean apply(Game game, Ability source) { RevealTargetFromHandCost cost = (RevealTargetFromHandCost) source.getCosts().get(0); Permanent creature = game.getPermanent(targetPointer.getFirst(game, source)); if (cost != null && creature != null) { - int cmcBoost = -1 * cost.convertedManaCosts; + int cmcBoost = -1 * cost.manaValues; ContinuousEffect effect = new BoostTargetEffect(cmcBoost, cmcBoost, Duration.EndOfTurn); effect.setTargetPointer(new FixedTarget(creature.getId(), creature.getZoneChangeCounter(game))); game.addEffect(effect, source); diff --git a/Mage.Sets/src/mage/cards/i/InduceParanoia.java b/Mage.Sets/src/mage/cards/i/InduceParanoia.java index 5ac7b094da60..724e6c0ab6cd 100644 --- a/Mage.Sets/src/mage/cards/i/InduceParanoia.java +++ b/Mage.Sets/src/mage/cards/i/InduceParanoia.java @@ -30,7 +30,7 @@ public InduceParanoia(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new InduceParanoiaEffect(), new CounterTargetEffect(), - new ManaWasSpentCondition(ColoredManaSymbol.B), "Counter target spell. If {B} was spent to cast this spell, that spell's controller puts the top X cards of their library into their graveyard, where X is the spell's converted mana cost.")); + new ManaWasSpentCondition(ColoredManaSymbol.B), "Counter target spell. If {B} was spent to cast this spell, that spell's controller mills X cards, where X is the spell's mana value.")); // Counter target spell. this.getSpellAbility().addTarget(new TargetSpell()); @@ -50,7 +50,7 @@ class InduceParanoiaEffect extends OneShotEffect { InduceParanoiaEffect() { super(Outcome.Detriment); - this.staticText = "Counter target spell. If {B} was spent to cast this spell, that spell's controller mills X cards, where X is the spell's converted mana cost."; + this.staticText = "Counter target spell. If {B} was spent to cast this spell, that spell's controller mills X cards, where X is the spell's mana value."; } InduceParanoiaEffect(final InduceParanoiaEffect effect) { @@ -67,7 +67,7 @@ public boolean apply(Game game, Ability source) { StackObject spell = game.getStack().getStackObject(targetPointer.getFirst(game, source)); if (spell != null) { game.getStack().counter(spell.getId(), source, game); - int spellCMC = spell.getConvertedManaCost(); + int spellCMC = spell.getManaValue(); Player player = game.getPlayer(spell.getControllerId()); if (player != null) { player.millCards(spellCMC, source, game); diff --git a/Mage.Sets/src/mage/cards/i/InducedAmnesia.java b/Mage.Sets/src/mage/cards/i/InducedAmnesia.java index 0ed3d030d0a6..7d857e2fcb02 100644 --- a/Mage.Sets/src/mage/cards/i/InducedAmnesia.java +++ b/Mage.Sets/src/mage/cards/i/InducedAmnesia.java @@ -1,26 +1,26 @@ - package mage.cards.i; -import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.PutIntoGraveFromBattlefieldSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; import mage.game.ExileZone; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPlayer; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class InducedAmnesia extends CardImpl { @@ -49,12 +49,12 @@ public InducedAmnesia copy() { class InducedAmnesiaExileEffect extends OneShotEffect { - public InducedAmnesiaExileEffect() { + InducedAmnesiaExileEffect() { super(Outcome.Detriment); this.staticText = "target player exiles all the cards in their hand face down, then draws that many cards"; } - public InducedAmnesiaExileEffect(final InducedAmnesiaExileEffect effect) { + private InducedAmnesiaExileEffect(final InducedAmnesiaExileEffect effect) { super(effect); } @@ -66,33 +66,36 @@ public InducedAmnesiaExileEffect copy() { @Override public boolean apply(Game game, Ability source) { Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source)); - Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (targetPlayer != null && sourcePermanent != null) { - int numberOfCards = targetPlayer.getHand().size(); - if (numberOfCards > 0) { - UUID exileId = CardUtil.getCardExileZoneId(game, source); - for (Card card : targetPlayer.getHand().getCards(game)) { - card.moveToExile(exileId, sourcePermanent.getName(), source, game); - card.setFaceDown(true, game); - } - game.informPlayers(sourcePermanent.getLogName() + ": " + targetPlayer.getLogName() + " exiles their hand face down (" + numberOfCards + "card" + (numberOfCards > 1 ? "s" : "") + ')'); - game.getState().processAction(game); - targetPlayer.drawCards(numberOfCards, source, game); - } - return true; + MageObject sourceObject = source.getSourceObject(game); + if (targetPlayer == null || sourceObject == null) { + return false; + } + int numberOfCards = targetPlayer.getHand().size(); + if (numberOfCards < 1) { + return false; } - return false; + Cards cards = new CardsImpl(targetPlayer.getHand()); + targetPlayer.moveCardsToExile( + cards.getCards(game), source, game, false, + CardUtil.getExileZoneId(game, source), sourceObject.getIdName() + ); + cards.getCards(game) + .stream() + .filter(card -> game.getState().getZone(card.getId()) == Zone.EXILED) + .forEach(card -> card.setFaceDown(true, game)); + targetPlayer.drawCards(numberOfCards, source, game); + return true; } } class InducedAmnesiaReturnEffect extends OneShotEffect { - public InducedAmnesiaReturnEffect() { + InducedAmnesiaReturnEffect() { super(Outcome.Benefit); this.staticText = "return the exiled cards to their owner's hand"; } - public InducedAmnesiaReturnEffect(final InducedAmnesiaReturnEffect effect) { + private InducedAmnesiaReturnEffect(final InducedAmnesiaReturnEffect effect) { super(effect); } @@ -104,23 +107,10 @@ public InducedAmnesiaReturnEffect copy() { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (controller != null && sourcePermanent != null) { - UUID exileId = CardUtil.getCardExileZoneId(game, source.getSourceId(), true); - int numberOfCards = 0; - ExileZone exileZone = game.getExile().getExileZone(exileId); - if (exileZone != null) { - for (Card card : exileZone.getCards(game)) { - numberOfCards++; - card.moveToZone(Zone.HAND, source, game, true); - card.setFaceDown(false, game); - } - } - if (numberOfCards > 0) { - game.informPlayers(sourcePermanent.getLogName() + ": " + controller.getLogName() + " returns " + numberOfCards + " card" + (numberOfCards > 1 ? "s" : "") + " from exile to hand"); - } - return true; - } - return false; + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + return controller != null + && exileZone != null + && !exileZone.isEmpty() + && controller.moveCards(exileZone, Zone.HAND, source, game); } } diff --git a/Mage.Sets/src/mage/cards/i/InfectiousBloodlust.java b/Mage.Sets/src/mage/cards/i/InfectiousBloodlust.java index 2ab452544fb0..edf4b74d7448 100644 --- a/Mage.Sets/src/mage/cards/i/InfectiousBloodlust.java +++ b/Mage.Sets/src/mage/cards/i/InfectiousBloodlust.java @@ -1,11 +1,8 @@ - package mage.cards.i; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.DiesAttachedTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.combat.AttacksIfAbleAttachedEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; @@ -15,20 +12,16 @@ import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AttachmentType; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.FilterCard; import mage.filter.predicate.mageobject.NamePredicate; import mage.target.TargetPermanent; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class InfectiousBloodlust extends CardImpl { @@ -40,7 +33,7 @@ public final class InfectiousBloodlust extends CardImpl { } public InfectiousBloodlust(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{R}"); this.subtype.add(SubType.AURA); // Enchant creature @@ -51,20 +44,19 @@ public InfectiousBloodlust(UUID ownerId, CardSetInfo setInfo) { this.addAbility(ability); // Enchanted creature gets +2/+1, has haste, and attacks each turn if able. - Effect effect = new BoostEnchantedEffect(2, 1); - effect.setText("Enchanted creature gets +2/+1"); - ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); - effect = new GainAbilityAttachedEffect(HasteAbility.getInstance(), AttachmentType.AURA); - effect.setText("has haste"); - ability.addEffect(effect); - effect = new AttacksIfAbleAttachedEffect(Duration.WhileOnBattlefield, AttachmentType.AURA); - effect.setText("and attacks each turn if able"); - ability.addEffect(effect); + ability = new SimpleStaticAbility(new BoostEnchantedEffect(2, 1)); + ability.addEffect(new GainAbilityAttachedEffect( + HasteAbility.getInstance(), AttachmentType.AURA + ).setText(", has haste")); + ability.addEffect(new AttacksIfAbleAttachedEffect( + Duration.WhileOnBattlefield, AttachmentType.AURA + ).setText(", and attacks each combat if able")); this.addAbility(ability); // When enchanted creature dies, you may search your library for a card named Infectious Bloodlust, reveal it, put it into your hand, then shuffle your library. - TargetCardInLibrary target = new TargetCardInLibrary(0, 1, filter); - this.addAbility(new DiesAttachedTriggeredAbility(new SearchLibraryPutInHandEffect(target, true, true), "enchanted creature", true)); + this.addAbility(new DiesAttachedTriggeredAbility(new SearchLibraryPutInHandEffect( + new TargetCardInLibrary(filter), true, true + ), "enchanted creature", true)); } private InfectiousBloodlust(final InfectiousBloodlust card) { diff --git a/Mage.Sets/src/mage/cards/i/InfectiousCurse.java b/Mage.Sets/src/mage/cards/i/InfectiousCurse.java index c7efdb6cf951..cc822992d5bf 100644 --- a/Mage.Sets/src/mage/cards/i/InfectiousCurse.java +++ b/Mage.Sets/src/mage/cards/i/InfectiousCurse.java @@ -2,7 +2,7 @@ import mage.abilities.Ability; import mage.abilities.SpellAbility; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BeginningOfUpkeepAttachedTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.GainLifeEffect; @@ -13,12 +13,9 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.stack.Spell; -import mage.players.Player; import mage.target.TargetPlayer; -import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; import java.util.Objects; @@ -48,9 +45,11 @@ public InfectiousCurse(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new InfectiousCurseCostReductionEffect())); // At the beginning of enchanted player's upkeep, that player loses 1 life and you gain 1 life. - InfectiousCurseAbility curseAbility = new InfectiousCurseAbility(); - curseAbility.addEffect(new GainLifeEffect(1)); - this.addAbility(curseAbility); + Ability ability = new BeginningOfUpkeepAttachedTriggeredAbility( + new LoseLifeTargetEffect(1).setText("that player loses 1 life") + ); + ability.addEffect(new GainLifeEffect(1).concatBy("and")); + this.addAbility(ability); } private InfectiousCurse(final InfectiousCurse card) { @@ -63,54 +62,14 @@ public InfectiousCurse copy() { } } -class InfectiousCurseAbility extends TriggeredAbilityImpl { - - public InfectiousCurseAbility() { - super(Zone.BATTLEFIELD, new LoseLifeTargetEffect(1)); - } - - public InfectiousCurseAbility(final InfectiousCurseAbility ability) { - super(ability); - } - - @Override - public InfectiousCurseAbility copy() { - return new InfectiousCurseAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.UPKEEP_STEP_PRE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent enchantment = game.getPermanent(this.sourceId); - if (enchantment != null && enchantment.getAttachedTo() != null) { - Player player = game.getPlayer(enchantment.getAttachedTo()); - if (player != null && game.isActivePlayer(player.getId())) { - this.getEffects().get(0).setTargetPointer(new FixedTarget(player.getId())); - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "At the beginning of enchanted player's upkeep, that player loses 1 life and you gain 1 life."; - } - -} - class InfectiousCurseCostReductionEffect extends CostModificationEffectImpl { - public InfectiousCurseCostReductionEffect() { + InfectiousCurseCostReductionEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST); this.staticText = "Spells you cast that target enchanted player cost {1} less to cast"; } - protected InfectiousCurseCostReductionEffect(InfectiousCurseCostReductionEffect effect) { + private InfectiousCurseCostReductionEffect(InfectiousCurseCostReductionEffect effect) { super(effect); } diff --git a/Mage.Sets/src/mage/cards/i/InfernalGenesis.java b/Mage.Sets/src/mage/cards/i/InfernalGenesis.java index 89b387f0bbc2..dfd44f3e29ed 100644 --- a/Mage.Sets/src/mage/cards/i/InfernalGenesis.java +++ b/Mage.Sets/src/mage/cards/i/InfernalGenesis.java @@ -47,7 +47,7 @@ class InfernalGenesisEffect extends OneShotEffect { InfernalGenesisEffect() { super(Outcome.PutCreatureInPlay); staticText = "that player mills a card. Then they create X 1/1 black Minion creature tokens, " + - "where X is the milled card's converted mana cost."; + "where X is the milled card's mana value."; } private InfernalGenesisEffect(final InfernalGenesisEffect effect) { @@ -62,7 +62,7 @@ public boolean apply(Game game, Ability source) { .getCards(game) .stream() .filter(Objects::nonNull) - .mapToInt(MageObject::getConvertedManaCost) + .mapToInt(MageObject::getManaValue) .sum(); if (totalCMC > 0) { token.putOntoBattlefield(totalCMC, game, source, player.getId()); diff --git a/Mage.Sets/src/mage/cards/i/InfernalKirin.java b/Mage.Sets/src/mage/cards/i/InfernalKirin.java index a2c770de0727..307eb2a50f62 100644 --- a/Mage.Sets/src/mage/cards/i/InfernalKirin.java +++ b/Mage.Sets/src/mage/cards/i/InfernalKirin.java @@ -55,7 +55,7 @@ class InfernalKirinEffect extends OneShotEffect { InfernalKirinEffect() { super(Outcome.Detriment); - this.staticText = "target player reveals their hand and discards all cards with that spell's converted mana cost"; + this.staticText = "target player reveals their hand and discards all cards with that spell's mana value"; } private InfernalKirinEffect(final InfernalKirinEffect effect) { @@ -73,7 +73,7 @@ public boolean apply(Game game, Ability source) { if (spell == null) { return false; } - int cmc = spell.getConvertedManaCost(); + int cmc = spell.getManaValue(); Player targetPlayer = null; for (Target target : source.getTargets()) { if (target instanceof TargetPlayer) { @@ -88,7 +88,7 @@ public boolean apply(Game game, Ability source) { } targetPlayer.revealCards(source, targetPlayer.getHand(), game); Cards cards = targetPlayer.getHand().copy(); - cards.removeIf(uuid -> game.getCard(uuid).getConvertedManaCost() != cmc); + cards.removeIf(uuid -> game.getCard(uuid).getManaValue() != cmc); targetPlayer.discard(cards, false, source, game); return true; } diff --git a/Mage.Sets/src/mage/cards/i/InfernalOffering.java b/Mage.Sets/src/mage/cards/i/InfernalOffering.java index 3c5309470378..210eb5926ea1 100644 --- a/Mage.Sets/src/mage/cards/i/InfernalOffering.java +++ b/Mage.Sets/src/mage/cards/i/InfernalOffering.java @@ -38,7 +38,7 @@ public InfernalOffering(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new InfernalOfferingSacrificeEffect()); // Choose an opponent. Return a creature card from your graveyard to the battlefield, then that player returns a creature card from their graveyard to the battlefield. - this.getSpellAbility().addEffect(new InfernalOfferingReturnEffect()); + this.getSpellAbility().addEffect(new InfernalOfferingReturnEffect().concatBy("
")); } private InfernalOffering(final InfernalOffering card) { diff --git a/Mage.Sets/src/mage/cards/i/InfernalReckoning.java b/Mage.Sets/src/mage/cards/i/InfernalReckoning.java index 37416a2f5ad9..fbd6653921cb 100644 --- a/Mage.Sets/src/mage/cards/i/InfernalReckoning.java +++ b/Mage.Sets/src/mage/cards/i/InfernalReckoning.java @@ -1,26 +1,28 @@ package mage.cards.i; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorlessPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.common.TargetCreaturePermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class InfernalReckoning extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("colorless creature"); + private static final FilterPermanent filter = new FilterCreaturePermanent("colorless creature"); static { filter.add(ColorlessPredicate.instance); @@ -31,7 +33,7 @@ public InfernalReckoning(UUID ownerId, CardSetInfo setInfo) { // Exile target colorless creature. You gain life equal to its power. this.getSpellAbility().addEffect(new InfernalJudgmentEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); } private InfernalReckoning(final InfernalReckoning card) { @@ -46,12 +48,12 @@ public InfernalReckoning copy() { class InfernalJudgmentEffect extends OneShotEffect { - public InfernalJudgmentEffect() { + InfernalJudgmentEffect() { super(Outcome.GainLife); staticText = "exile target colorless creature. You gain life equal to its power"; } - public InfernalJudgmentEffect(final InfernalJudgmentEffect effect) { + private InfernalJudgmentEffect(final InfernalJudgmentEffect effect) { super(effect); } @@ -62,14 +64,13 @@ public InfernalJudgmentEffect copy() { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); + Permanent permanent = game.getPermanent(source.getFirstTarget()); Player player = game.getPlayer(source.getControllerId()); if (permanent == null || player == null) { return false; } int creaturePower = permanent.getPower().getValue(); - permanent.moveToExile(null, null, source, game); - game.getState().processAction(game); + player.moveCards(permanent, Zone.EXILED, source, game); player.gainLife(creaturePower, game, source); return true; } diff --git a/Mage.Sets/src/mage/cards/i/InfernalTutor.java b/Mage.Sets/src/mage/cards/i/InfernalTutor.java index 9ffe0226caa1..489b25114ad8 100644 --- a/Mage.Sets/src/mage/cards/i/InfernalTutor.java +++ b/Mage.Sets/src/mage/cards/i/InfernalTutor.java @@ -39,7 +39,7 @@ public InfernalTutor(UUID ownerId, CardSetInfo setInfo) { Effect effect = new ConditionalOneShotEffect( new SearchLibraryPutInHandEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD), false, true), HellbentCondition.instance, - "

Hellbent — If you have no cards in hand, instead search your library for a card, put it into your hand, then shuffle your library"); + "

Hellbent — If you have no cards in hand, instead search your library for a card, put it into your hand, then shuffle"); this.getSpellAbility().addEffect(effect); } @@ -58,7 +58,7 @@ class InfernalTutorEffect extends OneShotEffect { public InfernalTutorEffect() { super(Outcome.Benefit); - this.staticText = "Reveal a card from your hand. Search your library for a card with the same name as that card, reveal it, put it into your hand, then shuffle your library"; + this.staticText = "Reveal a card from your hand. Search your library for a card with the same name as that card, reveal it, put it into your hand, then shuffle"; } public InfernalTutorEffect(final InfernalTutorEffect effect) { diff --git a/Mage.Sets/src/mage/cards/i/InfernoProject.java b/Mage.Sets/src/mage/cards/i/InfernoProject.java new file mode 100644 index 000000000000..b99734de1013 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InfernoProject.java @@ -0,0 +1,88 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.game.Game; +import mage.players.Player; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class InfernoProject extends CardImpl { + + private static final Hint hint = new ValueHint( + "Total mana value of instants and sorceries in your graveyard", InfernoProjectValue.instance + ); + + public InfernoProject(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{R}"); + + this.subtype.add(SubType.ELEMENTAL); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Inferno Project enters the battlefield with X +1/+1 counters on it, where X is the total mana value of instant and sorcery cards in your graveyard. + this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect( + CounterType.P1P1.createInstance(0), InfernoProjectValue.instance, false + ), "with X +1/+1 counters on it, where X is the total mana value " + + "of instant and sorcery cards in your graveyard").addHint(hint)); + } + + private InfernoProject(final InfernoProject card) { + super(card); + } + + @Override + public InfernoProject copy() { + return new InfernoProject(this); + } +} + +enum InfernoProjectValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + Player player = game.getPlayer(sourceAbility.getControllerId()); + if (player == null) { + return 0; + } + return player + .getGraveyard() + .getCards(game) + .stream() + .filter(Objects::nonNull) + .filter(MageObject::isInstantOrSorcery) + .mapToInt(MageObject::getManaValue) + .sum(); + } + + @Override + public InfernoProjectValue copy() { + return instance; + } + + @Override + public String getMessage() { + return ""; + } +} diff --git a/Mage.Sets/src/mage/cards/i/InfiniteObliteration.java b/Mage.Sets/src/mage/cards/i/InfiniteObliteration.java index 39e8a31d7952..eae9bd6c4305 100644 --- a/Mage.Sets/src/mage/cards/i/InfiniteObliteration.java +++ b/Mage.Sets/src/mage/cards/i/InfiniteObliteration.java @@ -84,7 +84,7 @@ public InfiniteObliterationEffect copy() { @Override public String getText(Mode mode) { - return "Name a creature card. " + super.getText(mode); + return "Choose a creature card name. " + super.getText(mode); } } diff --git a/Mage.Sets/src/mage/cards/i/InfuseWithVitality.java b/Mage.Sets/src/mage/cards/i/InfuseWithVitality.java new file mode 100644 index 000000000000..54bf61943415 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InfuseWithVitality.java @@ -0,0 +1,48 @@ +package mage.cards.i; + +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class InfuseWithVitality extends CardImpl { + + public InfuseWithVitality(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}{G}"); + + // Until end of turn, target creature gains deathtouch and "When this creature dies, return it to the battlefield tapped under its owner's control." + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + DeathtouchAbility.getInstance(), Duration.EndOfTurn + ).setText("Until end of turn, target creature gains deathtouch")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + new DiesSourceTriggeredAbility( + new ReturnSourceFromGraveyardToBattlefieldEffect(true, true), false + ), Duration.EndOfTurn, "and \"When this creature dies, " + + "return it to the battlefield tapped under its owner's control.\"" + )); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + + // You gain 2 life. + this.getSpellAbility().addEffect(new GainLifeEffect(2).concatBy("
")); + } + + private InfuseWithVitality(final InfuseWithVitality card) { + super(card); + } + + @Override + public InfuseWithVitality copy() { + return new InfuseWithVitality(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/IngeniousMastery.java b/Mage.Sets/src/mage/cards/i/IngeniousMastery.java new file mode 100644 index 000000000000..64f58a06c112 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IngeniousMastery.java @@ -0,0 +1,90 @@ +package mage.cards.i; + +import mage.abilities.Ability; +import mage.abilities.costs.AlternativeCostSourceAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.token.TreasureToken; +import mage.players.Player; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class IngeniousMastery extends CardImpl { + + public IngeniousMastery(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{2}{U}"); + + // You may pay {2}{U} rather than pay this spell's mana cost. + Ability costAbility = new AlternativeCostSourceAbility(new ManaCostsImpl<>("{2}{U}")); + this.addAbility(costAbility); + + // If the {2}{U} cost was paid, you draw three cards, then an opponent creates two Treasure tokens and they scry 2. If that cost wasn't paid, you draw X cards. + this.getSpellAbility().addEffect(new IngeniousMasteryEffect(costAbility.getOriginalId())); + } + + private IngeniousMastery(final IngeniousMastery card) { + super(card); + } + + @Override + public IngeniousMastery copy() { + return new IngeniousMastery(this); + } +} + +class IngeniousMasteryEffect extends OneShotEffect { + + private final UUID alternativeCostOriginalID; + + IngeniousMasteryEffect(UUID alternativeCostOriginalID) { + super(Outcome.Detriment); + staticText = "if the {2}{U} cost was paid, you draw three cards, then an opponent creates " + + "two Treasure tokens and they scry 2. If that cost wasn't paid, you draw X cards"; + this.alternativeCostOriginalID = alternativeCostOriginalID; + } + + private IngeniousMasteryEffect(IngeniousMasteryEffect effect) { + super(effect); + this.alternativeCostOriginalID = effect.alternativeCostOriginalID; + } + + @Override + public IngeniousMasteryEffect copy() { + return new IngeniousMasteryEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + if (!AlternativeCostSourceAbility.getActivatedStatus( + game, source, this.alternativeCostOriginalID, false + )) { + return player.drawCards(source.getManaCostsToPay().getX(), source, game) > 0; + } + + player.drawCards(3, source, game); + TargetOpponent targetOpponent = new TargetOpponent(true); + if (!player.chooseTarget(Outcome.DrawCard, targetOpponent, source, game)) { + return false; + } + Player opponent = game.getPlayer(targetOpponent.getFirstTarget()); + if (opponent == null) { + return false; + } + new TreasureToken().putOntoBattlefield(2, game, source, opponent.getId()); + opponent.scry(2, source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/i/InkEyesServantOfOni.java b/Mage.Sets/src/mage/cards/i/InkEyesServantOfOni.java index 42115c0db2ee..2ad0b5c7ba0f 100644 --- a/Mage.Sets/src/mage/cards/i/InkEyesServantOfOni.java +++ b/Mage.Sets/src/mage/cards/i/InkEyesServantOfOni.java @@ -15,7 +15,7 @@ import mage.constants.SuperType; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.game.events.DamagedPlayerEvent; import mage.game.events.GameEvent; @@ -99,6 +99,6 @@ public boolean checkTrigger(GameEvent event, Game game) { public String getRule() { return "Whenever {this} deals combat damage to a player, " + "you may put target creature card from that player's " - + "graveyard onto the battlefield under your control"; + + "graveyard onto the battlefield under your control."; } } diff --git a/Mage.Sets/src/mage/cards/i/InkTreaderNephilim.java b/Mage.Sets/src/mage/cards/i/InkTreaderNephilim.java index 86bbc8aa4c12..a0703ce003de 100644 --- a/Mage.Sets/src/mage/cards/i/InkTreaderNephilim.java +++ b/Mage.Sets/src/mage/cards/i/InkTreaderNephilim.java @@ -1,35 +1,38 @@ - package mage.cards.i; -import java.util.UUID; import mage.MageInt; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.CopySpellForEachItCouldTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.FilterInPlay; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; +import mage.filter.predicate.mageobject.MageObjectReferencePredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.game.stack.Spell; +import mage.game.stack.StackObject; import mage.players.Player; import mage.target.Target; import mage.util.TargetAddress; +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + /** * @author duncant */ public final class InkTreaderNephilim extends CardImpl { public InkTreaderNephilim(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{R}{G}{W}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{G}{W}{U}"); this.subtype.add(SubType.NEPHILIM); this.power = new MageInt(3); @@ -55,7 +58,7 @@ class InkTreaderNephilimTriggeredAbility extends TriggeredAbilityImpl { super(Zone.BATTLEFIELD, new InkTreaderNephilimEffect(), false); } - InkTreaderNephilimTriggeredAbility(final InkTreaderNephilimTriggeredAbility ability) { + private InkTreaderNephilimTriggeredAbility(final InkTreaderNephilimTriggeredAbility ability) { super(ability); } @@ -71,12 +74,9 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { - Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null - && (spell.isInstant() || spell.isSorcery())) { - for (Effect effect : getEffects()) { - effect.setValue("triggeringSpell", spell); - } + Spell spell = game.getSpell(event.getTargetId()); + if (spell != null && spell.isInstantOrSorcery()) { + getEffects().setValue("triggeringSpell", spell); return true; } return false; @@ -85,61 +85,72 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public boolean checkInterveningIfClause(Game game) { Spell spell = (Spell) getEffects().get(0).getValue("triggeringSpell"); - if (spell != null) { - boolean allTargetsInkTreaderNephilim = true; - boolean atLeastOneTargetsInkTreaderNephilim = false; - for (TargetAddress addr : TargetAddress.walk(spell)) { - Target targetInstance = addr.getTarget(spell); - for (UUID target : targetInstance.getTargets()) { - allTargetsInkTreaderNephilim &= target.equals(sourceId); - atLeastOneTargetsInkTreaderNephilim |= target.equals(sourceId); + if (spell == null) { + return false; + } + boolean flag = false; + for (TargetAddress addr : TargetAddress.walk(spell)) { + Target targetInstance = addr.getTarget(spell); + for (UUID target : targetInstance.getTargets()) { + if (target == null) { + continue; } - } - if (allTargetsInkTreaderNephilim && atLeastOneTargetsInkTreaderNephilim) { - return true; + Permanent permanent = game.getPermanent(target); + if (permanent == null || !permanent.getId().equals(getSourceId())) { + return false; + } + if (getSourceObjectZoneChangeCounter() != 0 + && getSourceObjectZoneChangeCounter() != permanent.getZoneChangeCounter(game)) { + return false; + } + flag = true; } } - return false; + return flag; } @Override public String getRule() { - return "Whenever a player casts an instant or sorcery spell, if that spell targets only {this}, copy the spell for each other creature that spell could target. Each copy targets a different one of those creatures."; + return "Whenever a player casts an instant or sorcery spell, " + + "if that spell targets only {this}, copy the spell for each other creature that spell could target. " + + "Each copy targets a different one of those creatures."; } } -class InkTreaderNephilimEffect extends CopySpellForEachItCouldTargetEffect { +class InkTreaderNephilimEffect extends CopySpellForEachItCouldTargetEffect { - public InkTreaderNephilimEffect() { - this(new FilterCreaturePermanent()); + InkTreaderNephilimEffect() { + super(); } - public InkTreaderNephilimEffect(InkTreaderNephilimEffect effect) { + private InkTreaderNephilimEffect(InkTreaderNephilimEffect effect) { super(effect); } - private InkTreaderNephilimEffect(FilterInPlay filter) { - super(filter); - } - @Override protected Player getPlayer(Game game, Ability source) { return game.getPlayer(source.getControllerId()); } @Override - protected Spell getSpell(Game game, Ability source) { - return (Spell) getValue("triggeringSpell"); - } - - @Override - protected boolean changeTarget(Target target, Game game, Ability source) { - return true; + protected List getPossibleTargets(StackObject stackObject, Player player, Ability source, Game game) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + return game.getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_PERMANENT_CREATURE, + player.getId(), source.getSourceId(), game + ).stream() + .filter(Objects::nonNull) + .filter(p -> !p.equals(permanent)) + .filter(p -> stackObject.canTarget(game, p.getId())) + .map(p -> new MageObjectReference(p, game)) + .map(MageObjectReferencePredicate::new) + .collect(Collectors.toList()); } @Override - protected void modifyCopy(Spell copy, Game game, Ability source) { - copy.setControllerId(source.getControllerId()); + protected Spell getStackObject(Game game, Ability source) { + return (Spell) getValue("triggeringSpell"); } @Override diff --git a/Mage.Sets/src/mage/cards/i/InklingSummoning.java b/Mage.Sets/src/mage/cards/i/InklingSummoning.java new file mode 100644 index 000000000000..274b3e86ce84 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InklingSummoning.java @@ -0,0 +1,34 @@ +package mage.cards.i; + +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.SilverquillToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class InklingSummoning extends CardImpl { + + public InklingSummoning(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{W/B}{W/B}"); + + this.subtype.add(SubType.LESSON); + + // Create a 2/1 white and black Inkling creature token with flying. + this.getSpellAbility().addEffect(new CreateTokenEffect(new SilverquillToken())); + } + + private InklingSummoning(final InklingSummoning card) { + super(card); + } + + @Override + public InklingSummoning copy() { + return new InklingSummoning(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/Inkshield.java b/Mage.Sets/src/mage/cards/i/Inkshield.java new file mode 100644 index 000000000000..c72853430820 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/Inkshield.java @@ -0,0 +1,74 @@ +package mage.cards.i; + +import mage.abilities.Ability; +import mage.abilities.effects.PreventionEffectData; +import mage.abilities.effects.PreventionEffectImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.token.SilverquillToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Inkshield extends CardImpl { + + public Inkshield(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{W}{B}"); + + // Prevent all combat damage that would be dealt to you this turn. For each 1 damage prevented this way, create a 2/1 white and black Inkling creature token with flying. + this.getSpellAbility().addEffect(new InkshieldEffect()); + } + + private Inkshield(final Inkshield card) { + super(card); + } + + @Override + public Inkshield copy() { + return new Inkshield(this); + } +} + +class InkshieldEffect extends PreventionEffectImpl { + + InkshieldEffect() { + super(Duration.EndOfTurn, Integer.MAX_VALUE, true, false); + staticText = "prevent all combat damage that would be dealt to you this turn. " + + "For each 1 damage prevented this way, create a 2/1 white and black Inkling creature token with flying"; + } + + private InkshieldEffect(final InkshieldEffect effect) { + super(effect); + } + + @Override + public InkshieldEffect copy() { + return new InkshieldEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGE_PLAYER; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return super.applies(event, source, game) && source.isControlledBy(event.getTargetId()); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + PreventionEffectData preventionEffectData = preventDamageAction(event, source, game); + if (preventionEffectData.getPreventedDamage() > 0) { + new CreateTokenEffect(new SilverquillToken(), preventionEffectData.getPreventedDamage()).apply(game, source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/i/InquisitionOfKozilek.java b/Mage.Sets/src/mage/cards/i/InquisitionOfKozilek.java index ed0742d905dc..71fffde96ab7 100644 --- a/Mage.Sets/src/mage/cards/i/InquisitionOfKozilek.java +++ b/Mage.Sets/src/mage/cards/i/InquisitionOfKozilek.java @@ -11,7 +11,7 @@ import mage.constants.TargetController; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetPlayer; /** @@ -20,11 +20,11 @@ */ public final class InquisitionOfKozilek extends CardImpl { - private static final FilterCard filter = new FilterCard("nonland card with converted mana cost 3 or less"); + private static final FilterCard filter = new FilterCard("nonland card with mana value 3 or less"); static { filter.add(Predicates.not(CardType.LAND.getPredicate())); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public InquisitionOfKozilek(UUID ownerId, CardSetInfo setInfo){ diff --git a/Mage.Sets/src/mage/cards/i/InquisitorsFlail.java b/Mage.Sets/src/mage/cards/i/InquisitorsFlail.java index da0965d1515f..3bc2200e3e09 100644 --- a/Mage.Sets/src/mage/cards/i/InquisitorsFlail.java +++ b/Mage.Sets/src/mage/cards/i/InquisitorsFlail.java @@ -1,7 +1,6 @@ package mage.cards.i; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.GenericManaCost; @@ -9,25 +8,22 @@ import mage.abilities.keyword.EquipAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; -import mage.game.events.DamageCreatureEvent; import mage.game.events.DamageEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.util.CardUtil; +import java.util.UUID; + /** * @author nantuko */ public final class InquisitorsFlail extends CardImpl { public InquisitorsFlail(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); this.subtype.add(SubType.EQUIPMENT); // If equipped creature would deal combat damage, it deals double that damage instead. @@ -67,20 +63,13 @@ public InquisitorsFlailEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE || - event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER || + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT || event.getType() == GameEvent.EventType.DAMAGE_PLAYER; } @Override public boolean applies(GameEvent event, Ability source, Game game) { - boolean isCombat = false; - if (event instanceof DamageCreatureEvent) { - isCombat = ((DamageCreatureEvent) event).isCombatDamage(); - } else if (event instanceof DamageEvent) { - isCombat = ((DamageEvent) event).isCombatDamage(); - } - if (isCombat) { + if (((DamageEvent) event).isCombatDamage()) { Permanent equipment = game.getPermanent(source.getSourceId()); if (equipment != null && equipment.getAttachedTo() != null) { UUID attachedTo = equipment.getAttachedTo(); diff --git a/Mage.Sets/src/mage/cards/i/InscriptionOfRuin.java b/Mage.Sets/src/mage/cards/i/InscriptionOfRuin.java index b7760fb148af..c91c0060892e 100644 --- a/Mage.Sets/src/mage/cards/i/InscriptionOfRuin.java +++ b/Mage.Sets/src/mage/cards/i/InscriptionOfRuin.java @@ -13,7 +13,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterCreatureCard; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetPermanent; import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetOpponent; @@ -26,13 +26,13 @@ public final class InscriptionOfRuin extends CardImpl { private static final FilterCard filter - = new FilterCreatureCard("creature card with converted mana cost 2 or less from your graveyard"); + = new FilterCreatureCard("creature card with mana value 2 or less from your graveyard"); private static final FilterPermanent filter2 - = new FilterCreaturePermanent("creature with converted mana cost 3 or less"); + = new FilterCreaturePermanent("creature with mana value 3 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); - filter2.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); + filter2.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public InscriptionOfRuin(UUID ownerId, CardSetInfo setInfo) { @@ -48,7 +48,7 @@ public InscriptionOfRuin(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addTarget(new TargetOpponent()); // • Return target creature card with converted mana cost 2 or less from your graveyard to the battlefield. - Mode mode = new Mode(new ReturnFromGraveyardToBattlefieldTargetEffect(false, false)); + Mode mode = new Mode(new ReturnFromGraveyardToBattlefieldTargetEffect()); mode.addTarget(new TargetCardInYourGraveyard(filter)); this.getSpellAbility().addMode(mode); diff --git a/Mage.Sets/src/mage/cards/i/InsidiousDreams.java b/Mage.Sets/src/mage/cards/i/InsidiousDreams.java index abb259944118..d1e15ea53b23 100644 --- a/Mage.Sets/src/mage/cards/i/InsidiousDreams.java +++ b/Mage.Sets/src/mage/cards/i/InsidiousDreams.java @@ -57,7 +57,7 @@ class InsidiousDreamsEffect extends OneShotEffect { public InsidiousDreamsEffect() { super(Outcome.Benefit); - this.staticText = "Search your library for up to X cards. Then shuffle your library and put those cards on top of it in any order"; + this.staticText = "Search your library for up to X cards. Then shuffle and put those cards on top of it in any order"; } public InsidiousDreamsEffect(final InsidiousDreamsEffect effect) { diff --git a/Mage.Sets/src/mage/cards/i/InspireAwe.java b/Mage.Sets/src/mage/cards/i/InspireAwe.java index 95b55830e4e6..483b60114aa9 100644 --- a/Mage.Sets/src/mage/cards/i/InspireAwe.java +++ b/Mage.Sets/src/mage/cards/i/InspireAwe.java @@ -8,7 +8,7 @@ import mage.constants.Duration; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.EnchantmentOrEnchantedPredicate; +import mage.filter.predicate.permanent.EnchantmentOrEnchantedPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/i/InspiringCommander.java b/Mage.Sets/src/mage/cards/i/InspiringCommander.java index a77e24fb9a25..ffe9d051bc29 100644 --- a/Mage.Sets/src/mage/cards/i/InspiringCommander.java +++ b/Mage.Sets/src/mage/cards/i/InspiringCommander.java @@ -15,7 +15,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/i/InspiringRefrain.java b/Mage.Sets/src/mage/cards/i/InspiringRefrain.java new file mode 100644 index 000000000000..c93c47847711 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InspiringRefrain.java @@ -0,0 +1,43 @@ +package mage.cards.i; + +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.ExileSpellEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.SuspendAbility; +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 InspiringRefrain extends CardImpl { + + public InspiringRefrain(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{U}{U}"); + + // Draw two cards. Exile Inspiring Refrain with three time counters on it. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2)); + this.getSpellAbility().addEffect(new ExileSpellEffect()); + this.getSpellAbility().addEffect(new AddCountersSourceEffect( + CounterType.TIME.createInstance(), StaticValue.get(3), false, true + ).setText("with three time counters on it")); + + // Suspend 3—{2}{U} + this.addAbility(new SuspendAbility(3, new ManaCostsImpl<>("{2}{U}"), this)); + } + + private InspiringRefrain(final InspiringRefrain card) { + super(card); + } + + @Override + public InspiringRefrain copy() { + return new InspiringRefrain(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/InstigatorGang.java b/Mage.Sets/src/mage/cards/i/InstigatorGang.java index f334bf868f1e..adbbb4ac4823 100644 --- a/Mage.Sets/src/mage/cards/i/InstigatorGang.java +++ b/Mage.Sets/src/mage/cards/i/InstigatorGang.java @@ -1,23 +1,18 @@ - package mage.cards.i; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.condition.common.TransformedCondition; -import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.common.FilterAttackingCreature; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; /** * @author nantuko @@ -36,15 +31,14 @@ public InstigatorGang(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(3); // Attacking creatures you control get +1/+0. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new ConditionalContinuousEffect(new BoostControlledEffect(1, 0, Duration.WhileOnBattlefield, new FilterAttackingCreature()), - new TransformedCondition(true), "Attacking creatures you control get +1/+0"))); - + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 1, 0, Duration.WhileOnBattlefield, + StaticFilters.FILTER_ATTACKING_CREATURES + ))); // At the beginning of each upkeep, if no spells were cast last turn, transform Instigator Gang. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private InstigatorGang(final InstigatorGang card) { diff --git a/Mage.Sets/src/mage/cards/i/InsultInjury.java b/Mage.Sets/src/mage/cards/i/InsultInjury.java index 4fca4d7fb57d..ea490acee1c4 100644 --- a/Mage.Sets/src/mage/cards/i/InsultInjury.java +++ b/Mage.Sets/src/mage/cards/i/InsultInjury.java @@ -72,9 +72,8 @@ public InsultDoubleDamageEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE - || event.getType() == GameEvent.EventType.DAMAGE_PLAYER - || event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER; + return event.getType() == GameEvent.EventType.DAMAGE_PLAYER + || event.getType() == GameEvent.EventType.DAMAGE_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/i/InterpretTheSigns.java b/Mage.Sets/src/mage/cards/i/InterpretTheSigns.java index 14bfa9b62a95..25a0b4b32726 100644 --- a/Mage.Sets/src/mage/cards/i/InterpretTheSigns.java +++ b/Mage.Sets/src/mage/cards/i/InterpretTheSigns.java @@ -39,7 +39,7 @@ class InterpretTheSignsEffect extends OneShotEffect { public InterpretTheSignsEffect() { super(Outcome.DrawCard); - this.staticText = "scry 3, then reveal the top card of your library. Draw cards equal to that card's converted mana cost"; + this.staticText = "scry 3, then reveal the top card of your library. Draw cards equal to that card's mana value"; } public InterpretTheSignsEffect(final InterpretTheSignsEffect effect) { @@ -63,7 +63,7 @@ public boolean apply(Game game, Ability source) { return true; } controller.revealCards(source, new CardsImpl(card), game); - controller.drawCards(card.getConvertedManaCost(), source, game); + controller.drawCards(card.getManaValue(), source, game); return true; } } diff --git a/Mage.Sets/src/mage/cards/i/Intervene.java b/Mage.Sets/src/mage/cards/i/Intervene.java index 61a8acf7c9db..a8b1667c2705 100644 --- a/Mage.Sets/src/mage/cards/i/Intervene.java +++ b/Mage.Sets/src/mage/cards/i/Intervene.java @@ -8,7 +8,7 @@ import mage.constants.CardType; import mage.filter.FilterSpell; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.other.TargetsPermanentPredicate; +import mage.filter.predicate.mageobject.TargetsPermanentPredicate; import mage.target.TargetSpell; /** diff --git a/Mage.Sets/src/mage/cards/i/IntrepidProvisioner.java b/Mage.Sets/src/mage/cards/i/IntrepidProvisioner.java index 2e3212300256..844e2edccc18 100644 --- a/Mage.Sets/src/mage/cards/i/IntrepidProvisioner.java +++ b/Mage.Sets/src/mage/cards/i/IntrepidProvisioner.java @@ -13,7 +13,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/i/IntroductionToAnnihilation.java b/Mage.Sets/src/mage/cards/i/IntroductionToAnnihilation.java new file mode 100644 index 000000000000..eb78b60cf538 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IntroductionToAnnihilation.java @@ -0,0 +1,73 @@ +package mage.cards.i; + +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.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetNonlandPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class IntroductionToAnnihilation extends CardImpl { + + public IntroductionToAnnihilation(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}"); + + this.subtype.add(SubType.LESSON); + + // Exile target nonland permanent. Its controller draws a card. + this.getSpellAbility().addEffect(new IntroductionToAnnihilationEffect()); + this.getSpellAbility().addTarget(new TargetNonlandPermanent()); + } + + private IntroductionToAnnihilation(final IntroductionToAnnihilation card) { + super(card); + } + + @Override + public IntroductionToAnnihilation copy() { + return new IntroductionToAnnihilation(this); + } +} + +class IntroductionToAnnihilationEffect extends OneShotEffect { + + IntroductionToAnnihilationEffect() { + super(Outcome.Benefit); + staticText = "exile target nonland permanent. Its controller draws a card"; + } + + private IntroductionToAnnihilationEffect(final IntroductionToAnnihilationEffect effect) { + super(effect); + } + + @Override + public IntroductionToAnnihilationEffect copy() { + return new IntroductionToAnnihilationEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (controller == null || permanent == null) { + return false; + } + controller.moveCards(permanent, Zone.EXILED, source, game); + Player player = game.getPlayer(permanent.getControllerId()); + if (player != null) { + player.drawCards(1, source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/i/IntroductionToProphecy.java b/Mage.Sets/src/mage/cards/i/IntroductionToProphecy.java new file mode 100644 index 000000000000..a8aa170883bf --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IntroductionToProphecy.java @@ -0,0 +1,35 @@ +package mage.cards.i; + +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.keyword.ScryEffect; +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 IntroductionToProphecy extends CardImpl { + + public IntroductionToProphecy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}"); + + this.subtype.add(SubType.LESSON); + + // Scry 2, then draw a card. + this.getSpellAbility().addEffect(new ScryEffect(2, false)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy(", then")); + } + + private IntroductionToProphecy(final IntroductionToProphecy card) { + super(card); + } + + @Override + public IntroductionToProphecy copy() { + return new IntroductionToProphecy(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/Intuition.java b/Mage.Sets/src/mage/cards/i/Intuition.java index 5589fa11ce3d..9099e98745b6 100644 --- a/Mage.Sets/src/mage/cards/i/Intuition.java +++ b/Mage.Sets/src/mage/cards/i/Intuition.java @@ -50,7 +50,7 @@ class IntuitionEffect extends SearchEffect { public IntuitionEffect() { super(new TargetCardInLibrary(3, new FilterCard()), Outcome.Benefit); - staticText = "Search your library for three cards and reveal them. Target opponent chooses one. Put that card into your hand and the rest into your graveyard. Then shuffle your library"; + staticText = "Search your library for three cards and reveal them. Target opponent chooses one. Put that card into your hand and the rest into your graveyard. Then shuffle"; } diff --git a/Mage.Sets/src/mage/cards/i/InvasionOfTheGiants.java b/Mage.Sets/src/mage/cards/i/InvasionOfTheGiants.java index cd67d815d49d..dbbe8f22a4ad 100644 --- a/Mage.Sets/src/mage/cards/i/InvasionOfTheGiants.java +++ b/Mage.Sets/src/mage/cards/i/InvasionOfTheGiants.java @@ -16,11 +16,15 @@ import mage.constants.*; import mage.filter.FilterCard; import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; import mage.target.common.TargetCardInHand; import mage.target.common.TargetOpponentOrPlaneswalker; import mage.util.CardUtil; -import mage.watchers.common.CastSpellLastTurnWatcher; +import mage.watchers.Watcher; +import java.util.HashMap; +import java.util.Map; import java.util.UUID; /** @@ -64,7 +68,7 @@ public InvasionOfTheGiants(UUID ownerId, CardSetInfo setInfo) { // III — The next Giant spell you cast this turns costs {2} less to cast. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new InvasionOfTheGiantsEffect()); - this.addAbility(sagaAbility); + this.addAbility(sagaAbility, new InvasionOfTheGiantsWatcher()); } private InvasionOfTheGiants(final InvasionOfTheGiants card) { @@ -94,9 +98,9 @@ private InvasionOfTheGiantsEffect(final InvasionOfTheGiantsEffect effect) { @Override public void init(Ability source, Game game) { super.init(source, game); - CastSpellLastTurnWatcher watcher = game.getState().getWatcher(CastSpellLastTurnWatcher.class); + InvasionOfTheGiantsWatcher watcher = game.getState().getWatcher(InvasionOfTheGiantsWatcher.class); if (watcher != null) { - spellsCast = watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(source.getControllerId()); + spellsCast = watcher.getCount(source.getControllerId()); } } @@ -108,11 +112,11 @@ public boolean apply(Game game, Ability source, Ability abilityToModify) { @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { - CastSpellLastTurnWatcher watcher = game.getState().getWatcher(CastSpellLastTurnWatcher.class); + InvasionOfTheGiantsWatcher watcher = game.getState().getWatcher(InvasionOfTheGiantsWatcher.class); if (watcher == null) { return false; } - if (watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(source.getControllerId()) > spellsCast) { + if (watcher.getCount(source.getControllerId()) > spellsCast) { discard(); // only one use return false; } @@ -129,3 +133,33 @@ public InvasionOfTheGiantsEffect copy() { return new InvasionOfTheGiantsEffect(this); } } + +class InvasionOfTheGiantsWatcher extends Watcher { + + private final Map playerMap = new HashMap<>(); + + InvasionOfTheGiantsWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.SPELL_CAST) { + return; + } + Spell spell = game.getSpell(event.getSourceId()); + if (spell != null && spell.hasSubtype(SubType.GIANT, game)) { + playerMap.compute(event.getPlayerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + } + } + + @Override + public void reset() { + super.reset(); + playerMap.clear(); + } + + int getCount(UUID playerId) { + return playerMap.getOrDefault(playerId, 0); + } +} diff --git a/Mage.Sets/src/mage/cards/i/InvasionPlans.java b/Mage.Sets/src/mage/cards/i/InvasionPlans.java index b01c0b69d669..d08a9c659ebe 100644 --- a/Mage.Sets/src/mage/cards/i/InvasionPlans.java +++ b/Mage.Sets/src/mage/cards/i/InvasionPlans.java @@ -70,7 +70,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { Player blockController = game.getPlayer(game.getCombat().getAttackingPlayerId()); if (blockController != null) { // temporary workaround for AI bugging out while choosing blockers - if (blockController.isHuman()) { + if (!blockController.isComputer()) { game.getCombat().selectBlockers(blockController, source, game); return event.getPlayerId().equals(game.getCombat().getAttackingPlayerId()); } diff --git a/Mage.Sets/src/mage/cards/i/InvasiveSpecies.java b/Mage.Sets/src/mage/cards/i/InvasiveSpecies.java index 0a8d8f0e72c7..8964bb4be258 100644 --- a/Mage.Sets/src/mage/cards/i/InvasiveSpecies.java +++ b/Mage.Sets/src/mage/cards/i/InvasiveSpecies.java @@ -10,7 +10,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/i/InvasiveSurgery.java b/Mage.Sets/src/mage/cards/i/InvasiveSurgery.java index ee53835e906b..66fbd8c4e8d7 100644 --- a/Mage.Sets/src/mage/cards/i/InvasiveSurgery.java +++ b/Mage.Sets/src/mage/cards/i/InvasiveSurgery.java @@ -97,6 +97,6 @@ public String getText(Mode mode) { return "Counter target sorcery spell.

" + "Delirium — If there are four or more card types among cards in your graveyard, " + "search the graveyard, hand, and library of that spell's controller for any number of cards " - + "with the same name as that spell, exile those cards, then that player shuffles their library"; + + "with the same name as that spell, exile those cards, then that player shuffles"; } } diff --git a/Mage.Sets/src/mage/cards/i/InventorsFair.java b/Mage.Sets/src/mage/cards/i/InventorsFair.java index 550052600d5c..66df24f53811 100644 --- a/Mage.Sets/src/mage/cards/i/InventorsFair.java +++ b/Mage.Sets/src/mage/cards/i/InventorsFair.java @@ -13,15 +13,13 @@ import mage.abilities.mana.ColorlessManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SuperType; import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.filter.common.FilterArtifactPermanent; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.target.common.TargetCardInLibrary; import java.util.UUID; @@ -31,12 +29,6 @@ */ public final class InventorsFair extends CardImpl { - private static final FilterCard filter = new FilterCard("artifact"); - - static { - filter.add(CardType.ARTIFACT.getPredicate()); - } - public InventorsFair(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); addSuperType(SuperType.LEGENDARY); @@ -49,11 +41,10 @@ public InventorsFair(UUID ownerId, CardSetInfo setInfo) { // {4}, {T}, Sacrifice Inventors' Fair: Search your library for an artifact card, reveal it, put it into your hand, then shuffle your library. // Activate this ability only if you control threeor more artifacts. - Ability ability = new ActivateIfConditionActivatedAbility(Zone.BATTLEFIELD, new SearchLibraryPutInHandEffect(new TargetCardInLibrary(1, filter), true), + Ability ability = new ActivateIfConditionActivatedAbility(Zone.BATTLEFIELD, new SearchLibraryPutInHandEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_ARTIFACT), true), new GenericManaCost(4), MetalcraftCondition.instance); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); - ability.setAbilityWord(AbilityWord.METALCRAFT); ability.addHint(MetalcraftHint.instance); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/i/InverterOfTruth.java b/Mage.Sets/src/mage/cards/i/InverterOfTruth.java index c967c8c01e84..e44d90fc4269 100644 --- a/Mage.Sets/src/mage/cards/i/InverterOfTruth.java +++ b/Mage.Sets/src/mage/cards/i/InverterOfTruth.java @@ -1,31 +1,32 @@ - package mage.cards.i; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.DevoidAbility; import mage.abilities.keyword.FlyingAbility; -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.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.players.Player; +import java.util.Objects; +import java.util.UUID; + /** - * * @author fireshoes */ public final class InverterOfTruth extends CardImpl { public InverterOfTruth(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}"); this.subtype.add(SubType.ELDRAZI); this.power = new MageInt(6); this.toughness = new MageInt(6); @@ -37,7 +38,7 @@ public InverterOfTruth(UUID ownerId, CardSetInfo setInfo) { this.addAbility(FlyingAbility.getInstance()); // When Inverter of Truth enters the battlefield, exile all cards from your library face down, then shuffle all cards from your graveyard into your library. - this.addAbility(new EntersBattlefieldTriggeredAbility(new ExileLibraryEffect(), false)); + this.addAbility(new EntersBattlefieldTriggeredAbility(new InverterOfTruthEffect(), false)); } private InverterOfTruth(final InverterOfTruth card) { @@ -50,35 +51,37 @@ public InverterOfTruth copy() { } } -class ExileLibraryEffect extends OneShotEffect { +class InverterOfTruthEffect extends OneShotEffect { + + InverterOfTruthEffect() { + super(Outcome.Benefit); + staticText = "exile all cards from your library face down, " + + "then shuffle all cards from your graveyard into your library"; + } - public ExileLibraryEffect() { - super(Outcome.Exile); - staticText = "exile all cards from your library face down, then shuffle all cards from your graveyard into your library"; + private InverterOfTruthEffect(final InverterOfTruthEffect effect) { + super(effect); } @Override - public ExileLibraryEffect copy() { - return new ExileLibraryEffect(); + public InverterOfTruthEffect copy() { + return new InverterOfTruthEffect(this); } @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - int count = controller.getLibrary().size(); - if (count > 0) { - for (Card cardToExile: controller.getLibrary().getCards(game)) { - cardToExile.moveToExile(null, "", source, game); - cardToExile.setFaceDown(true, game); - } - } - for (Card cardToLibrary: controller.getGraveyard().getCards(game)) { - controller.moveCardToLibraryWithInfo(cardToLibrary, source, game, Zone.GRAVEYARD, true, true); - } - controller.shuffleLibrary(source, game); - return true; + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; } - return false; + Cards cards = new CardsImpl(player.getLibrary().getCards(game)); + player.moveCards(cards, Zone.EXILED, source, game); + cards.removeIf(uuid -> game.getState().getZone(uuid) != Zone.EXILED); + cards.getCards(game) + .stream() + .filter(Objects::nonNull) + .forEach(card -> card.setFaceDown(true, game)); + player.shuffleCardsToLibrary(player.getGraveyard(), game, source); + return true; } } diff --git a/Mage.Sets/src/mage/cards/i/InvokePrejudice.java b/Mage.Sets/src/mage/cards/i/InvokePrejudice.java index 21eb76d163a6..e3cea30c48e3 100644 --- a/Mage.Sets/src/mage/cards/i/InvokePrejudice.java +++ b/Mage.Sets/src/mage/cards/i/InvokePrejudice.java @@ -15,7 +15,6 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.target.targetpointer.FixedTarget; @@ -97,7 +96,7 @@ class InvokePrejudiceEffect extends CounterUnlessPaysEffect { public InvokePrejudiceEffect() { super(new GenericManaCost(1)); - this.staticText = "counter that spell unless that player pays {X}, where X is its converted mana cost"; + this.staticText = "counter that spell unless that player pays {X}, where X is its mana value"; } public InvokePrejudiceEffect(final InvokePrejudiceEffect effect) { @@ -114,7 +113,7 @@ public boolean apply(Game game, Ability source) { boolean result = true; Spell spell = game.getStack().getSpell(getTargetPointer().getFirst(game, source)); if (spell != null) { - CounterUnlessPaysEffect effect = new CounterUnlessPaysEffect(new GenericManaCost(spell.getConvertedManaCost())); + CounterUnlessPaysEffect effect = new CounterUnlessPaysEffect(new GenericManaCost(spell.getManaValue())); effect.setTargetPointer(getTargetPointer()); result = effect.apply(game, source); } diff --git a/Mage.Sets/src/mage/cards/i/IridescentDrake.java b/Mage.Sets/src/mage/cards/i/IridescentDrake.java index c4008d5260e3..bedaa8438746 100644 --- a/Mage.Sets/src/mage/cards/i/IridescentDrake.java +++ b/Mage.Sets/src/mage/cards/i/IridescentDrake.java @@ -15,7 +15,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.other.AuraCardCanAttachToPermanentId; +import mage.filter.predicate.card.AuraCardCanAttachToPermanentId; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/i/Ironfang.java b/Mage.Sets/src/mage/cards/i/Ironfang.java index ebd59f773376..2e287c13515f 100644 --- a/Mage.Sets/src/mage/cards/i/Ironfang.java +++ b/Mage.Sets/src/mage/cards/i/Ironfang.java @@ -1,20 +1,14 @@ - package mage.cards.i; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.keyword.FirstStrikeAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** * @author nantuko @@ -22,7 +16,7 @@ public final class Ironfang extends CardImpl { public Ironfang(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.WEREWOLF); this.color.setRed(true); @@ -36,8 +30,7 @@ public Ironfang(UUID ownerId, CardSetInfo setInfo) { this.addAbility(FirstStrikeAbility.getInstance()); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Ironfang. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private Ironfang(final Ironfang card) { diff --git a/Mage.Sets/src/mage/cards/i/IsarethTheAwakener.java b/Mage.Sets/src/mage/cards/i/IsarethTheAwakener.java index d6b45bece8f5..86b4934d34d0 100644 --- a/Mage.Sets/src/mage/cards/i/IsarethTheAwakener.java +++ b/Mage.Sets/src/mage/cards/i/IsarethTheAwakener.java @@ -18,7 +18,7 @@ import mage.counters.Counters; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; @@ -61,7 +61,7 @@ public IsarethTheAwakener copy() { class IsarethTheAwakenerCreateReflexiveTriggerEffect extends OneShotEffect { private static final String rule = "return target creature card " - + "with converted mana cost X from your graveyard to the battlefield " + + "with mana value X from your graveyard to the battlefield " + "with a corpse counter on it. If that creature would leave the battlefield, " + "exile it instead of putting it anywhere else."; @@ -103,10 +103,10 @@ public boolean apply(Game game, Ability source) { private static FilterCard makeFilter(int xValue) { FilterCard filter = new FilterCreatureCard( - "creature card with converted mana cost " + + "creature card with mana value " + xValue + " or less from your graveyard" ); - filter.add(new ConvertedManaCostPredicate( + filter.add(new ManaValuePredicate( ComparisonType.EQUAL_TO, xValue )); return filter; diff --git a/Mage.Sets/src/mage/cards/i/IsochronScepter.java b/Mage.Sets/src/mage/cards/i/IsochronScepter.java index e27d76eb23a9..b6e6ede572eb 100644 --- a/Mage.Sets/src/mage/cards/i/IsochronScepter.java +++ b/Mage.Sets/src/mage/cards/i/IsochronScepter.java @@ -16,7 +16,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -60,17 +60,16 @@ public IsochronScepter copy() { class IsochronScepterImprintEffect extends OneShotEffect { private static final FilterCard filter = new FilterCard("instant card with " - + "converted mana cost 2 or less from your hand"); + + "mana value 2 or less from your hand"); static { filter.add(CardType.INSTANT.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); } public IsochronScepterImprintEffect() { super(Outcome.Benefit); - staticText = "you may exile an instant card with converted mana " - + "cost 2 or less from your hand"; + staticText = "you may exile an instant card with mana value 2 or less from your hand"; } public IsochronScepterImprintEffect(IsochronScepterImprintEffect effect) { diff --git a/Mage.Sets/src/mage/cards/i/Isolate.java b/Mage.Sets/src/mage/cards/i/Isolate.java index 3fe07ae781ac..d8fe93ca5b8e 100644 --- a/Mage.Sets/src/mage/cards/i/Isolate.java +++ b/Mage.Sets/src/mage/cards/i/Isolate.java @@ -7,7 +7,7 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetPermanent; /** @@ -16,10 +16,10 @@ */ public final class Isolate extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("permanent with converted mana cost 1"); + private static final FilterPermanent filter = new FilterPermanent("permanent with mana value 1"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, 1)); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, 1)); } public Isolate(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/i/IsperiaTheInscrutable.java b/Mage.Sets/src/mage/cards/i/IsperiaTheInscrutable.java index 9eefe30f0271..b0c31a83f183 100644 --- a/Mage.Sets/src/mage/cards/i/IsperiaTheInscrutable.java +++ b/Mage.Sets/src/mage/cards/i/IsperiaTheInscrutable.java @@ -68,7 +68,7 @@ class IsperiaTheInscrutableEffect extends OneShotEffect { public IsperiaTheInscrutableEffect() { super(Outcome.Neutral); - staticText = "That player reveals their hand. If a card with the chosen name is revealed this way, search your library for a creature card with flying, reveal it, put it into your hand, then shuffle your library"; + staticText = "That player reveals their hand. If a card with the chosen name is revealed this way, search your library for a creature card with flying, reveal it, put it into your hand, then shuffle"; } public IsperiaTheInscrutableEffect(final IsperiaTheInscrutableEffect effect) { diff --git a/Mage.Sets/src/mage/cards/i/IvyLaneDenizen.java b/Mage.Sets/src/mage/cards/i/IvyLaneDenizen.java index 8ff34902bc60..4257e4b85fbd 100644 --- a/Mage.Sets/src/mage/cards/i/IvyLaneDenizen.java +++ b/Mage.Sets/src/mage/cards/i/IvyLaneDenizen.java @@ -15,7 +15,7 @@ import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/i/IxidorRealitySculptor.java b/Mage.Sets/src/mage/cards/i/IxidorRealitySculptor.java index 90933bf727f3..e86ca1c76322 100644 --- a/Mage.Sets/src/mage/cards/i/IxidorRealitySculptor.java +++ b/Mage.Sets/src/mage/cards/i/IxidorRealitySculptor.java @@ -13,7 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.other.FaceDownPredicate; +import mage.filter.predicate.card.FaceDownPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/i/Ixidron.java b/Mage.Sets/src/mage/cards/i/Ixidron.java index d6d5ed529db9..ab4af5899875 100644 --- a/Mage.Sets/src/mage/cards/i/Ixidron.java +++ b/Mage.Sets/src/mage/cards/i/Ixidron.java @@ -17,8 +17,8 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.other.FaceDownPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.card.FaceDownPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; /** diff --git a/Mage.Sets/src/mage/cards/i/IzzetGuildmage.java b/Mage.Sets/src/mage/cards/i/IzzetGuildmage.java index 1a88f7888663..93c201ea68e5 100644 --- a/Mage.Sets/src/mage/cards/i/IzzetGuildmage.java +++ b/Mage.Sets/src/mage/cards/i/IzzetGuildmage.java @@ -15,7 +15,7 @@ import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetSpell; /** @@ -24,15 +24,15 @@ */ public final class IzzetGuildmage extends CardImpl { - private static final FilterSpell filterInstant = new FilterSpell("instant spell you control with converted mana cost 2 or less"); - private static final FilterSpell filterSorcery = new FilterSpell("sorcery spell you control with converted mana cost 2 or less"); + private static final FilterSpell filterInstant = new FilterSpell("instant spell you control with mana value 2 or less"); + private static final FilterSpell filterSorcery = new FilterSpell("sorcery spell you control with mana value 2 or less"); static { filterInstant.add(CardType.INSTANT.getPredicate()); - filterInstant.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); + filterInstant.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); filterInstant.add(TargetController.YOU.getControllerPredicate()); filterSorcery.add(CardType.SORCERY.getPredicate()); - filterSorcery.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); + filterSorcery.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); filterSorcery.add(TargetController.YOU.getControllerPredicate()); } diff --git a/Mage.Sets/src/mage/cards/i/IzzetKeyrune.java b/Mage.Sets/src/mage/cards/i/IzzetKeyrune.java index 1cda0ffffa39..b9776d3fb38c 100644 --- a/Mage.Sets/src/mage/cards/i/IzzetKeyrune.java +++ b/Mage.Sets/src/mage/cards/i/IzzetKeyrune.java @@ -1,26 +1,21 @@ - package mage.cards.i; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawDiscardControllerEffect; import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; import mage.abilities.mana.BlueManaAbility; import mage.abilities.mana.RedManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; +import mage.constants.SubType; import mage.game.permanent.token.TokenImpl; -import mage.players.Player; + +import java.util.UUID; /** * @author LevelX2 @@ -28,17 +23,23 @@ public final class IzzetKeyrune extends CardImpl { public IzzetKeyrune(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); // {T}: Add {U} or {R}. this.addAbility(new BlueManaAbility()); this.addAbility(new RedManaAbility()); // {U}{R}: Until end of turn, Izzet Keyrune becomes a 2/1 blue and red Elemental artifact creature. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new IzzetKeyruneToken(), "", Duration.EndOfTurn), new ManaCostsImpl("{U}{R}"))); + this.addAbility(new SimpleActivatedAbility(new BecomesCreatureSourceEffect( + new IzzetKeyruneToken(), "", Duration.EndOfTurn + ), new ManaCostsImpl("{U}{R}"))); // Whenever Izzet Keyrune deals combat damage to a player, you may draw a card. If you do, discard a card. - this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new IzzetKeyruneEffect(), true)); + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( + new DrawDiscardControllerEffect(1, 1, true) + .setText("you may draw a card. If you do, discard a card"), + false + )); } private IzzetKeyrune(final IzzetKeyrune card) { @@ -50,37 +51,8 @@ public IzzetKeyrune copy() { return new IzzetKeyrune(this); } - private static class IzzetKeyruneEffect extends OneShotEffect { - - public IzzetKeyruneEffect() { - super(Outcome.DrawCard); - this.staticText = "you may draw a card. If you do, discard a card"; - } - - public IzzetKeyruneEffect(final IzzetKeyruneEffect effect) { - super(effect); - } - - @Override - public IzzetKeyruneEffect copy() { - return new IzzetKeyruneEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null && player.chooseUse(Outcome.DrawCard, "Do you wish to draw a card? If you do, discard a card.", source, game)) { - if (player.drawCards(1, source, game) > 0) { - player.discard(1, false, false, source, game); - } - return true; - } - return false; - } - } - private static class IzzetKeyruneToken extends TokenImpl { - IzzetKeyruneToken() { + private IzzetKeyruneToken() { super("", "2/1 blue and red Elemental artifact creature"); cardType.add(CardType.ARTIFACT); cardType.add(CardType.CREATURE); @@ -91,7 +63,7 @@ private static class IzzetKeyruneToken extends TokenImpl { toughness = new MageInt(1); } - public IzzetKeyruneToken(final IzzetKeyruneToken token) { + private IzzetKeyruneToken(final IzzetKeyruneToken token) { super(token); } @@ -100,4 +72,3 @@ public IzzetKeyruneToken copy() { } } } - diff --git a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java index 18ea7e85a362..e11673056860 100644 --- a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java +++ b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java @@ -215,7 +215,7 @@ class JaceArchitectOfThoughtEffect3 extends OneShotEffect { public JaceArchitectOfThoughtEffect3() { super(Outcome.PlayForFree); this.staticText = "For each player, search that player's library for a nonland card and exile it, " - + "then that player shuffles their library. You may cast those cards without paying their mana costs"; + + "then that player shuffles. You may cast those cards without paying their mana costs"; } public JaceArchitectOfThoughtEffect3(final JaceArchitectOfThoughtEffect3 effect) { diff --git a/Mage.Sets/src/mage/cards/j/JaceMirrorMage.java b/Mage.Sets/src/mage/cards/j/JaceMirrorMage.java index 7d2bacda1d31..faabc94065e4 100644 --- a/Mage.Sets/src/mage/cards/j/JaceMirrorMage.java +++ b/Mage.Sets/src/mage/cards/j/JaceMirrorMage.java @@ -98,7 +98,7 @@ class JaceMirrorMageDrawEffect extends OneShotEffect { JaceMirrorMageDrawEffect() { super(Outcome.Benefit); - staticText = "draw a card and reveal it. Remove a number of loyalty counters equal to that card's converted mana cost from {this}"; + staticText = "draw a card and reveal it. Remove a number of loyalty counters equal to that card's mana value from {this}"; } private JaceMirrorMageDrawEffect(final JaceMirrorMageDrawEffect effect) { @@ -124,14 +124,14 @@ public boolean apply(Game game, Ability source) { return true; } controller.revealCards(source, new CardsImpl(card), game); - if (card == null || card.getConvertedManaCost() == 0) { + if (card == null || card.getManaValue() == 0) { return true; } Permanent permanent = source.getSourcePermanentIfItStillExists(game); if (permanent == null) { return true; } - permanent.removeCounters(CounterType.LOYALTY.createInstance(card.getConvertedManaCost()), source, game); + permanent.removeCounters(CounterType.LOYALTY.createInstance(card.getManaValue()), source, game); return true; } } diff --git a/Mage.Sets/src/mage/cards/j/JaceTheLivingGuildpact.java b/Mage.Sets/src/mage/cards/j/JaceTheLivingGuildpact.java index 0d89760d55d8..c905f3cacc5e 100644 --- a/Mage.Sets/src/mage/cards/j/JaceTheLivingGuildpact.java +++ b/Mage.Sets/src/mage/cards/j/JaceTheLivingGuildpact.java @@ -18,7 +18,7 @@ import mage.filter.FilterCard; import mage.filter.FilterPermanent; import mage.filter.common.FilterNonlandPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; /** diff --git a/Mage.Sets/src/mage/cards/j/JaceTheMindSculptor.java b/Mage.Sets/src/mage/cards/j/JaceTheMindSculptor.java index 7b829873b196..b4ae3b8e6cf1 100644 --- a/Mage.Sets/src/mage/cards/j/JaceTheMindSculptor.java +++ b/Mage.Sets/src/mage/cards/j/JaceTheMindSculptor.java @@ -92,7 +92,7 @@ public boolean apply(Game game, Ability source) { if (card != null) { Cards cards = new CardsImpl(card); controller.lookAtCards("Jace, the Mind Sculptor", cards, game); - if (controller.chooseUse(outcome, "Do you wish to put card on the bottom of player's library?", source, game)) { + if (controller.chooseUse(outcome, "Put that card on the bottom of its owner's library?", source, game)) { controller.moveCardToLibraryWithInfo(card, source, game, Zone.LIBRARY, false, false); } else { game.informPlayers(controller.getLogName() + " puts the card back on top of the library."); diff --git a/Mage.Sets/src/mage/cards/j/JackalopeHerd.java b/Mage.Sets/src/mage/cards/j/JackalopeHerd.java index 113eed3fa4c2..aca52e91b857 100644 --- a/Mage.Sets/src/mage/cards/j/JackalopeHerd.java +++ b/Mage.Sets/src/mage/cards/j/JackalopeHerd.java @@ -10,6 +10,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.FilterSpell; +import mage.filter.StaticFilters; /** * @@ -25,7 +26,7 @@ public JackalopeHerd(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(5); // When you cast a spell, return Jackalope Herd to its owner's hand. - this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), new FilterSpell("a spell"), false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), StaticFilters.FILTER_SPELL_A, false)); } private JackalopeHerd(final JackalopeHerd card) { diff --git a/Mage.Sets/src/mage/cards/j/JadeBearer.java b/Mage.Sets/src/mage/cards/j/JadeBearer.java index d8adf1bc1614..e2dc08431945 100644 --- a/Mage.Sets/src/mage/cards/j/JadeBearer.java +++ b/Mage.Sets/src/mage/cards/j/JadeBearer.java @@ -11,7 +11,7 @@ import mage.constants.*; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/j/JadeMonolith.java b/Mage.Sets/src/mage/cards/j/JadeMonolith.java index e51b283adcd8..51fa65d4bc4f 100644 --- a/Mage.Sets/src/mage/cards/j/JadeMonolith.java +++ b/Mage.Sets/src/mage/cards/j/JadeMonolith.java @@ -93,15 +93,13 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE; + return event.getType() == EventType.DAMAGE_PERMANENT; } @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getSourceId().equals(targetSource.getFirstTarget()) && event.getTargetId().equals(source.getFirstTarget())) { - return true; - } - return false; + return event.getSourceId().equals(targetSource.getFirstTarget()) + && event.getTargetId().equals(source.getFirstTarget()); } } diff --git a/Mage.Sets/src/mage/cards/j/JadeStatue.java b/Mage.Sets/src/mage/cards/j/JadeStatue.java index 4904534dd7ca..590772a94f63 100644 --- a/Mage.Sets/src/mage/cards/j/JadeStatue.java +++ b/Mage.Sets/src/mage/cards/j/JadeStatue.java @@ -27,7 +27,7 @@ public JadeStatue(UUID ownerId, CardSetInfo setInfo) { // {2}: Jade Statue becomes a 3/6 Golem artifact creature until end of combat. Activate this ability only during combat. - this.addAbility(new ConditionalActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect (new JadeStatueToken(), "", Duration.EndOfCombat), new ManaCostsImpl("{2}"), new IsPhaseCondition(TurnPhase.COMBAT), "{2}: {this} becomes a 3/6 Golem artifact creature until end of combat. Activate this ability only during combat.")); + this.addAbility(new ConditionalActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect (new JadeStatueToken(), "", Duration.EndOfCombat), new ManaCostsImpl("{2}"), new IsPhaseCondition(TurnPhase.COMBAT), "{2}: {this} becomes a 3/6 Golem artifact creature until end of combat. Activate only during combat.")); } private JadeStatue(final JadeStatue card) { diff --git a/Mage.Sets/src/mage/cards/j/JadziOracleOfArcavios.java b/Mage.Sets/src/mage/cards/j/JadziOracleOfArcavios.java new file mode 100644 index 000000000000..f44089be340b --- /dev/null +++ b/Mage.Sets/src/mage/cards/j/JadziOracleOfArcavios.java @@ -0,0 +1,158 @@ +package mage.cards.j; + +import mage.ApprovingObject; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.ReturnToHandSourceEffect; +import mage.abilities.hint.common.LandsYouControlHint; +import mage.cards.Card; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.cards.ModalDoubleFacesCard; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInHand; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class JadziOracleOfArcavios extends ModalDoubleFacesCard { + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, ComparisonType.MORE_THAN, 7 + ); + + public JadziOracleOfArcavios(UUID ownerId, CardSetInfo setInfo) { + super( + ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.HUMAN, SubType.WIZARD}, "{6}{U}{U}", + "Journey to the Oracle", + new CardType[]{CardType.SORCERY}, new SubType[]{}, "{2}{G}{G}" + ); + + // 1. + // Jadzi, Oracle of Arcavios + // Legendary Creature - Human Wizard + this.getLeftHalfCard().addSuperType(SuperType.LEGENDARY); + this.getLeftHalfCard().setPT(5, 5); + + // Discard a card: Return Jadzi, Oracle of Arcavios to its owner's hand. + this.getLeftHalfCard().addAbility(new SimpleActivatedAbility( + new ReturnToHandSourceEffect(true), new DiscardCardCost() + )); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, reveal the top card of your library. If it's a nonland card, you may cast it by paying {1} rather than paying its mana cost. If it's a land card, put it onto the battlefield. + this.getLeftHalfCard().addAbility(new MagecraftAbility(new JadziOracleOfArcaviosEffect())); + + // 1. + // Journey to the Oracle + // Sorcery + // You may put any number of land cards from your hand onto the battlefield. Then if you control eight or more lands, you may discard a card. If you do, return Journey to the Oracle to it owner's hand. + this.getRightHalfCard().getSpellAbility().addEffect(new JourneyToTheOracleEffect()); + this.getRightHalfCard().getSpellAbility().addEffect(new ConditionalOneShotEffect( + new DoIfCostPaid( + new ReturnToHandSourceEffect(), new DiscardCardCost() + ), condition, "Then if you control eight or more lands, " + + "you may discard a card. If you do, return {this} to its owner's hand." + )); + this.getRightHalfCard().getSpellAbility().addHint(LandsYouControlHint.instance); + } + + private JadziOracleOfArcavios(final JadziOracleOfArcavios card) { + super(card); + } + + @Override + public JadziOracleOfArcavios copy() { + return new JadziOracleOfArcavios(this); + } +} + +class JadziOracleOfArcaviosEffect extends OneShotEffect { + + JadziOracleOfArcaviosEffect() { + super(Outcome.Benefit); + staticText = "reveal the top card of your library. If it's a nonland card, you may cast it " + + "by paying {1} rather than paying its mana cost. If it's a land card, put it onto the battlefield"; + } + + private JadziOracleOfArcaviosEffect(final JadziOracleOfArcaviosEffect effect) { + super(effect); + } + + @Override + public JadziOracleOfArcaviosEffect copy() { + return new JadziOracleOfArcaviosEffect(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.revealCards(source, new CardsImpl(card), game); + if (card.isLand()) { + return player.moveCards(card, Zone.BATTLEFIELD, source, game); + } + if (!player.chooseUse(outcome, "Cast " + card.getName() + " by paying {1}?", source, game)) { + return false; + } + SpellAbility spellAbility = player.chooseAbilityForCast(card, game, true); + if (spellAbility == null) { + return false; + } + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); + player.setCastSourceIdWithAlternateMana(card.getId(), new ManaCostsImpl<>("{1}"), null); + player.cast(spellAbility, game, false, new ApprovingObject(source, game)); + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); + return true; + } +} + +class JourneyToTheOracleEffect extends OneShotEffect { + + JourneyToTheOracleEffect() { + super(Outcome.Benefit); + staticText = "You may put any number of land cards from your hand onto the battlefield"; + } + + private JourneyToTheOracleEffect(final JourneyToTheOracleEffect effect) { + super(effect); + } + + @Override + public JourneyToTheOracleEffect copy() { + return new JourneyToTheOracleEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + TargetCardInHand target = new TargetCardInHand( + 0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD_LANDS + ); + player.choose(Outcome.PutCardInPlay, target, source.getSourceId(), game); + return player.moveCards(new CardsImpl(target.getTargets()), Zone.BATTLEFIELD, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/j/JaradsOrders.java b/Mage.Sets/src/mage/cards/j/JaradsOrders.java index 22d5cbfdc138..264b79f361d8 100644 --- a/Mage.Sets/src/mage/cards/j/JaradsOrders.java +++ b/Mage.Sets/src/mage/cards/j/JaradsOrders.java @@ -44,7 +44,7 @@ class JaradsOrdersEffect extends OneShotEffect { public JaradsOrdersEffect() { super(Outcome.PutLandInPlay); - staticText = "Search your library for up to two creature cards and reveal them. Put one into your hand and the other into your graveyard. Then shuffle your library"; + staticText = "Search your library for up to two creature cards and reveal them. Put one into your hand and the other into your graveyard. Then shuffle"; } public JaradsOrdersEffect(final JaradsOrdersEffect effect) { diff --git a/Mage.Sets/src/mage/cards/j/JayaBallard.java b/Mage.Sets/src/mage/cards/j/JayaBallard.java index 6f11b3855585..5a1e88a84a32 100644 --- a/Mage.Sets/src/mage/cards/j/JayaBallard.java +++ b/Mage.Sets/src/mage/cards/j/JayaBallard.java @@ -1,25 +1,18 @@ package mage.cards.j; import mage.Mana; -import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.GetEmblemEffect; +import mage.abilities.effects.common.discard.DiscardAndDrawThatManyEffect; import mage.abilities.effects.mana.AddConditionalManaEffect; import mage.abilities.mana.builder.common.InstantOrSorcerySpellManaBuilder; 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.SuperType; -import mage.filter.FilterCard; -import mage.game.Game; import mage.game.command.emblems.JayaBallardEmblem; -import mage.players.Player; -import mage.target.common.TargetDiscard; import mage.watchers.common.CastFromGraveyardWatcher; import java.util.UUID; @@ -37,13 +30,17 @@ public JayaBallard(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); // +1: Add {R}{R}{R}. Spend this mana only to cast instant or sorcery spells. - this.addAbility(new LoyaltyAbility(new AddConditionalManaEffect(Mana.RedMana(3), new InstantOrSorcerySpellManaBuilder()), 1)); + this.addAbility(new LoyaltyAbility(new AddConditionalManaEffect( + Mana.RedMana(3), new InstantOrSorcerySpellManaBuilder() + ), 1)); // +1: Discard up to three cards, then draw that many cards. - this.addAbility(new LoyaltyAbility(new JayaBallardDiscardDrawEffect(), 1)); + this.addAbility(new LoyaltyAbility(new DiscardAndDrawThatManyEffect(3), 1)); // −8: You get an emblem with "You may cast instant and sorcery cards from your graveyard. If a card cast this way would be put into your graveyard, exile it instead." - this.addAbility(new LoyaltyAbility(new GetEmblemEffect(new JayaBallardEmblem()), -8), new CastFromGraveyardWatcher()); + this.addAbility(new LoyaltyAbility( + new GetEmblemEffect(new JayaBallardEmblem()), -8 + ), new CastFromGraveyardWatcher()); } private JayaBallard(final JayaBallard card) { @@ -55,33 +52,3 @@ public JayaBallard copy() { return new JayaBallard(this); } } - -class JayaBallardDiscardDrawEffect extends OneShotEffect { - - public JayaBallardDiscardDrawEffect() { - super(Outcome.Detriment); - this.staticText = "Discard up to three cards, then draw that many cards"; - } - - public JayaBallardDiscardDrawEffect(final JayaBallardDiscardDrawEffect effect) { - super(effect); - } - - @Override - public JayaBallardDiscardDrawEffect copy() { - return new JayaBallardDiscardDrawEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { - return false; - } - TargetDiscard target = new TargetDiscard(0, 3, new FilterCard(), controller.getId()); - target.choose(outcome, controller.getId(), source.getSourceId(), game); - int count = controller.discard(new CardsImpl(target.getTargets()), false, source, game).size(); - controller.drawCards(count, source, game); - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/j/JayaVeneratedFiremage.java b/Mage.Sets/src/mage/cards/j/JayaVeneratedFiremage.java index 8805450e1bd5..f124de4b7782 100644 --- a/Mage.Sets/src/mage/cards/j/JayaVeneratedFiremage.java +++ b/Mage.Sets/src/mage/cards/j/JayaVeneratedFiremage.java @@ -64,8 +64,7 @@ private JayaVeneratedFiremageEffect(final JayaVeneratedFiremageEffect effect) { @Override public boolean checksEventType(GameEvent event, Game game) { switch (event.getType()) { - case DAMAGE_CREATURE: - case DAMAGE_PLANESWALKER: + case DAMAGE_PERMANENT: case DAMAGE_PLAYER: return true; default: diff --git a/Mage.Sets/src/mage/cards/j/JediInstructor.java b/Mage.Sets/src/mage/cards/j/JediInstructor.java index 80cc06c6be22..be0757481b29 100644 --- a/Mage.Sets/src/mage/cards/j/JediInstructor.java +++ b/Mage.Sets/src/mage/cards/j/JediInstructor.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/j/JediSentinel.java b/Mage.Sets/src/mage/cards/j/JediSentinel.java index b4802f9787f4..dbaf877bb75f 100644 --- a/Mage.Sets/src/mage/cards/j/JediSentinel.java +++ b/Mage.Sets/src/mage/cards/j/JediSentinel.java @@ -12,7 +12,7 @@ import mage.constants.SubType; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/j/JeeringInstigator.java b/Mage.Sets/src/mage/cards/j/JeeringInstigator.java index 10cb9d136214..61beb4fa0ec7 100644 --- a/Mage.Sets/src/mage/cards/j/JeeringInstigator.java +++ b/Mage.Sets/src/mage/cards/j/JeeringInstigator.java @@ -19,7 +19,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/j/JeganthaTheWellspring.java b/Mage.Sets/src/mage/cards/j/JeganthaTheWellspring.java index ccb82e3359fd..a18abde231f9 100644 --- a/Mage.Sets/src/mage/cards/j/JeganthaTheWellspring.java +++ b/Mage.Sets/src/mage/cards/j/JeganthaTheWellspring.java @@ -69,8 +69,7 @@ public boolean isLegal(Set deck, int startingSize) { private static boolean checkCard(Card card) { Map symbolMap = new HashMap(); - return card.getManaCost() - .getSymbols() + return card.getManaCostSymbols() .stream() .anyMatch(s -> symbolMap.compute( s, (str, i) -> (i == null) ? 1 : i + 1 diff --git a/Mage.Sets/src/mage/cards/j/JelevaNephaliasScourge.java b/Mage.Sets/src/mage/cards/j/JelevaNephaliasScourge.java index 3a76cca32b25..95915d4afb59 100644 --- a/Mage.Sets/src/mage/cards/j/JelevaNephaliasScourge.java +++ b/Mage.Sets/src/mage/cards/j/JelevaNephaliasScourge.java @@ -172,7 +172,7 @@ public void watch(GameEvent event, Game game) { Spell spell = (Spell) stackObject; manaSpendToCast.putIfAbsent(spell.getSourceId().toString() + spell.getCard().getZoneChangeCounter(game), - spell.getSpellAbility().getManaCostsToPay().convertedManaCost()); + spell.getSpellAbility().getManaCostsToPay().manaValue()); } } } diff --git a/Mage.Sets/src/mage/cards/j/JeskaiBarricade.java b/Mage.Sets/src/mage/cards/j/JeskaiBarricade.java index ecd471db5517..96abb9c9bf2e 100644 --- a/Mage.Sets/src/mage/cards/j/JeskaiBarricade.java +++ b/Mage.Sets/src/mage/cards/j/JeskaiBarricade.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/j/JestersCap.java b/Mage.Sets/src/mage/cards/j/JestersCap.java index be742ebbf926..07a5ed3ba8f6 100644 --- a/Mage.Sets/src/mage/cards/j/JestersCap.java +++ b/Mage.Sets/src/mage/cards/j/JestersCap.java @@ -51,7 +51,7 @@ class JestersCapEffect extends OneShotEffect { public JestersCapEffect() { super(Outcome.Benefit); - this.staticText = "Search target player's library for three cards and exile them. Then that player shuffles their library"; + this.staticText = "Search target player's library for three cards and exile them. Then that player shuffles"; } public JestersCapEffect(final JestersCapEffect effect) { diff --git a/Mage.Sets/src/mage/cards/j/JestersScepter.java b/Mage.Sets/src/mage/cards/j/JestersScepter.java index 75eff4eff920..32d8e7478102 100644 --- a/Mage.Sets/src/mage/cards/j/JestersScepter.java +++ b/Mage.Sets/src/mage/cards/j/JestersScepter.java @@ -156,7 +156,8 @@ public boolean pay(Ability ability, Game game, Ability source, UUID controllerId TargetCardInExile target = new TargetCardInExile(new FilterCard(), CardUtil.getCardExileZoneId(game, ability)); target.setNotTarget(true); Cards cards = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, ability)); - if (!cards.isEmpty() + if (cards != null + && !cards.isEmpty() && controller.choose(Outcome.Benefit, cards, target, game)) { Card card = game.getCard(target.getFirstTarget()); if (card != null) { diff --git a/Mage.Sets/src/mage/cards/j/JeweledAmulet.java b/Mage.Sets/src/mage/cards/j/JeweledAmulet.java index eb7dee7cee9b..9c879362d787 100644 --- a/Mage.Sets/src/mage/cards/j/JeweledAmulet.java +++ b/Mage.Sets/src/mage/cards/j/JeweledAmulet.java @@ -29,7 +29,7 @@ */ public final class JeweledAmulet extends CardImpl { - private static final String rule = "{1}, {T}: Put a charge counter on {this}. Note the type of mana spent to pay this activation cost. Activate this ability only if there are no charge counters on {this}"; + private static final String rule = "{1}, {T}: Put a charge counter on {this}. Note the type of mana spent to pay this activation cost. Activate only if there are no charge counters on {this}"; public JeweledAmulet(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{0}"); @@ -63,7 +63,7 @@ class JeweledAmuletAddCounterEffect extends OneShotEffect { public JeweledAmuletAddCounterEffect() { super(Outcome.Benefit); - this.staticText = "Note the type of mana spent to pay this activation cost. Activate this ability only if there are no charge counters on {this}"; + this.staticText = "Note the type of mana spent to pay this activation cost. Activate only if there are no charge counters on {this}"; } public JeweledAmuletAddCounterEffect(final JeweledAmuletAddCounterEffect effect) { diff --git a/Mage.Sets/src/mage/cards/j/JeweledLotus.java b/Mage.Sets/src/mage/cards/j/JeweledLotus.java index 435512972a32..561226a28a66 100644 --- a/Mage.Sets/src/mage/cards/j/JeweledLotus.java +++ b/Mage.Sets/src/mage/cards/j/JeweledLotus.java @@ -10,7 +10,7 @@ import mage.constants.CardType; import mage.constants.TargetController; import mage.filter.FilterSpell; -import mage.filter.predicate.permanent.CommanderPredicate; +import mage.filter.predicate.mageobject.CommanderPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/j/JhoiraOfTheGhitu.java b/Mage.Sets/src/mage/cards/j/JhoiraOfTheGhitu.java index ceb598598216..bfc7e937e9dd 100644 --- a/Mage.Sets/src/mage/cards/j/JhoiraOfTheGhitu.java +++ b/Mage.Sets/src/mage/cards/j/JhoiraOfTheGhitu.java @@ -58,7 +58,7 @@ class JhoiraOfTheGhituSuspendEffect extends OneShotEffect { public JhoiraOfTheGhituSuspendEffect() { super(Outcome.PutCardInPlay); - this.staticText = "Put four time counters on the exiled card. If it doesn't have suspend, it gains suspend (At the beginning of your upkeep, remove a time counter from that card. When the last is removed, cast it without paying its mana cost. If it's a creature, it has haste.)"; + this.staticText = "Put four time counters on the exiled card. If it doesn't have suspend, it gains suspend. (At the beginning of your upkeep, remove a time counter from that card. When the last is removed, cast it without paying its mana cost. If it's a creature, it has haste.)"; } public JhoiraOfTheGhituSuspendEffect(final JhoiraOfTheGhituSuspendEffect effect) { diff --git a/Mage.Sets/src/mage/cards/j/Jilt.java b/Mage.Sets/src/mage/cards/j/Jilt.java index c4bc2b79c801..8f90ac0e26b5 100644 --- a/Mage.Sets/src/mage/cards/j/Jilt.java +++ b/Mage.Sets/src/mage/cards/j/Jilt.java @@ -12,7 +12,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/j/JorKadeenThePrevailer.java b/Mage.Sets/src/mage/cards/j/JorKadeenThePrevailer.java index 13e3088954e3..07aa41f3e226 100644 --- a/Mage.Sets/src/mage/cards/j/JorKadeenThePrevailer.java +++ b/Mage.Sets/src/mage/cards/j/JorKadeenThePrevailer.java @@ -19,7 +19,7 @@ */ public final class JorKadeenThePrevailer extends CardImpl { - private static final String effectText = "Metalcraft — Creatures you control get +3/+0 as long as you control three or more artifacts."; + private static final String effectText = "Creatures you control get +3/+0 as long as you control three or more artifacts."; public JorKadeenThePrevailer(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{W}"); diff --git a/Mage.Sets/src/mage/cards/j/JoragaAuxiliary.java b/Mage.Sets/src/mage/cards/j/JoragaAuxiliary.java index aa40559229ed..8ae089d9368a 100644 --- a/Mage.Sets/src/mage/cards/j/JoragaAuxiliary.java +++ b/Mage.Sets/src/mage/cards/j/JoragaAuxiliary.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/j/JoragaWarcaller.java b/Mage.Sets/src/mage/cards/j/JoragaWarcaller.java index f2c4a7b653fd..27d71d108c76 100644 --- a/Mage.Sets/src/mage/cards/j/JoragaWarcaller.java +++ b/Mage.Sets/src/mage/cards/j/JoragaWarcaller.java @@ -15,7 +15,7 @@ import mage.constants.*; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/j/JourneyForTheElixir.java b/Mage.Sets/src/mage/cards/j/JourneyForTheElixir.java index adf1fd7049e6..3051f4f11895 100644 --- a/Mage.Sets/src/mage/cards/j/JourneyForTheElixir.java +++ b/Mage.Sets/src/mage/cards/j/JourneyForTheElixir.java @@ -31,7 +31,7 @@ public JourneyForTheElixir(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{G}"); // Search your library and graveyard for a basic land card and a card named Jiang Yanggu, reveal them, put them into your hand, then shuffle your library. - this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new JourneyForTheElixirLibraryTarget())); + this.getSpellAbility().addEffect(new JourneyForTheElixirEffect()); } private JourneyForTheElixir(final JourneyForTheElixir card) { @@ -49,7 +49,7 @@ class JourneyForTheElixirEffect extends OneShotEffect { JourneyForTheElixirEffect() { super(Outcome.Benefit); staticText = "Search your library and graveyard for a basic land card and a card named Jiang Yanggu, " + - "reveal them, put them into your hand, then shuffle your library."; + "reveal them, put them into your hand, then shuffle."; } private JourneyForTheElixirEffect(final JourneyForTheElixirEffect effect) { diff --git a/Mage.Sets/src/mage/cards/j/JourneyOfDiscovery.java b/Mage.Sets/src/mage/cards/j/JourneyOfDiscovery.java index 5f4b072b339b..cb24153a7e14 100644 --- a/Mage.Sets/src/mage/cards/j/JourneyOfDiscovery.java +++ b/Mage.Sets/src/mage/cards/j/JourneyOfDiscovery.java @@ -24,7 +24,7 @@ public JourneyOfDiscovery(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{G}"); // Choose one - Search your library for up to two basic land cards, reveal them, put them into your hand, then shuffle your library; - this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 2, StaticFilters.FILTER_CARD_BASIC_LAND))); + this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 2, StaticFilters.FILTER_CARD_BASIC_LANDS), true)); // or you may play up to two additional lands this turn. Mode mode = new Mode(); diff --git a/Mage.Sets/src/mage/cards/j/JourneyToNowhere.java b/Mage.Sets/src/mage/cards/j/JourneyToNowhere.java index 40ac830b4bec..938f60007133 100644 --- a/Mage.Sets/src/mage/cards/j/JourneyToNowhere.java +++ b/Mage.Sets/src/mage/cards/j/JourneyToNowhere.java @@ -12,7 +12,7 @@ import mage.constants.CardType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.Target; import mage.target.TargetPermanent; diff --git a/Mage.Sets/src/mage/cards/j/JubilantMascot.java b/Mage.Sets/src/mage/cards/j/JubilantMascot.java index 16ae99f91045..6cb52caff596 100644 --- a/Mage.Sets/src/mage/cards/j/JubilantMascot.java +++ b/Mage.Sets/src/mage/cards/j/JubilantMascot.java @@ -15,7 +15,7 @@ import mage.constants.TargetController; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/j/JudgeUnworthy.java b/Mage.Sets/src/mage/cards/j/JudgeUnworthy.java index 189e682b30f4..ebe17f1fe925 100644 --- a/Mage.Sets/src/mage/cards/j/JudgeUnworthy.java +++ b/Mage.Sets/src/mage/cards/j/JudgeUnworthy.java @@ -48,7 +48,7 @@ class JudgeUnworthyEffect extends OneShotEffect { public JudgeUnworthyEffect() { super(Outcome.Damage); - this.staticText = ", then reveal the top card of your library. {this} deals damage equal to that card's converted mana cost to that creature"; + this.staticText = ", then reveal the top card of your library. {this} deals damage equal to that card's mana value to that creature"; } public JudgeUnworthyEffect(final JudgeUnworthyEffect effect) { @@ -70,7 +70,7 @@ public boolean apply(Game game, Ability source) { controller.revealCards(sourceCard.getName(), new CardsImpl(card), game); Permanent targetCreature = game.getPermanent(this.getTargetPointer().getFirst(game, source)); if (targetCreature != null) { - targetCreature.damage(card.getConvertedManaCost(), source.getSourceId(), source, game, false, true); + targetCreature.damage(card.getManaValue(), source.getSourceId(), source, game, false, true); return true; } } diff --git a/Mage.Sets/src/mage/cards/j/JundHackblade.java b/Mage.Sets/src/mage/cards/j/JundHackblade.java index f69e22c9c5f5..c52fe1306a79 100644 --- a/Mage.Sets/src/mage/cards/j/JundHackblade.java +++ b/Mage.Sets/src/mage/cards/j/JundHackblade.java @@ -18,7 +18,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.MulticoloredPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/j/JungleWayfinder.java b/Mage.Sets/src/mage/cards/j/JungleWayfinder.java index 794f75f8d1a5..30ab2f8a1115 100644 --- a/Mage.Sets/src/mage/cards/j/JungleWayfinder.java +++ b/Mage.Sets/src/mage/cards/j/JungleWayfinder.java @@ -51,7 +51,7 @@ class JungleWayfinderEffect extends OneShotEffect { public JungleWayfinderEffect() { super(Outcome.Benefit); - this.staticText = "each player may search their library for a basic land card, reveal it, put it into their hand, then shuffle their library"; + this.staticText = "each player may search their library for a basic land card, reveal it, put it into their hand, then shuffle"; } public JungleWayfinderEffect(final JungleWayfinderEffect effect) { diff --git a/Mage.Sets/src/mage/cards/j/JuniperOrderRanger.java b/Mage.Sets/src/mage/cards/j/JuniperOrderRanger.java index f8c9e112343d..ee986f5c2658 100644 --- a/Mage.Sets/src/mage/cards/j/JuniperOrderRanger.java +++ b/Mage.Sets/src/mage/cards/j/JuniperOrderRanger.java @@ -15,7 +15,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/j/Justice.java b/Mage.Sets/src/mage/cards/j/Justice.java index 854eb20032a8..72342d4c486b 100644 --- a/Mage.Sets/src/mage/cards/j/Justice.java +++ b/Mage.Sets/src/mage/cards/j/Justice.java @@ -67,9 +67,8 @@ public JusticeTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE - || event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT + || event.getType() == GameEvent.EventType.DAMAGED_PLAYER; } @Override diff --git a/Mage.Sets/src/mage/cards/j/Juxtapose.java b/Mage.Sets/src/mage/cards/j/Juxtapose.java index 7e6ebe710006..236ee8147d75 100644 --- a/Mage.Sets/src/mage/cards/j/Juxtapose.java +++ b/Mage.Sets/src/mage/cards/j/Juxtapose.java @@ -29,8 +29,8 @@ public Juxtapose(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}"); // You and target player exchange control of the creature you each control with the highest converted mana cost. Then exchange control of artifacts the same way. If two or more permanents a player controls are tied for highest cost, their controller chooses one of them. - this.getSpellAbility().addEffect(new JuxtaposeEffect(StaticFilters.FILTER_PERMANENT_CREATURE, "You and target player exchange control of the creature you each control with the highest converted mana cost")); - this.getSpellAbility().addEffect(new JuxtaposeEffect(new FilterArtifactPermanent(), "Then exchange control of artifacts the same way. If two or more permanents a player controls are tied for highest cost, their controller chooses one of them")); + this.getSpellAbility().addEffect(new JuxtaposeEffect(StaticFilters.FILTER_PERMANENT_CREATURE, "You and target player exchange control of the creature you each control with the highest mana value")); + this.getSpellAbility().addEffect(new JuxtaposeEffect(new FilterArtifactPermanent(), "Then exchange control of artifacts the same way. If two or more permanents a player controls are tied for highest, their controller chooses one of them")); this.getSpellAbility().addTarget(new TargetPlayer()); } @@ -140,13 +140,13 @@ private List getPermanentsWithTheHighestCMC(Game game, UUID playerId, List permanents = game.getBattlefield().getAllActivePermanents(filter, playerId, game); int highestCMC = -1; for (Permanent permanent : permanents) { - if (highestCMC < permanent.getConvertedManaCost()) { - highestCMC = permanent.getConvertedManaCost(); + if (highestCMC < permanent.getManaValue()) { + highestCMC = permanent.getManaValue(); } } List result = new ArrayList<>(); for (Permanent permanent : permanents) { - if (permanent.getConvertedManaCost() == highestCMC) { + if (permanent.getManaValue() == highestCMC) { result.add(permanent); } } diff --git a/Mage.Sets/src/mage/cards/j/JwariShapeshifter.java b/Mage.Sets/src/mage/cards/j/JwariShapeshifter.java index 04aca55b2fbb..6b9bda453719 100644 --- a/Mage.Sets/src/mage/cards/j/JwariShapeshifter.java +++ b/Mage.Sets/src/mage/cards/j/JwariShapeshifter.java @@ -10,7 +10,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/k/KadenaSlinkingSorcerer.java b/Mage.Sets/src/mage/cards/k/KadenaSlinkingSorcerer.java index 181c71867644..00c25107f62f 100644 --- a/Mage.Sets/src/mage/cards/k/KadenaSlinkingSorcerer.java +++ b/Mage.Sets/src/mage/cards/k/KadenaSlinkingSorcerer.java @@ -16,8 +16,8 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicate; -import mage.filter.predicate.other.FaceDownCastablePredicate; -import mage.filter.predicate.other.FaceDownPredicate; +import mage.filter.predicate.card.FaceDownCastablePredicate; +import mage.filter.predicate.card.FaceDownPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; diff --git a/Mage.Sets/src/mage/cards/k/KaervekTheMerciless.java b/Mage.Sets/src/mage/cards/k/KaervekTheMerciless.java index d3396f289e9b..22f1c228534d 100644 --- a/Mage.Sets/src/mage/cards/k/KaervekTheMerciless.java +++ b/Mage.Sets/src/mage/cards/k/KaervekTheMerciless.java @@ -31,7 +31,7 @@ public KaervekTheMerciless(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(4); // Whenever an opponent casts a spell, Kaervek the Merciless deals damage to any target equal to that spell's converted mana cost. - Ability ability = new SpellCastOpponentTriggeredAbility(Zone.BATTLEFIELD, new KaervekTheMercilessEffect(), StaticFilters.FILTER_SPELL, false, SetTargetPointer.SPELL); + Ability ability = new SpellCastOpponentTriggeredAbility(Zone.BATTLEFIELD, new KaervekTheMercilessEffect(), StaticFilters.FILTER_SPELL_A, false, SetTargetPointer.SPELL); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); @@ -51,7 +51,7 @@ class KaervekTheMercilessEffect extends OneShotEffect { public KaervekTheMercilessEffect() { super(Outcome.Benefit); - this.staticText = "{this} deals damage to any target equal to that spell's converted mana cost"; + this.staticText = "{this} deals damage to any target equal to that spell's mana value"; } public KaervekTheMercilessEffect(final KaervekTheMercilessEffect effect) { @@ -67,7 +67,7 @@ public KaervekTheMercilessEffect copy() { public boolean apply(Game game, Ability source) { Spell spell = game.getSpellOrLKIStack(this.getTargetPointer().getFirst(game, source)); if (spell != null) { - int cost = spell.getConvertedManaCost(); + int cost = spell.getManaValue(); Player target = game.getPlayer(source.getFirstTarget()); if (target != null) { target.damage(cost, source.getSourceId(), source, game); diff --git a/Mage.Sets/src/mage/cards/k/KaerveksPurge.java b/Mage.Sets/src/mage/cards/k/KaerveksPurge.java index 43d352bd709d..aa62b44507d8 100644 --- a/Mage.Sets/src/mage/cards/k/KaerveksPurge.java +++ b/Mage.Sets/src/mage/cards/k/KaerveksPurge.java @@ -9,7 +9,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -48,8 +48,8 @@ enum KaerveksPurgeAdjuster implements TargetAdjuster { public void adjustTargets(Ability ability, Game game) { ability.getTargets().clear(); int xValue = ability.getManaCostsToPay().getX(); - FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with converted mana cost " + xValue); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, xValue)); + FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with mana value " + xValue); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, xValue)); ability.addTarget(new TargetCreaturePermanent(filter)); } } @@ -58,7 +58,7 @@ class KaerveksPurgeEffect extends OneShotEffect { public KaerveksPurgeEffect() { super(Outcome.DestroyPermanent); - this.staticText = "Destroy target creature with converted mana cost X. " + + this.staticText = "Destroy target creature with mana value X. " + "If that creature dies this way, " + "{this} deals damage equal to the creature's power" + " to the creature's controller"; diff --git a/Mage.Sets/src/mage/cards/k/KaheeraTheOrphanguard.java b/Mage.Sets/src/mage/cards/k/KaheeraTheOrphanguard.java index a186762ac71d..5220c04e96b0 100644 --- a/Mage.Sets/src/mage/cards/k/KaheeraTheOrphanguard.java +++ b/Mage.Sets/src/mage/cards/k/KaheeraTheOrphanguard.java @@ -15,7 +15,7 @@ import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.Arrays; import java.util.List; @@ -93,7 +93,7 @@ public String getRule() { ); private static boolean checkTypes(Card card) { - return subtypes.stream().anyMatch(subtype -> card.getSubtype().contains(subtype)); + return subtypes.stream().anyMatch(subtype -> card.hasSubtype(subtype, null)); } @Override diff --git a/Mage.Sets/src/mage/cards/k/KahoMinamoHistorian.java b/Mage.Sets/src/mage/cards/k/KahoMinamoHistorian.java index 4f8154036252..8217dae71e7d 100644 --- a/Mage.Sets/src/mage/cards/k/KahoMinamoHistorian.java +++ b/Mage.Sets/src/mage/cards/k/KahoMinamoHistorian.java @@ -19,7 +19,7 @@ import mage.constants.SuperType; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInExile; @@ -73,7 +73,7 @@ class KahoMinamoHistorianEffect extends SearchEffect { public KahoMinamoHistorianEffect() { super(new TargetCardInLibrary(0, 3, filter), Outcome.Benefit); this.staticText = "search your library for up to three instant cards " - + "and exile them. Then shuffle your library"; + + "and exile them. Then shuffle"; } public KahoMinamoHistorianEffect(final KahoMinamoHistorianEffect effect) { @@ -109,7 +109,7 @@ class KahoMinamoHistorianCastEffect extends OneShotEffect { public KahoMinamoHistorianCastEffect() { super(Outcome.PlayForFree); - this.staticText = "you may cast a card with converted mana cost X " + this.staticText = "you may cast a card with mana value X " + "exiled with {this} without paying its mana cost"; } @@ -127,10 +127,11 @@ public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { FilterCard filter = new FilterCard(); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, source.getManaCostsToPay().getX())); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, source.getManaCostsToPay().getX())); TargetCardInExile target = new TargetCardInExile(filter, CardUtil.getCardExileZoneId(game, source)); Cards cards = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); - if (!cards.isEmpty() + if (cards != null + && !cards.isEmpty() && controller.choose(Outcome.PlayForFree, cards, target, game)) { Card card = game.getCard(target.getFirstTarget()); if (card != null) { diff --git a/Mage.Sets/src/mage/cards/k/KalemneDiscipleOfIroas.java b/Mage.Sets/src/mage/cards/k/KalemneDiscipleOfIroas.java index e8e992c8f539..3c1741a85533 100644 --- a/Mage.Sets/src/mage/cards/k/KalemneDiscipleOfIroas.java +++ b/Mage.Sets/src/mage/cards/k/KalemneDiscipleOfIroas.java @@ -22,7 +22,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; @@ -32,11 +32,11 @@ */ public final class KalemneDiscipleOfIroas extends CardImpl { - private static final FilterSpell filterSpell = new FilterSpell("a creature spell with converted mana cost 5 or greater"); + private static final FilterSpell filterSpell = new FilterSpell("a creature spell with mana value 5 or greater"); static { filterSpell.add(CardType.CREATURE.getPredicate()); - filterSpell.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 4)); + filterSpell.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 4)); } public KalemneDiscipleOfIroas(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/k/KalitasTraitorOfGhet.java b/Mage.Sets/src/mage/cards/k/KalitasTraitorOfGhet.java index f859550cb309..5d1723c60ca8 100644 --- a/Mage.Sets/src/mage/cards/k/KalitasTraitorOfGhet.java +++ b/Mage.Sets/src/mage/cards/k/KalitasTraitorOfGhet.java @@ -18,7 +18,7 @@ import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; diff --git a/Mage.Sets/src/mage/cards/k/KamahlsDruidicVow.java b/Mage.Sets/src/mage/cards/k/KamahlsDruidicVow.java index 428fab7e879c..bf0ab36d674f 100644 --- a/Mage.Sets/src/mage/cards/k/KamahlsDruidicVow.java +++ b/Mage.Sets/src/mage/cards/k/KamahlsDruidicVow.java @@ -16,7 +16,7 @@ import mage.filter.FilterCard; import mage.filter.common.FilterPermanentCard; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; @@ -54,7 +54,7 @@ class KamahlsDruidicVowEffect extends OneShotEffect { public KamahlsDruidicVowEffect() { super(Outcome.PutCardInPlay); - this.staticText = "Look at the top X cards of your library. You may put any number of land and/or legendary permanent cards with converted mana cost X or less from among them onto the battlefield. Put the rest into your graveyard"; + this.staticText = "Look at the top X cards of your library. You may put any number of land and/or legendary permanent cards with mana value X or less from among them onto the battlefield. Put the rest into your graveyard"; } public KamahlsDruidicVowEffect(final KamahlsDruidicVowEffect effect) { @@ -71,8 +71,8 @@ public boolean apply(Game game, Ability source) { Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, xValue)); controller.lookAtCards(source, null, cards, game); if (!cards.isEmpty()) { - FilterCard filter = new FilterPermanentCard("land and/or legendary permanent cards with converted mana cost " + xValue + " or less to put onto the battlefield"); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, xValue + 1)); + FilterCard filter = new FilterPermanentCard("land and/or legendary permanent cards with mana value " + xValue + " or less to put onto the battlefield"); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); filter.add( Predicates.or( CardType.LAND.getPredicate(), diff --git a/Mage.Sets/src/mage/cards/k/KamiOfTheHonoredDead.java b/Mage.Sets/src/mage/cards/k/KamiOfTheHonoredDead.java index 4c0f016a27df..38a0f9275fe0 100644 --- a/Mage.Sets/src/mage/cards/k/KamiOfTheHonoredDead.java +++ b/Mage.Sets/src/mage/cards/k/KamiOfTheHonoredDead.java @@ -68,7 +68,7 @@ public KamiOfTheHonoredDeadTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/k/KangeeAerieKeeper.java b/Mage.Sets/src/mage/cards/k/KangeeAerieKeeper.java index 5c0b8129b286..d03e56b4c4da 100644 --- a/Mage.Sets/src/mage/cards/k/KangeeAerieKeeper.java +++ b/Mage.Sets/src/mage/cards/k/KangeeAerieKeeper.java @@ -17,7 +17,7 @@ import mage.constants.*; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/k/KarametrasBlessing.java b/Mage.Sets/src/mage/cards/k/KarametrasBlessing.java index c0de88dd3c40..59b3b33ad914 100644 --- a/Mage.Sets/src/mage/cards/k/KarametrasBlessing.java +++ b/Mage.Sets/src/mage/cards/k/KarametrasBlessing.java @@ -11,7 +11,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.filter.predicate.mageobject.EnchantmentOrEnchantedPredicate; +import mage.filter.predicate.permanent.EnchantmentOrEnchantedPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/k/KardursViciousReturn.java b/Mage.Sets/src/mage/cards/k/KardursViciousReturn.java index 58290c0b11a3..36ff9e8d073a 100644 --- a/Mage.Sets/src/mage/cards/k/KardursViciousReturn.java +++ b/Mage.Sets/src/mage/cards/k/KardursViciousReturn.java @@ -56,7 +56,7 @@ ability, new SacrificeTargetCost(new TargetControlledPermanent(StaticFilters.FIL // III — Return target creature card from your graveyard to the battlefield. // Put a +1/+1 counter on it. It gains haste until your next turn. Effects effects = new Effects( - new ReturnFromGraveyardToBattlefieldTargetEffect(false, false), + new ReturnFromGraveyardToBattlefieldTargetEffect(), new AddCountersTargetEffect(CounterType.P1P1.createInstance()) .setText("Put a +1/+1 counter on it"), new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.UntilYourNextTurn) diff --git a/Mage.Sets/src/mage/cards/k/KarnScionOfUrza.java b/Mage.Sets/src/mage/cards/k/KarnScionOfUrza.java index 1ec0683c68fa..ed5784b42a6e 100644 --- a/Mage.Sets/src/mage/cards/k/KarnScionOfUrza.java +++ b/Mage.Sets/src/mage/cards/k/KarnScionOfUrza.java @@ -44,7 +44,7 @@ public KarnScionOfUrza(UUID ownerId, CardSetInfo setInfo) { this.addAbility(ability2); // -2: Create a 0/0 colorless Construct artifact creature token with "This creature gets +1/+1 for each artifact you control." - LoyaltyAbility ability3 = new LoyaltyAbility(new KarnConstructEffect(), -2); + LoyaltyAbility ability3 = new LoyaltyAbility(new CreateTokenEffect(new KarnConstructToken(), 1), -2); ability3.addHint(ArtifactYouControlHint.instance); this.addAbility(ability3); } @@ -61,12 +61,13 @@ public KarnScionOfUrza copy() { class KarnPlus1Effect extends OneShotEffect { - public KarnPlus1Effect() { + KarnPlus1Effect() { super(Outcome.Benefit); - this.staticText = "Reveal the top two cards of your library. An opponent chooses one of them. Put that card into your hand and exile the other with a silver counter on it."; + this.staticText = "Reveal the top two cards of your library. An opponent chooses one of them. " + + "Put that card into your hand and exile the other with a silver counter on it."; } - public KarnPlus1Effect(final KarnPlus1Effect effect) { + private KarnPlus1Effect(final KarnPlus1Effect effect) { super(effect); } @@ -79,43 +80,44 @@ public KarnPlus1Effect copy() { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = game.getObject(source.getSourceId()); - if (sourceObject != null && controller != null) { - Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, 2)); - - if (!cards.isEmpty()) { - controller.revealCards(staticText, cards, game); - Card cardToHand; - if (cards.size() == 1) { - cardToHand = cards.getRandom(game); - } else { - Player opponent; - Set opponents = game.getOpponents(controller.getId()); - if (opponents.size() == 1) { - opponent = game.getPlayer(opponents.iterator().next()); - } else { - Target target = new TargetOpponent(true); - controller.chooseTarget(Outcome.Detriment, target, source, game); - opponent = game.getPlayer(target.getFirstTarget()); - } - TargetCard target = new TargetCard(1, Zone.LIBRARY, new FilterCard()); - opponent.chooseTarget(outcome, cards, target, source, game); - cardToHand = game.getCard(target.getFirstTarget()); - } - if (cardToHand != null) { - controller.moveCards(cardToHand, Zone.HAND, source, game); - cards.remove(cardToHand); - } - - if (!cards.isEmpty()) { - controller.moveCards(cards, Zone.EXILED, source, game); - for (Card c : cards.getCards(game)) { - c.addCounters(CounterType.SILVER.createInstance(1), source.getControllerId(), source, game); - } - } - } + if (sourceObject == null || controller == null) { + return false; + } + Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, 2)); + + if (cards.isEmpty()) { return true; } - return false; + controller.revealCards(staticText, cards, game); + Card cardToHand; + if (cards.size() == 1) { + cardToHand = cards.getRandom(game); + } else { + Player opponent; + Set opponents = game.getOpponents(controller.getId()); + if (opponents.size() == 1) { + opponent = game.getPlayer(opponents.iterator().next()); + } else { + Target target = new TargetOpponent(true); + controller.chooseTarget(Outcome.Detriment, target, source, game); + opponent = game.getPlayer(target.getFirstTarget()); + } + TargetCard target = new TargetCard(1, Zone.LIBRARY, new FilterCard()); + opponent.chooseTarget(outcome, cards, target, source, game); + cardToHand = game.getCard(target.getFirstTarget()); + } + if (cardToHand != null) { + controller.moveCards(cardToHand, Zone.HAND, source, game); + cards.remove(cardToHand); + } + + if (!cards.isEmpty()) { + controller.moveCards(cards, Zone.EXILED, source, game); + for (Card c : cards.getCards(game)) { + c.addCounters(CounterType.SILVER.createInstance(1), source.getControllerId(), source, game); + } + } + return true; } } @@ -127,12 +129,12 @@ class KarnMinus1Effect extends OneShotEffect { filter.add(CounterType.SILVER.getPredicate()); } - public KarnMinus1Effect() { + KarnMinus1Effect() { super(Outcome.ReturnToHand); this.staticText = "Put a card you own with a silver counter on it from exile into your hand"; } - public KarnMinus1Effect(final KarnMinus1Effect effect) { + private KarnMinus1Effect(final KarnMinus1Effect effect) { super(effect); } @@ -144,71 +146,41 @@ public KarnMinus1Effect copy() { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - ExileZone exZone = game.getExile().getPermanentExile(); // getExileZone(Zone.EXILED); - if (exZone != null) { - Card card = null; - - List exile = game.getExile().getAllCards(game); - boolean noTargets = exile.isEmpty(); - if (noTargets) { - game.informPlayer(controller, "You have no exiled cards."); - return true; - } - - Cards filteredCards = new CardsImpl(); - - for (Card exileCard : exile) { - if (exileCard.isOwnedBy(source.getControllerId()) && filter.match(exileCard, game)) { - filteredCards.add(exileCard); - } - } - - TargetCard target = new TargetCard(Zone.EXILED, filter); - target.setNotTarget(true); - if (controller.choose(Outcome.Benefit, filteredCards, target, game)) { - if (card == null) { - card = game.getCard(target.getFirstTarget()); - } - if (card != null) { - card.moveToZone(Zone.HAND, source, game, false); - Cards revealCard = new CardsImpl(); - revealCard.add(card); - controller.revealCards("BLAHALJALJDSLAKJD", revealCard, game); - controller.moveCards(card, Zone.HAND, source, game); - } - } - } + if (controller == null) { + return false; + } + ExileZone exZone = game.getExile().getPermanentExile(); // getExileZone(Zone.EXILED); + if (exZone == null) { return true; } - return false; - } -} - -class KarnConstructEffect extends OneShotEffect { + Card card = null; - public KarnConstructEffect() { - super(Outcome.PutCreatureInPlay); - this.staticText = "Create a 0/0 colorless Construct artifact creature token with \"This creature gets +1/+1 for each artifact you control.\""; - } + List exile = game.getExile().getAllCards(game); + boolean noTargets = exile.isEmpty(); + if (noTargets) { + game.informPlayer(controller, "You have no exiled cards."); + return true; + } - public KarnConstructEffect(final KarnConstructEffect effect) { - super(effect); - } + Cards filteredCards = new CardsImpl(); - @Override - public KarnConstructEffect copy() { - return new KarnConstructEffect(this); - } + for (Card exileCard : exile) { + if (exileCard.isOwnedBy(source.getControllerId()) && filter.match(exileCard, game)) { + filteredCards.add(exileCard); + } + } - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - CreateTokenEffect effect = new CreateTokenEffect(new KarnConstructToken(), 1); - effect.apply(game, source); + TargetCard target = new TargetCard(Zone.EXILED, filter); + target.setNotTarget(true); + if (!controller.choose(Outcome.Benefit, filteredCards, target, game)) { return true; } - return false; + if (card == null) { + card = game.getCard(target.getFirstTarget()); + } + if (card != null) { + controller.moveCards(card, Zone.HAND, source, game); + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/k/KarnSilverGolem.java b/Mage.Sets/src/mage/cards/k/KarnSilverGolem.java index e7e33379fb1f..d84895174f6a 100644 --- a/Mage.Sets/src/mage/cards/k/KarnSilverGolem.java +++ b/Mage.Sets/src/mage/cards/k/KarnSilverGolem.java @@ -60,7 +60,7 @@ class KarnSilverGolemEffect extends ContinuousEffectImpl { public KarnSilverGolemEffect() { super(Duration.EndOfTurn, Outcome.BecomeCreature); - staticText = "Target noncreature artifact becomes an artifact creature with power and toughness each equal to its converted mana cost until end of turn"; + staticText = "Target noncreature artifact becomes an artifact creature with power and toughness each equal to its mana value until end of turn"; } public KarnSilverGolemEffect(final KarnSilverGolemEffect effect) { @@ -89,7 +89,7 @@ public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) case PTChangingEffects_7: if (sublayer == SubLayer.SetPT_7b) { - int cmc = artifact.getConvertedManaCost(); + int cmc = artifact.getManaValue(); artifact.getPower().setValue(cmc); artifact.getToughness().setValue(cmc); } diff --git a/Mage.Sets/src/mage/cards/k/KarnTheGreatCreator.java b/Mage.Sets/src/mage/cards/k/KarnTheGreatCreator.java index ed497824c025..8f85098a320d 100644 --- a/Mage.Sets/src/mage/cards/k/KarnTheGreatCreator.java +++ b/Mage.Sets/src/mage/cards/k/KarnTheGreatCreator.java @@ -95,7 +95,7 @@ class KarnTheGreatCreatorAnimateEffect extends ContinuousEffectImpl { KarnTheGreatCreatorAnimateEffect() { super(Duration.UntilYourNextTurn, Outcome.BecomeCreature); staticText = "Until your next turn, up to one target noncreature artifact becomes " + - "an artifact creature with power and toughness each equal to its converted mana cost."; + "an artifact creature with power and toughness each equal to its mana value."; } private KarnTheGreatCreatorAnimateEffect(final KarnTheGreatCreatorAnimateEffect effect) { @@ -124,7 +124,7 @@ public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) case PTChangingEffects_7: if (sublayer == SubLayer.SetPT_7b) { - int cmc = artifact.getConvertedManaCost(); + int cmc = artifact.getManaValue(); artifact.getPower().setValue(cmc); artifact.getToughness().setValue(cmc); } diff --git a/Mage.Sets/src/mage/cards/k/KarnsTemporalSundering.java b/Mage.Sets/src/mage/cards/k/KarnsTemporalSundering.java index 5e4bf69457a3..8b7b25423393 100644 --- a/Mage.Sets/src/mage/cards/k/KarnsTemporalSundering.java +++ b/Mage.Sets/src/mage/cards/k/KarnsTemporalSundering.java @@ -33,7 +33,7 @@ public KarnsTemporalSundering(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new KarnsTemporalSunderingEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addTarget(new TargetNonlandPermanent(0, 1, false)); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private KarnsTemporalSundering(final KarnsTemporalSundering card) { diff --git a/Mage.Sets/src/mage/cards/k/KarnsTouch.java b/Mage.Sets/src/mage/cards/k/KarnsTouch.java index 773e7491cb9f..4353daac74ab 100644 --- a/Mage.Sets/src/mage/cards/k/KarnsTouch.java +++ b/Mage.Sets/src/mage/cards/k/KarnsTouch.java @@ -51,7 +51,7 @@ class KarnsTouchEffect extends ContinuousEffectImpl { public KarnsTouchEffect() { super(Duration.EndOfTurn, Outcome.BecomeCreature); - staticText = "Target noncreature artifact becomes an artifact creature with power and toughness each equal to its converted mana cost until end of turn"; + staticText = "Target noncreature artifact becomes an artifact creature with power and toughness each equal to its mana value until end of turn"; } public KarnsTouchEffect(final KarnsTouchEffect effect) { @@ -83,7 +83,7 @@ public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) case PTChangingEffects_7: if (sublayer == SubLayer.SetPT_7b) { - int cmc = artifact.getConvertedManaCost(); + int cmc = artifact.getManaValue(); artifact.getPower().setValue(cmc); artifact.getToughness().setValue(cmc); } diff --git a/Mage.Sets/src/mage/cards/k/KarokWrangler.java b/Mage.Sets/src/mage/cards/k/KarokWrangler.java new file mode 100644 index 000000000000..4cc885c16160 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KarokWrangler.java @@ -0,0 +1,43 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KarokWrangler extends CardImpl { + + public KarokWrangler(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}"); + + this.subtype.add(SubType.ELF); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, put a +1/+1 counter on target creature you control. + Ability ability = new MagecraftAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + private KarokWrangler(final KarokWrangler card) { + super(card); + } + + @Override + public KarokWrangler copy() { + return new KarokWrangler(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KarrthusTyrantOfJund.java b/Mage.Sets/src/mage/cards/k/KarrthusTyrantOfJund.java index 99567018f6f8..a6f9211bdfaa 100644 --- a/Mage.Sets/src/mage/cards/k/KarrthusTyrantOfJund.java +++ b/Mage.Sets/src/mage/cards/k/KarrthusTyrantOfJund.java @@ -18,7 +18,7 @@ import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; diff --git a/Mage.Sets/src/mage/cards/k/KasminaEnigmaSage.java b/Mage.Sets/src/mage/cards/k/KasminaEnigmaSage.java new file mode 100644 index 000000000000..0aac5aa97494 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KasminaEnigmaSage.java @@ -0,0 +1,155 @@ +package mage.cards.k; + +import mage.ApprovingObject; +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.GetXLoyaltyValue; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.common.FilterInstantOrSorceryCard; +import mage.filter.predicate.mageobject.SharesColorPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.QuandrixToken; +import mage.players.Player; +import mage.target.common.TargetCardInLibrary; + +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class KasminaEnigmaSage extends CardImpl { + + public KasminaEnigmaSage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{1}{G}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.KASMINA); + this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(2)); + + // Each other planeswalker you control has the loyalty abilities of Kasmina, Enigma Sage. + this.addAbility(new SimpleStaticAbility(new KasminaEnigmaSageGainAbilitiesEffect())); + + // +2: Scry 1. + this.addAbility(new LoyaltyAbility(new ScryEffect(1), 2)); + + // −X: Create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it. + this.addAbility(new LoyaltyAbility(QuandrixToken.getEffect( + GetXLoyaltyValue.instance, "Put X +1/+1 counters on it" + ))); + + // −8: Search your library for an instant or sorcery card that shares a color with this planeswalker, exile that card, then shuffle. You may cast that card without paying its mana cost. + this.addAbility(new LoyaltyAbility(new KasminaEnigmaSageSearchEffect(), -8)); + } + + private KasminaEnigmaSage(final KasminaEnigmaSage card) { + super(card); + } + + @Override + public KasminaEnigmaSage copy() { + return new KasminaEnigmaSage(this); + } +} + +class KasminaEnigmaSageGainAbilitiesEffect extends ContinuousEffectImpl { + + KasminaEnigmaSageGainAbilitiesEffect() { + super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + staticText = "each other planeswalker you control has the loyalty abilities of {this}"; + } + + private KasminaEnigmaSageGainAbilitiesEffect(final KasminaEnigmaSageGainAbilitiesEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent perm = source.getSourcePermanentIfItStillExists(game); + if (perm == null) { + return true; + } + List loyaltyAbilities = perm + .getAbilities(game) + .stream() + .filter(LoyaltyAbility.class::isInstance) + .collect(Collectors.toList()); + for (Permanent permanent : game.getState().getBattlefield().getActivePermanents( + StaticFilters.FILTER_CONTROLLED_PERMANENT_PLANESWALKER, + source.getControllerId(), source.getSourceId(), game + )) { + if (permanent == null || permanent == perm) { + continue; + } + for (Ability ability : loyaltyAbilities) { + permanent.addAbility(ability, source.getSourceId(), game); + } + } + return true; + } + + @Override + public KasminaEnigmaSageGainAbilitiesEffect copy() { + return new KasminaEnigmaSageGainAbilitiesEffect(this); + } +} + +class KasminaEnigmaSageSearchEffect extends OneShotEffect { + + KasminaEnigmaSageSearchEffect() { + super(Outcome.Benefit); + staticText = "Search your library for an instant or sorcery card that shares a color with this planeswalker, " + + "exile that card, then shuffle. You may cast that card without paying its mana cost."; + } + + private KasminaEnigmaSageSearchEffect(final KasminaEnigmaSageSearchEffect effect) { + super(effect); + } + + @Override + public KasminaEnigmaSageSearchEffect copy() { + return new KasminaEnigmaSageSearchEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent permanent = source.getSourcePermanentOrLKI(game); + if (controller == null || permanent == null) { + return false; + } + FilterCard filter = new FilterInstantOrSorceryCard( + "an instant, or sorcery card which shares a color with " + permanent.getLogName() + ); + filter.add(new SharesColorPredicate(permanent.getColor(game))); + TargetCardInLibrary target = new TargetCardInLibrary(filter); + controller.searchLibrary(target, source, game); + Card card = controller.getLibrary().getCard(target.getFirstTarget(), game); + if (card != null) { + controller.moveCards(card, Zone.EXILED, source, game); + } + controller.shuffleLibrary(source, game); + if (card == null || !controller.chooseUse( + Outcome.PlayForFree, "Cast " + card.getName() + " without paying its mana cost?", source, game + )) { + return true; + } + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); + Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(card, game, true), + game, true, new ApprovingObject(source, game)); + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/k/KavuMauler.java b/Mage.Sets/src/mage/cards/k/KavuMauler.java index 4653cd2d98ea..86b80f5afa13 100644 --- a/Mage.Sets/src/mage/cards/k/KavuMauler.java +++ b/Mage.Sets/src/mage/cards/k/KavuMauler.java @@ -13,7 +13,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.common.FilterAttackingCreature; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/k/KavuMonarch.java b/Mage.Sets/src/mage/cards/k/KavuMonarch.java index cccf4ff75551..05a3a3611244 100644 --- a/Mage.Sets/src/mage/cards/k/KavuMonarch.java +++ b/Mage.Sets/src/mage/cards/k/KavuMonarch.java @@ -16,7 +16,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/k/KayaOrzhovUsurper.java b/Mage.Sets/src/mage/cards/k/KayaOrzhovUsurper.java index dbd1c07d8741..dd8eb03591de 100644 --- a/Mage.Sets/src/mage/cards/k/KayaOrzhovUsurper.java +++ b/Mage.Sets/src/mage/cards/k/KayaOrzhovUsurper.java @@ -10,7 +10,7 @@ import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterNonlandPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.Target; @@ -26,10 +26,10 @@ public final class KayaOrzhovUsurper extends CardImpl { private static final FilterPermanent filter - = new FilterNonlandPermanent("nonland permanent with converted mana cost 1 or less"); + = new FilterNonlandPermanent("nonland permanent with mana value 1 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 2)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 2)); } public KayaOrzhovUsurper(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/k/KazaRoilChaser.java b/Mage.Sets/src/mage/cards/k/KazaRoilChaser.java index 43af56bd2845..faef776560ba 100644 --- a/Mage.Sets/src/mage/cards/k/KazaRoilChaser.java +++ b/Mage.Sets/src/mage/cards/k/KazaRoilChaser.java @@ -19,9 +19,13 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; import mage.util.CardUtil; -import mage.watchers.common.CastSpellLastTurnWatcher; +import mage.watchers.Watcher; +import java.util.HashMap; +import java.util.Map; import java.util.UUID; /** @@ -49,7 +53,9 @@ public KazaRoilChaser(UUID ownerId, CardSetInfo setInfo) { this.addAbility(HasteAbility.getInstance()); // {T}: The next instant or sorcery spell you cast this turn costs {X} less to cast, where X is the number of Wizards you control as this ability resolves. - this.addAbility(new SimpleActivatedAbility(new KazaRoilChaserEffect(), new TapSourceCost()).addHint(hint)); + this.addAbility(new SimpleActivatedAbility( + new KazaRoilChaserEffect(), new TapSourceCost() + ).addHint(hint), new KazaRoilChaserWatcher()); } private KazaRoilChaser(final KazaRoilChaser card) { @@ -64,9 +70,9 @@ public KazaRoilChaser copy() { class KazaRoilChaserEffect extends CostModificationEffectImpl { + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.WIZARD); private int spellsCast; private int wizardCount = 0; - private static final FilterPermanent filter = new FilterControlledPermanent(SubType.WIZARD); KazaRoilChaserEffect() { super(Duration.EndOfTurn, Outcome.Benefit, CostModificationType.REDUCE_COST); @@ -83,9 +89,9 @@ private KazaRoilChaserEffect(final KazaRoilChaserEffect effect) { @Override public void init(Ability source, Game game) { super.init(source, game); - CastSpellLastTurnWatcher watcher = game.getState().getWatcher(CastSpellLastTurnWatcher.class); + KazaRoilChaserWatcher watcher = game.getState().getWatcher(KazaRoilChaserWatcher.class); if (watcher != null) { - spellsCast = watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(source.getControllerId()); + spellsCast = watcher.getCount(source.getControllerId()); } wizardCount = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game); } @@ -98,11 +104,11 @@ public boolean apply(Game game, Ability source, Ability abilityToModify) { @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { - CastSpellLastTurnWatcher watcher = game.getState().getWatcher(CastSpellLastTurnWatcher.class); + KazaRoilChaserWatcher watcher = game.getState().getWatcher(KazaRoilChaserWatcher.class); if (watcher == null) { return false; } - if (watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(source.getControllerId()) > spellsCast) { + if (watcher.getCount(source.getControllerId()) > spellsCast) { discard(); // only one use return false; } @@ -119,3 +125,33 @@ public KazaRoilChaserEffect copy() { return new KazaRoilChaserEffect(this); } } + +class KazaRoilChaserWatcher extends Watcher { + + private final Map playerMap = new HashMap<>(); + + KazaRoilChaserWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.SPELL_CAST) { + return; + } + Spell spell = game.getSpell(event.getSourceId()); + if (spell != null && spell.isInstantOrSorcery()) { + playerMap.compute(event.getPlayerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + } + } + + @Override + public void reset() { + super.reset(); + playerMap.clear(); + } + + int getCount(UUID playerId) { + return playerMap.getOrDefault(playerId, 0); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KazarovSengirPureblood.java b/Mage.Sets/src/mage/cards/k/KazarovSengirPureblood.java index 54c081b77994..ac9021f9dff9 100644 --- a/Mage.Sets/src/mage/cards/k/KazarovSengirPureblood.java +++ b/Mage.Sets/src/mage/cards/k/KazarovSengirPureblood.java @@ -75,22 +75,15 @@ public KazarovSengirPurebloodTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event == null) { - return false; - } Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); - if (permanent == null) { - return false; - } - if (permanent.isControlledBy(this.getControllerId())) { - return false; - } - return true; + return permanent!=null + && permanent.isCreature() + && game.getOpponents(permanent.getControllerId()).contains(this.getControllerId()); } @Override diff --git a/Mage.Sets/src/mage/cards/k/KederektLeviathan.java b/Mage.Sets/src/mage/cards/k/KederektLeviathan.java index 0f2effbb1287..a762841d9c64 100644 --- a/Mage.Sets/src/mage/cards/k/KederektLeviathan.java +++ b/Mage.Sets/src/mage/cards/k/KederektLeviathan.java @@ -12,7 +12,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterNonlandPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/k/KedissEmberclawFamiliar.java b/Mage.Sets/src/mage/cards/k/KedissEmberclawFamiliar.java index 36e52261e723..6153b7dfeaaa 100644 --- a/Mage.Sets/src/mage/cards/k/KedissEmberclawFamiliar.java +++ b/Mage.Sets/src/mage/cards/k/KedissEmberclawFamiliar.java @@ -10,7 +10,7 @@ import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.CommanderPredicate; +import mage.filter.predicate.mageobject.CommanderPredicate; import mage.game.Game; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/k/KeenDuelist.java b/Mage.Sets/src/mage/cards/k/KeenDuelist.java new file mode 100644 index 000000000000..07a4e28ee4f3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KeenDuelist.java @@ -0,0 +1,86 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.*; +import mage.constants.*; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KeenDuelist extends CardImpl { + + public KeenDuelist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // At the beginning of your upkeep, you and target opponent each reveal the top card of your library. You each lose life equal to the mana value of the card revealed by the other player. You each put the card you revealed into your hand. + Ability ability = new BeginningOfUpkeepTriggeredAbility( + new KeenDuelistEffect(), TargetController.YOU, false + ); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + } + + private KeenDuelist(final KeenDuelist card) { + super(card); + } + + @Override + public KeenDuelist copy() { + return new KeenDuelist(this); + } +} + +class KeenDuelistEffect extends OneShotEffect { + + KeenDuelistEffect() { + super(Outcome.Benefit); + staticText = "you and target opponent each reveal the top card of your library. " + + "You each lose life equal to the mana value of the card revealed by the other player. " + + "You each put the card you revealed into your hand"; + } + + private KeenDuelistEffect(final KeenDuelistEffect effect) { + super(effect); + } + + @Override + public KeenDuelistEffect copy() { + return new KeenDuelistEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player opponent = game.getPlayer(source.getFirstTarget()); + if (controller == null || opponent == null) { + return false; + } + Cards cards = new CardsImpl(); + Card myCard = controller.getLibrary().getFromTop(game); + cards.add(myCard); + Card theirCard = opponent.getLibrary().getFromTop(game); + cards.add(theirCard); + controller.revealCards(source, cards, game); + if (theirCard != null && theirCard.getManaValue() < 1) { + controller.loseLife(theirCard.getManaValue(), game, source, false); + } + if (myCard != null && myCard.getManaValue() < 1) { + opponent.loseLife(myCard.getManaValue(), game, source, false); + } + controller.moveCards(cards, Zone.HAND, source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/k/KeepSafe.java b/Mage.Sets/src/mage/cards/k/KeepSafe.java index 997a355c03c9..a7a676677e64 100644 --- a/Mage.Sets/src/mage/cards/k/KeepSafe.java +++ b/Mage.Sets/src/mage/cards/k/KeepSafe.java @@ -7,7 +7,7 @@ import mage.constants.CardType; import mage.filter.FilterSpell; import mage.filter.StaticFilters; -import mage.filter.predicate.other.TargetsPermanentPredicate; +import mage.filter.predicate.mageobject.TargetsPermanentPredicate; import mage.target.TargetSpell; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/k/KeeperOfTheAccord.java b/Mage.Sets/src/mage/cards/k/KeeperOfTheAccord.java index 4f57edbae2c5..526ee1e6ae7d 100644 --- a/Mage.Sets/src/mage/cards/k/KeeperOfTheAccord.java +++ b/Mage.Sets/src/mage/cards/k/KeeperOfTheAccord.java @@ -57,7 +57,7 @@ public KeeperOfTheAccord(UUID ownerId, CardSetInfo setInfo) { ), TargetController.OPPONENT, true), KeeperOfTheAccordCondition.LANDS, "At the beginning of each opponent's end step, " + "if that player controls more lands than you, you may search your library for a basic Plains card, " + - "put it onto the battlefield tapped, then shuffle your library." + "put it onto the battlefield tapped, then shuffle." )); } diff --git a/Mage.Sets/src/mage/cards/k/KeeperOfTheBeasts.java b/Mage.Sets/src/mage/cards/k/KeeperOfTheBeasts.java index 927a50decb77..7451ab835487 100644 --- a/Mage.Sets/src/mage/cards/k/KeeperOfTheBeasts.java +++ b/Mage.Sets/src/mage/cards/k/KeeperOfTheBeasts.java @@ -36,7 +36,7 @@ public KeeperOfTheBeasts(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(2); // {G}, {tap}: Choose target opponent who controlled more creatures than you did as you activated this ability. Put a 2/2 green Beast creature token onto the battlefield. - Ability ability = new SimpleActivatedAbility(new CreateTokenEffect(new BeastToken4()).setText("Choose target opponent who controlled more creatures than you did as you activated this ability. Put a 2/2 green Beast creature token onto the battlefield."), + Ability ability = new SimpleActivatedAbility(new CreateTokenEffect(new BeastToken4()).setText("Choose target opponent who controlled more creatures than you did as you activated this ability. Create a 2/2 green Beast creature token."), new ManaCostsImpl("{G}")); ability.addCost(new TapSourceCost()); ability.addTarget(new KeeperOfTheBeastsTarget()); diff --git a/Mage.Sets/src/mage/cards/k/KeeperOfTheLens.java b/Mage.Sets/src/mage/cards/k/KeeperOfTheLens.java index 8407018afd74..60812e38b541 100644 --- a/Mage.Sets/src/mage/cards/k/KeeperOfTheLens.java +++ b/Mage.Sets/src/mage/cards/k/KeeperOfTheLens.java @@ -13,7 +13,7 @@ import mage.cards.CardsImpl; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.other.FaceDownPredicate; +import mage.filter.predicate.card.FaceDownPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/k/KelethSunmaneFamiliar.java b/Mage.Sets/src/mage/cards/k/KelethSunmaneFamiliar.java index 920490e003ad..390509351df2 100644 --- a/Mage.Sets/src/mage/cards/k/KelethSunmaneFamiliar.java +++ b/Mage.Sets/src/mage/cards/k/KelethSunmaneFamiliar.java @@ -11,7 +11,7 @@ import mage.constants.SuperType; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.CommanderPredicate; +import mage.filter.predicate.mageobject.CommanderPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/k/KelpieGuide.java b/Mage.Sets/src/mage/cards/k/KelpieGuide.java new file mode 100644 index 000000000000..71dc527d5ee4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KelpieGuide.java @@ -0,0 +1,66 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.hint.common.LandsYouControlHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledLandPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KelpieGuide extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledLandPermanent("you control eight or more lands"); + private static final FilterPermanent filter2 + = new FilterControlledPermanent("another target permanent you control"); + private static final Condition condition + = new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 7); + + public KelpieGuide(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.BEAST); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // {T}: Untap another target permanent you control. + Ability ability = new SimpleActivatedAbility(new UntapTargetEffect(), new TapSourceCost()); + ability.addTarget(new TargetPermanent(filter2)); + this.addAbility(ability); + + // {T}: Tap target permanent. Activate only if you control eight or more lands. + ability = new ActivateIfConditionActivatedAbility( + Zone.BATTLEFIELD, new TapTargetEffect(), new TapSourceCost(), condition + ); + ability.addTarget(new TargetPermanent()); + this.addAbility(ability.addHint(LandsYouControlHint.instance)); + } + + private KelpieGuide(final KelpieGuide card) { + super(card); + } + + @Override + public KelpieGuide copy() { + return new KelpieGuide(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KentaroTheSmilingCat.java b/Mage.Sets/src/mage/cards/k/KentaroTheSmilingCat.java index c54215c25f8f..3b73053c011e 100644 --- a/Mage.Sets/src/mage/cards/k/KentaroTheSmilingCat.java +++ b/Mage.Sets/src/mage/cards/k/KentaroTheSmilingCat.java @@ -61,11 +61,11 @@ class KentaroTheSmilingCatCastingEffect extends ContinuousEffectImpl { } private final AlternativeCostSourceAbility alternativeCastingCostAbility = new AlternativeCostSourceAbility( - SourceIsSpellCondition.instance, null, filterSamurai, true, new ColorlessConvertedManaCost()); + SourceIsSpellCondition.instance, null, filterSamurai, true, new ColorlessManaValue()); public KentaroTheSmilingCatCastingEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "You may pay {X} rather than pay the mana cost for Samurai spells you cast, where X is that spell's converted mana cost"; + staticText = "You may pay {X} rather than pay the mana cost for Samurai spells you cast, where X is that spell's mana value"; } public KentaroTheSmilingCatCastingEffect(final KentaroTheSmilingCatCastingEffect effect) { @@ -104,11 +104,11 @@ public boolean hasLayer(Layer layer) { } } -class ColorlessConvertedManaCost implements DynamicCost { +class ColorlessManaValue implements DynamicCost { @Override public Cost getCost(Ability ability, Game game) { - return new GenericManaCost(ability.getManaCosts().convertedManaCost()); + return new GenericManaCost(ability.getManaCosts().manaValue()); } @Override diff --git a/Mage.Sets/src/mage/cards/k/KerugaTheMacrosage.java b/Mage.Sets/src/mage/cards/k/KerugaTheMacrosage.java index a9373bc5b706..0abde82c078a 100644 --- a/Mage.Sets/src/mage/cards/k/KerugaTheMacrosage.java +++ b/Mage.Sets/src/mage/cards/k/KerugaTheMacrosage.java @@ -14,8 +14,8 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.Set; import java.util.UUID; @@ -25,11 +25,11 @@ */ public final class KerugaTheMacrosage extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("other permanent you control with converted mana cost 3 or greater"); + private static final FilterControlledPermanent filter = new FilterControlledPermanent("other permanent you control with mana value 3 or greater"); static { filter.add(AnotherPredicate.instance); - filter.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 2)); + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 2)); } public KerugaTheMacrosage(UUID ownerId, CardSetInfo setInfo) { @@ -62,11 +62,11 @@ enum KerugaCondition implements CompanionCondition { @Override public String getRule() { - return "Your starting deck contains only cards with converted mana cost 3 or greater and land cards."; + return "Your starting deck contains only cards with mana value 3 or greater and land cards."; } @Override public boolean isLegal(Set deck, int startingSize) { - return deck.stream().allMatch(card -> card.isLand() || card.getConvertedManaCost() >= 3); + return deck.stream().allMatch(card -> card.isLand() || card.getManaValue() >= 3); } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/k/KeskitTheFleshSculptor.java b/Mage.Sets/src/mage/cards/k/KeskitTheFleshSculptor.java index 6eaafcaf4a13..8dfd0ab4cffa 100644 --- a/Mage.Sets/src/mage/cards/k/KeskitTheFleshSculptor.java +++ b/Mage.Sets/src/mage/cards/k/KeskitTheFleshSculptor.java @@ -17,7 +17,7 @@ import mage.filter.StaticFilters; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/k/KessigForgemaster.java b/Mage.Sets/src/mage/cards/k/KessigForgemaster.java index 3528facae03a..110c958e848f 100644 --- a/Mage.Sets/src/mage/cards/k/KessigForgemaster.java +++ b/Mage.Sets/src/mage/cards/k/KessigForgemaster.java @@ -1,24 +1,18 @@ - package mage.cards.k; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; -import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; import mage.filter.StaticFilters; +import java.util.UUID; + /** * @author LevelX2 */ @@ -41,8 +35,7 @@ public KessigForgemaster(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each upkeep, if no spells were cast last turn, transform Kessig Forgemaster. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } diff --git a/Mage.Sets/src/mage/cards/k/KhalniGem.java b/Mage.Sets/src/mage/cards/k/KhalniGem.java index e6e789aca2bb..d6d6138991e9 100644 --- a/Mage.Sets/src/mage/cards/k/KhalniGem.java +++ b/Mage.Sets/src/mage/cards/k/KhalniGem.java @@ -1,8 +1,5 @@ - - package mage.cards.k; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.costs.common.TapSourceCost; @@ -11,38 +8,35 @@ import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.common.FilterControlledLandPermanent; -import mage.filter.common.FilterControlledPermanent; +import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.target.Target; -import mage.target.common.TargetControlledPermanent; +import mage.players.Player; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author maurer.it_at_gmail.com */ public final class KhalniGem extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledLandPermanent(); - - public KhalniGem (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); + public KhalniGem(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); // When Khalni Gem enters the battlefield, return two lands you control to their owner's hand. - Ability etbAbility = new EntersBattlefieldTriggeredAbility(new KhalniGemReturnToHandTargetEffect()); - Target target = new TargetControlledPermanent(2, 2, filter, false); - etbAbility.addTarget(target); - this.addAbility(etbAbility); + this.addAbility(new EntersBattlefieldTriggeredAbility(new KhalniGemReturnToHandTargetEffect())); + // {tap}: Add two mana of any one color. - SimpleManaAbility ability = new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaOfAnyColorEffect(2), new TapSourceCost()); - this.addAbility(ability); + this.addAbility(new SimpleManaAbility( + Zone.BATTLEFIELD, new AddManaOfAnyColorEffect(2), new TapSourceCost() + )); } - public KhalniGem (final KhalniGem card) { + private KhalniGem(final KhalniGem card) { super(card); } @@ -50,36 +44,41 @@ public KhalniGem (final KhalniGem card) { public KhalniGem copy() { return new KhalniGem(this); } - } class KhalniGemReturnToHandTargetEffect extends OneShotEffect { private static final String effectText = "return two lands you control to their owner's hand"; - KhalniGemReturnToHandTargetEffect ( ) { + KhalniGemReturnToHandTargetEffect() { super(Outcome.ReturnToHand); staticText = effectText; } - KhalniGemReturnToHandTargetEffect ( KhalniGemReturnToHandTargetEffect effect ) { + private KhalniGemReturnToHandTargetEffect(KhalniGemReturnToHandTargetEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { - for ( UUID target : targetPointer.getTargets(game, source) ) { - Permanent permanent = game.getPermanent(target); - if ( permanent != null ) { - permanent.moveToZone(Zone.HAND, source, game, true); - } + Player player = game.getPlayer(source.getControllerId()); + int landCount = game.getBattlefield().count( + StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, + source.getSourceId(), source.getControllerId(), game + ); + if (player == null || landCount < 1) { + return false; } - return true; + TargetPermanent target = new TargetPermanent( + Math.min(landCount, 2), StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND + ); + target.setNotTarget(true); + player.choose(outcome, target, source.getSourceId(), game); + return player.moveCards(new CardsImpl(target.getTargets()), Zone.HAND, source, game); } @Override public KhalniGemReturnToHandTargetEffect copy() { return new KhalniGemReturnToHandTargetEffect(this); } - } diff --git a/Mage.Sets/src/mage/cards/k/KherKeep.java b/Mage.Sets/src/mage/cards/k/KherKeep.java index b5cc8ce67c5d..70b8597d9b1b 100644 --- a/Mage.Sets/src/mage/cards/k/KherKeep.java +++ b/Mage.Sets/src/mage/cards/k/KherKeep.java @@ -1,4 +1,3 @@ - package mage.cards.k; import java.util.UUID; @@ -25,9 +24,10 @@ public KherKeep(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); addSuperType(SuperType.LEGENDARY); - // {tap}: Add {C}. + // {T}: Add {C}. this.addAbility(new ColorlessManaAbility()); - // {1}{R}, {tap}: Create a 0/1 red Kobold creature token named Kobolds of Kher Keep. + + // {1}{R}, {T}: Create a 0/1 red Kobold creature token named Kobolds of Kher Keep. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new KherKeepKoboldToken()), new ManaCostsImpl("{1}{R}")); ability.addCost(new TapSourceCost()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/k/KiAdiMundi.java b/Mage.Sets/src/mage/cards/k/KiAdiMundi.java index f65fc8fae2bd..4c57b9b0bd67 100644 --- a/Mage.Sets/src/mage/cards/k/KiAdiMundi.java +++ b/Mage.Sets/src/mage/cards/k/KiAdiMundi.java @@ -15,7 +15,7 @@ import mage.constants.TargetController; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/k/KianneDeanOfSubstance.java b/Mage.Sets/src/mage/cards/k/KianneDeanOfSubstance.java new file mode 100644 index 000000000000..4517e6613687 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KianneDeanOfSubstance.java @@ -0,0 +1,226 @@ +package mage.cards.k; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.hint.Hint; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.*; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterCard; +import mage.filter.common.FilterOwnedCard; +import mage.game.Game; +import mage.game.permanent.token.QuandrixToken; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInExile; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class KianneDeanOfSubstance extends ModalDoubleFacesCard { + + public KianneDeanOfSubstance(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.ELF, SubType.DRUID}, "{2}{G}", + "Imbraham, Dean of Theory", new CardType[]{CardType.CREATURE}, new SubType[]{SubType.BIRD, SubType.WIZARD}, "{2}{U}{U}"); + + // 1. + // Kianne, Dean of Substance + // Legendary Creature - Elf Druid + this.getLeftHalfCard().addSuperType(SuperType.LEGENDARY); + this.getLeftHalfCard().setPT(2, 2); + + // {T}: Exile the top card of your library. If it's a land card, put it into your hand. Otherwise, put a study counter on it. + this.getLeftHalfCard().addAbility(new SimpleActivatedAbility( + new KianneDeanOfSubstanceExileEffect(), new TapSourceCost() + )); + + // {4}{G}: Create a 0/0 green and blue Fractal creature token. Put a +1/+1 counter on it for each different mana value among nonland cards you own in exile with study counters on them. + this.getLeftHalfCard().addAbility(new SimpleActivatedAbility(QuandrixToken.getEffect( + KianneDeanOfSubstanceValue.instance, "Put a +1/+1 counter on it for each different mana value " + + "among nonland cards you own in exile with study counters on them" + ), new ManaCostsImpl("{4}{G}")).addHint(KianneDeanOfSubstanceHint.instance)); + + // 2. + // Imbraham, Dean of Theory + // Legendary Creature - Bird Wizard + this.getRightHalfCard().addSuperType(SuperType.LEGENDARY); + this.getRightHalfCard().setPT(3, 3); + + // Flying + this.getRightHalfCard().addAbility(FlyingAbility.getInstance()); + + // {X}{U}{U}, {T}: Exile the top X cards of your library and put a study counter on each of them. Then you may put a card you own in exile with a study counter on it into your hand. + Ability ability = new SimpleActivatedAbility( + new ImbrahamDeanOfTheoryEffect(), new ManaCostsImpl("{X}{U}{U}") + ); + ability.addCost(new TapSourceCost()); + this.getRightHalfCard().addAbility(ability); + } + + private KianneDeanOfSubstance(final KianneDeanOfSubstance card) { + super(card); + } + + @Override + public KianneDeanOfSubstance copy() { + return new KianneDeanOfSubstance(this); + } +} + +class KianneDeanOfSubstanceExileEffect extends OneShotEffect { + + KianneDeanOfSubstanceExileEffect() { + super(Outcome.Benefit); + staticText = "Exile the top card of your library. If it's a land card, " + + "put it into your hand. Otherwise, put a study counter on it."; + } + + private KianneDeanOfSubstanceExileEffect(final KianneDeanOfSubstanceExileEffect effect) { + super(effect); + } + + @Override + public KianneDeanOfSubstanceExileEffect copy() { + return new KianneDeanOfSubstanceExileEffect(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.moveCards(card, Zone.EXILED, source, game); + if (card.isLand()) { + return player.moveCards(card, Zone.HAND, source, game); + } + return card.getMainCard().addCounters( + CounterType.STUDY.createInstance(), + player.getId(), source, game + ); + } +} + +enum KianneDeanOfSubstanceValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return game + .getExile() + .getAllCards(game) + .stream() + .filter(Objects::nonNull) + .filter(card -> card.isOwnedBy(sourceAbility.getControllerId())) + .filter(card -> card.getCounters(game).containsKey(CounterType.STUDY)) + .map(MageObject::getManaValue) + .distinct() + .mapToInt(x -> 1) + .sum(); + } + + @Override + public KianneDeanOfSubstanceValue copy() { + return instance; + } + + @Override + public String getMessage() { + return ""; + } +} + +enum KianneDeanOfSubstanceHint implements Hint { + instance; + + @Override + public String getText(Game game, Ability ability) { + List values = game.getExile() + .getAllCards(game) + .stream() + .filter(Objects::nonNull) + .filter(card -> card.isOwnedBy(ability.getControllerId())) + .filter(card -> card.getCounters(game).containsKey(CounterType.STUDY)) + .map(MageObject::getManaValue) + .distinct() + .sorted() + .collect(Collectors.toList()); + String message = "" + values.size(); + if (values.size() > 0) { + message += " ("; + message += values.stream().map(i -> "" + i).reduce((a, b) -> a + ", " + b).orElse(""); + message += ')'; + } + return "Mana values of cards exiled with study counters: " + message; + } + + @Override + public KianneDeanOfSubstanceHint copy() { + return instance; + } +} + +class ImbrahamDeanOfTheoryEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterOwnedCard("card you own in exile with a study counter on it"); + + static { + filter.add(CounterType.STUDY.getPredicate()); + } + + ImbrahamDeanOfTheoryEffect() { + super(Outcome.DrawCard); + staticText = "Exile the top X cards of your library and put a study counter on each of them. " + + "Then you may put a card you own in exile with a study counter on it into your hand."; + } + + private ImbrahamDeanOfTheoryEffect(final ImbrahamDeanOfTheoryEffect effect) { + super(effect); + } + + @Override + public ImbrahamDeanOfTheoryEffect copy() { + return new ImbrahamDeanOfTheoryEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, source.getManaCostsToPay().getX())); + player.moveCards(cards, Zone.EXILED, source, game); + for (Card card : cards.getCards(game)) { + if (card == null) { + continue; + } + card.addCounters(CounterType.STUDY.createInstance(), source.getControllerId(), source, game); + } + TargetCard targetCard = new TargetCardInExile(0, 1, filter, null); + targetCard.setNotTarget(true); + player.choose(outcome, targetCard, source.getSourceId(), game); + Card card = game.getCard(targetCard.getFirstTarget()); + if (card != null) { + player.moveCards(card, Zone.HAND, source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/k/KillSuitCultist.java b/Mage.Sets/src/mage/cards/k/KillSuitCultist.java index 4a3c27b84c1d..00dfe7fd272c 100644 --- a/Mage.Sets/src/mage/cards/k/KillSuitCultist.java +++ b/Mage.Sets/src/mage/cards/k/KillSuitCultist.java @@ -72,7 +72,7 @@ public KillSuitCultistEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE; + return event.getType() == EventType.DAMAGE_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/k/KillianInkDuelist.java b/Mage.Sets/src/mage/cards/k/KillianInkDuelist.java new file mode 100644 index 000000000000..ae4c83cc381d --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KillianInkDuelist.java @@ -0,0 +1,87 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.util.CardUtil; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KillianInkDuelist extends CardImpl { + + public KillianInkDuelist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Menace + this.addAbility(new MenaceAbility()); + + // Spells you cast that target a creature cost {2} less to cast. + this.addAbility(new SimpleStaticAbility(new KillianInkDuelistEffect())); + } + + private KillianInkDuelist(final KillianInkDuelist card) { + super(card); + } + + @Override + public KillianInkDuelist copy() { + return new KillianInkDuelist(this); + } +} + +class KillianInkDuelistEffect extends CostModificationEffectImpl { + + KillianInkDuelistEffect() { + super(Duration.WhileOnBattlefield, Outcome.Neutral, CostModificationType.REDUCE_COST); + staticText = "spells you cast that target a creature cost {2} less to cast"; + } + + private KillianInkDuelistEffect(KillianInkDuelistEffect effect) { + super(effect); + } + + @Override + public KillianInkDuelistEffect copy() { + return new KillianInkDuelistEffect(this); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + CardUtil.reduceCost(abilityToModify, 2); + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + return abilityToModify instanceof SpellAbility + && abilityToModify.isControlledBy(source.getControllerId()) + && CardUtil + .getAllSelectedTargets(abilityToModify, game) + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .anyMatch(MageObject::isCreature); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KindleTheCarnage.java b/Mage.Sets/src/mage/cards/k/KindleTheCarnage.java index 814b1ea0d7b8..49ae8cd77dc1 100644 --- a/Mage.Sets/src/mage/cards/k/KindleTheCarnage.java +++ b/Mage.Sets/src/mage/cards/k/KindleTheCarnage.java @@ -42,7 +42,7 @@ class KindleTheCarnageEffect extends OneShotEffect { public KindleTheCarnageEffect() { super(Outcome.AIDontUseIt); - this.staticText = "Discard a card at random. If you do, {this} deals damage equal to that card's converted mana cost to each creature. You may repeat this process any number of times"; + this.staticText = "Discard a card at random. If you do, {this} deals damage equal to that card's mana value to each creature. You may repeat this process any number of times"; } public KindleTheCarnageEffect(final KindleTheCarnageEffect effect) { @@ -64,7 +64,7 @@ public boolean apply(Game game, Ability source) { && controller.chooseUse(Outcome.AIDontUseIt, "Discard a card randomly from your hand?", source, game)) { Card discardedCard = controller.discardOne(true, false, source, game); if (discardedCard != null) { - new DamageAllEffect(discardedCard.getConvertedManaCost(), new FilterCreaturePermanent()).apply(game, source); + new DamageAllEffect(discardedCard.getManaValue(), new FilterCreaturePermanent()).apply(game, source); } } return true; diff --git a/Mage.Sets/src/mage/cards/k/KindlyStranger.java b/Mage.Sets/src/mage/cards/k/KindlyStranger.java index bd4e98cc9608..0b33ba3ea891 100644 --- a/Mage.Sets/src/mage/cards/k/KindlyStranger.java +++ b/Mage.Sets/src/mage/cards/k/KindlyStranger.java @@ -1,7 +1,5 @@ package mage.cards.k; -import java.util.UUID; - import mage.MageInt; import mage.abilities.condition.common.DeliriumCondition; import mage.abilities.costs.mana.ManaCostsImpl; @@ -15,6 +13,8 @@ import mage.constants.SubType; import mage.constants.Zone; +import java.util.UUID; + /** * @author fireshoes */ @@ -31,9 +31,11 @@ public KindlyStranger(UUID ownerId, CardSetInfo setInfo) { // Delirium — {2}{B}: Transform Kindly Stranger. Activate this ability only if there are four or more card types among cards in your graveyard. this.addAbility(new TransformAbility()); - this.addAbility(new ConditionalActivatedAbility(Zone.BATTLEFIELD, - new TransformSourceEffect(true), new ManaCostsImpl<>("{2}{B}"), DeliriumCondition.instance) - .addHint(DeliriumHint.instance)); + this.addAbility(new ConditionalActivatedAbility( + Zone.BATTLEFIELD, new TransformSourceEffect(true), new ManaCostsImpl<>("{2}{B}"), + DeliriumCondition.instance, "Delirium — {2}{B}: Transform {this}. " + + "Activate this ability only if there are four or more card types among cards in your graveyard." + ).addHint(DeliriumHint.instance)); } private KindlyStranger(final KindlyStranger card) { diff --git a/Mage.Sets/src/mage/cards/k/KineticAugur.java b/Mage.Sets/src/mage/cards/k/KineticAugur.java index 009a4ce500d5..ded12e23c6b6 100644 --- a/Mage.Sets/src/mage/cards/k/KineticAugur.java +++ b/Mage.Sets/src/mage/cards/k/KineticAugur.java @@ -1,24 +1,21 @@ package mage.cards.k; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.SetPowerSourceEffect; +import mage.abilities.effects.common.discard.DiscardAndDrawThatManyEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.CardsImpl; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.StaticFilters; import mage.filter.common.FilterInstantOrSorceryCard; -import mage.game.Game; -import mage.players.Player; -import mage.target.common.TargetDiscard; import java.util.UUID; @@ -45,7 +42,7 @@ public KineticAugur(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerSourceEffect(xValue, Duration.EndOfGame))); // When Kinetic Augur enters the battlefield, discard up to two cards, then draw that many cards. - this.addAbility(new EntersBattlefieldTriggeredAbility(new KineticAugurEffect())); + this.addAbility(new EntersBattlefieldTriggeredAbility(new DiscardAndDrawThatManyEffect(2))); } private KineticAugur(final KineticAugur card) { @@ -57,35 +54,3 @@ public KineticAugur copy() { return new KineticAugur(this); } } - -class KineticAugurEffect extends OneShotEffect { - - KineticAugurEffect() { - super(Outcome.Benefit); - staticText = "discard up to two cards, then draw that many cards"; - } - - private KineticAugurEffect(final KineticAugurEffect effect) { - super(effect); - } - - @Override - public KineticAugurEffect copy() { - return new KineticAugurEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player == null || player.getHand().isEmpty()) { - return false; - } - TargetDiscard target = new TargetDiscard(0, 2, StaticFilters.FILTER_CARD, player.getId()); - player.choose(Outcome.AIDontUseIt, player.getHand(), target, game); - int discarded = player.discard(new CardsImpl(target.getTargets()), false, source, game).size(); - if (discarded > 0) { - player.drawCards(discarded, source, game); - } - return true; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/k/KingHaraldsRevenge.java b/Mage.Sets/src/mage/cards/k/KingHaraldsRevenge.java index e69e6ff76db7..135bedd7efaf 100644 --- a/Mage.Sets/src/mage/cards/k/KingHaraldsRevenge.java +++ b/Mage.Sets/src/mage/cards/k/KingHaraldsRevenge.java @@ -2,7 +2,7 @@ import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.common.combat.MustBeBlockedByAllTargetEffect; +import mage.abilities.effects.common.combat.MustBeBlockedByAtLeastOneTargetEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.TrampleAbility; @@ -33,7 +33,7 @@ public KingHaraldsRevenge(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new GainAbilityTargetEffect( TrampleAbility.getInstance(), Duration.EndOfTurn ).setText("and gains trample.")); - this.getSpellAbility().addEffect(new MustBeBlockedByAllTargetEffect(Duration.EndOfTurn) + this.getSpellAbility().addEffect(new MustBeBlockedByAtLeastOneTargetEffect(Duration.EndOfTurn) .setText("It must be blocked this turn if able")); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/cards/k/KingNarfisBetrayal.java b/Mage.Sets/src/mage/cards/k/KingNarfisBetrayal.java index c933c8537aba..14b44b51e164 100644 --- a/Mage.Sets/src/mage/cards/k/KingNarfisBetrayal.java +++ b/Mage.Sets/src/mage/cards/k/KingNarfisBetrayal.java @@ -146,7 +146,7 @@ public boolean apply(Game game, Ability source) { ExileZone zone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); if (zone != null) { for (Card card : zone.getCards(game)) { - CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.EndOfTurn); + CardUtil.makeCardPlayable(game, source, card, Duration.EndOfTurn, true); } } diff --git a/Mage.Sets/src/mage/cards/k/KiorasFollower.java b/Mage.Sets/src/mage/cards/k/KiorasFollower.java index 3125ec8f1836..b87413fab6c5 100644 --- a/Mage.Sets/src/mage/cards/k/KiorasFollower.java +++ b/Mage.Sets/src/mage/cards/k/KiorasFollower.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; /** diff --git a/Mage.Sets/src/mage/cards/k/KitesailSkirmisher.java b/Mage.Sets/src/mage/cards/k/KitesailSkirmisher.java index 22d7e11b0b05..38e8c6a1767b 100644 --- a/Mage.Sets/src/mage/cards/k/KitesailSkirmisher.java +++ b/Mage.Sets/src/mage/cards/k/KitesailSkirmisher.java @@ -18,7 +18,7 @@ import mage.filter.common.FilterAttackingCreature; import mage.filter.predicate.ObjectSourcePlayer; import mage.filter.predicate.ObjectSourcePlayerPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.target.TargetPermanent; diff --git a/Mage.Sets/src/mage/cards/k/KithkinArmor.java b/Mage.Sets/src/mage/cards/k/KithkinArmor.java index ababcbe64172..8dd7a95f1bc4 100644 --- a/Mage.Sets/src/mage/cards/k/KithkinArmor.java +++ b/Mage.Sets/src/mage/cards/k/KithkinArmor.java @@ -2,6 +2,7 @@ import java.util.UUID; import mage.constants.SubType; +import mage.game.events.DamageEvent; import mage.target.common.TargetCreaturePermanent; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -20,7 +21,6 @@ import mage.constants.Duration; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamageCreatureEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.TargetSource; @@ -155,11 +155,11 @@ public KithkinArmorPreventionEffect copy() { @Override public boolean applies(GameEvent event, Ability source, Game game) { if (super.applies(event, source, game) - && event instanceof DamageCreatureEvent + && event instanceof DamageEvent && event.getAmount() > 0 && !this.used) { UUID enchantedCreatureId = (UUID) game.getState().getValue(source.getSourceId().toString() + "attachedToPermanent"); - DamageCreatureEvent damageEvent = (DamageCreatureEvent) event; + DamageEvent damageEvent = (DamageEvent) event; if (enchantedCreatureId != null && event.getTargetId().equals(enchantedCreatureId) && damageEvent.getSourceId().equals(source.getFirstTarget())) { diff --git a/Mage.Sets/src/mage/cards/k/KitsuneMystic.java b/Mage.Sets/src/mage/cards/k/KitsuneMystic.java index 22fa53abd825..000f8e5c6777 100644 --- a/Mage.Sets/src/mage/cards/k/KitsuneMystic.java +++ b/Mage.Sets/src/mage/cards/k/KitsuneMystic.java @@ -15,7 +15,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterEnchantmentPermanent; -import mage.filter.predicate.mageobject.AttachmentAttachedToCardTypePredicate; +import mage.filter.predicate.permanent.AttachmentAttachedToCardTypePredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/k/KiyomaroFirstToStand.java b/Mage.Sets/src/mage/cards/k/KiyomaroFirstToStand.java index 5e4e7319fda8..cf00b5ed5934 100644 --- a/Mage.Sets/src/mage/cards/k/KiyomaroFirstToStand.java +++ b/Mage.Sets/src/mage/cards/k/KiyomaroFirstToStand.java @@ -89,8 +89,7 @@ public KiyomaroFirstToStandDealsDamageTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || event.getType() == GameEvent.EventType.DAMAGED_CREATURE - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + || event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/k/KnightExemplar.java b/Mage.Sets/src/mage/cards/k/KnightExemplar.java index ab0575cd5fe0..bd08df503407 100644 --- a/Mage.Sets/src/mage/cards/k/KnightExemplar.java +++ b/Mage.Sets/src/mage/cards/k/KnightExemplar.java @@ -14,7 +14,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/k/KnightOfTheLastBreath.java b/Mage.Sets/src/mage/cards/k/KnightOfTheLastBreath.java index 5ac5c3ea80d4..78d7451f8ac1 100644 --- a/Mage.Sets/src/mage/cards/k/KnightOfTheLastBreath.java +++ b/Mage.Sets/src/mage/cards/k/KnightOfTheLastBreath.java @@ -14,7 +14,7 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.permanent.token.WhiteBlackSpiritToken; import mage.target.common.TargetControlledPermanent; diff --git a/Mage.Sets/src/mage/cards/k/KnightOfTheWhiteOrchid.java b/Mage.Sets/src/mage/cards/k/KnightOfTheWhiteOrchid.java index 79a0a654ae22..67ddbb470aa2 100644 --- a/Mage.Sets/src/mage/cards/k/KnightOfTheWhiteOrchid.java +++ b/Mage.Sets/src/mage/cards/k/KnightOfTheWhiteOrchid.java @@ -38,7 +38,7 @@ public KnightOfTheWhiteOrchid(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new ConditionalInterveningIfTriggeredAbility( new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 1, new FilterBySubtypeCard(SubType.PLAINS)), false), true), new OpponentControlsMoreCondition(StaticFilters.FILTER_LANDS), - "When {this} enters the battlefield, if an opponent controls more lands than you, you may search your library for a Plains card, put it onto the battlefield, then shuffle your library.")); + "When {this} enters the battlefield, if an opponent controls more lands than you, you may search your library for a Plains card, put it onto the battlefield, then shuffle.")); } diff --git a/Mage.Sets/src/mage/cards/k/KnollspineInvocation.java b/Mage.Sets/src/mage/cards/k/KnollspineInvocation.java index 38c0d46c5d16..8a6243381a67 100644 --- a/Mage.Sets/src/mage/cards/k/KnollspineInvocation.java +++ b/Mage.Sets/src/mage/cards/k/KnollspineInvocation.java @@ -1,10 +1,9 @@ - package mage.cards.k; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.Cost; +import mage.abilities.costs.CostAdjuster; import mage.abilities.costs.common.DiscardTargetCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.dynamicvalue.common.ManacostVariableValue; @@ -15,46 +14,31 @@ import mage.constants.ComparisonType; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; -import mage.target.common.TargetCardInHand; import mage.target.common.TargetAnyTarget; +import mage.target.common.TargetCardInHand; + +import java.util.UUID; /** - * * @author anonymous */ public final class KnollspineInvocation extends CardImpl { - private static final FilterCard filter = new FilterCard("a card with converted mana cost X"); + private static final FilterCard filter = new FilterCard("a card with mana value X"); public KnollspineInvocation(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{R}{R}"); // {X}, Discard a card with converted mana cost X: Knollspine Invocation deals X damage to any target. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(ManacostVariableValue.instance, true), new ManaCostsImpl<>("{X}")); ability.addCost(new DiscardTargetCost(new TargetCardInHand(filter))); ability.addTarget(new TargetAnyTarget()); + ability.setCostAdjuster(KnollspineInvocationAdjuster.instance); this.addAbility(ability); } - @Override - public void adjustCosts(Ability ability, Game game) { - if (ability instanceof SimpleActivatedAbility) { - int xValue = ability.getManaCostsToPay().getX(); - for (Cost cost : ability.getCosts()) { - if (cost instanceof DiscardTargetCost) { - DiscardTargetCost discardCost = (DiscardTargetCost) cost; - discardCost.getTargets().clear(); - FilterCard adjustedFilter = filter.copy(); // don't use it directly, it's static!!!! - adjustedFilter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, xValue)); - discardCost.addTarget(new TargetCardInHand(adjustedFilter)); - return; - } - } - } - } - private KnollspineInvocation(final KnollspineInvocation card) { super(card); } @@ -64,3 +48,23 @@ public KnollspineInvocation copy() { return new KnollspineInvocation(this); } } + +enum KnollspineInvocationAdjuster implements CostAdjuster { + instance; + + @Override + public void adjustCosts(Ability ability, Game game) { + int xValue = ability.getManaCostsToPay().getX(); + for (Cost cost : ability.getCosts()) { + if (!(cost instanceof DiscardTargetCost)) { + continue; + } + DiscardTargetCost discardCost = (DiscardTargetCost) cost; + discardCost.getTargets().clear(); + FilterCard adjustedFilter = new FilterCard("a card with mana value X"); + adjustedFilter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, xValue)); + discardCost.addTarget(new TargetCardInHand(adjustedFilter)); + return; + } + } +} diff --git a/Mage.Sets/src/mage/cards/k/KnowledgeExploitation.java b/Mage.Sets/src/mage/cards/k/KnowledgeExploitation.java index 94370a439dbe..fb793d9e96ba 100644 --- a/Mage.Sets/src/mage/cards/k/KnowledgeExploitation.java +++ b/Mage.Sets/src/mage/cards/k/KnowledgeExploitation.java @@ -1,6 +1,5 @@ package mage.cards.k; -import java.util.UUID; import mage.ApprovingObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; @@ -11,14 +10,15 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.filter.common.FilterInstantOrSorceryCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author emerald000 */ public final class KnowledgeExploitation extends CardImpl { @@ -54,10 +54,10 @@ class KnowledgeExploitationEffect extends OneShotEffect { this.staticText = "Search target opponent's library for an " + "instant or sorcery card. You may cast that card " + "without paying its mana cost. Then that " - + "player shuffles their library"; + + "player shuffles"; } - KnowledgeExploitationEffect(final KnowledgeExploitationEffect effect) { + private KnowledgeExploitationEffect(final KnowledgeExploitationEffect effect) { super(effect); } @@ -69,21 +69,24 @@ public KnowledgeExploitationEffect copy() { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Player opponent = game.getPlayer(this.getTargetPointer().getFirst(game, source)); - if (controller != null && opponent != null) { - TargetCardInLibrary target = new TargetCardInLibrary(0, 1, new FilterInstantOrSorceryCard()); - if (controller.searchLibrary(target, source, game, opponent.getId())) { - Card card = opponent.getLibrary().remove(target.getFirstTarget(), game); - if (card != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - } - } - opponent.shuffleLibrary(source, game); - return true; + Player opponent = game.getPlayer(source.getFirstTarget()); + if (controller == null || opponent == null) { + return false; + } + TargetCardInLibrary target = new TargetCardInLibrary( + 0, 1, StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY + ); + controller.searchLibrary(target, source, game, opponent.getId()); + Card card = opponent.getLibrary().getCard(target.getFirstTarget(), game); + if (card != null) { + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); + controller.cast( + controller.chooseAbilityForCast(card, game, true), + game, true, new ApprovingObject(source, game) + ); + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); } - return false; + opponent.shuffleLibrary(source, game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/k/KodamaOfTheEastTree.java b/Mage.Sets/src/mage/cards/k/KodamaOfTheEastTree.java index 1cc623504524..e85cd70256df 100644 --- a/Mage.Sets/src/mage/cards/k/KodamaOfTheEastTree.java +++ b/Mage.Sets/src/mage/cards/k/KodamaOfTheEastTree.java @@ -18,8 +18,8 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -56,7 +56,7 @@ public KodamaOfTheEastTree(UUID ownerId, CardSetInfo setInfo) { new EntersBattlefieldAllTriggeredAbility(new KodamaOfTheEastTreeEffect(), filter), KodamaOfTheEastTreeCondition.instance, "Whenever another permanent enters the battlefield " + "under your control, if it wasn't put onto the battlefield with this ability, you may put " + - "a permanent card with equal or lesser converted mana cost from your hand onto the battlefield." + "a permanent card with equal or lesser mana value from your hand onto the battlefield." ), new KodamaOfTheEastTreeWatcher()); // Partner @@ -118,10 +118,10 @@ public boolean apply(Game game, Ability source) { return false; } FilterCard filter = new FilterPermanentCard( - "a permanent card with converted mana cost " + permanent.getConvertedManaCost() + " or less" + "a permanent card with mana value " + permanent.getManaValue() + " or less" ); - filter.add(new ConvertedManaCostPredicate( - ComparisonType.FEWER_THAN, permanent.getConvertedManaCost() + 1 + filter.add(new ManaValuePredicate( + ComparisonType.FEWER_THAN, permanent.getManaValue() + 1 )); TargetCardInHand target = new TargetCardInHand(filter); if (!target.canChoose(source.getSourceId(), source.getControllerId(), game) diff --git a/Mage.Sets/src/mage/cards/k/KodamasReach.java b/Mage.Sets/src/mage/cards/k/KodamasReach.java index 3e0da6734743..9ace6ccedc82 100644 --- a/Mage.Sets/src/mage/cards/k/KodamasReach.java +++ b/Mage.Sets/src/mage/cards/k/KodamasReach.java @@ -47,7 +47,8 @@ class KodamasReachEffect extends OneShotEffect { public KodamasReachEffect() { super(Outcome.PutLandInPlay); - staticText = "Search your library for up to two basic land cards, reveal those cards, and put one onto the battlefield tapped and the other into your hand. Then shuffle your library"; + staticText = "search your library for up to two basic land cards, reveal those cards, " + + "put one onto the battlefield tapped and the other into your hand, then shuffle"; } public KodamasReachEffect(final KodamasReachEffect effect) { diff --git a/Mage.Sets/src/mage/cards/k/KollTheForgemaster.java b/Mage.Sets/src/mage/cards/k/KollTheForgemaster.java index c67001d66b1d..86c30a4676c4 100644 --- a/Mage.Sets/src/mage/cards/k/KollTheForgemaster.java +++ b/Mage.Sets/src/mage/cards/k/KollTheForgemaster.java @@ -18,7 +18,7 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicate; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.EnchantedPredicate; import mage.filter.predicate.permanent.EquippedPredicate; import mage.filter.predicate.permanent.TokenPredicate; diff --git a/Mage.Sets/src/mage/cards/k/KomaCosmosSerpent.java b/Mage.Sets/src/mage/cards/k/KomaCosmosSerpent.java index 3220849f2873..9d020070aa5e 100644 --- a/Mage.Sets/src/mage/cards/k/KomaCosmosSerpent.java +++ b/Mage.Sets/src/mage/cards/k/KomaCosmosSerpent.java @@ -17,7 +17,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.KomasCoilToken; diff --git a/Mage.Sets/src/mage/cards/k/KorChant.java b/Mage.Sets/src/mage/cards/k/KorChant.java index 75db62cb3b79..7b14735d97e5 100644 --- a/Mage.Sets/src/mage/cards/k/KorChant.java +++ b/Mage.Sets/src/mage/cards/k/KorChant.java @@ -10,7 +10,7 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.target.TargetSource; @@ -76,7 +76,7 @@ public void init(Ability source, Game game) { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/k/KorDirge.java b/Mage.Sets/src/mage/cards/k/KorDirge.java index 0d102b371086..2d66c71f3cd0 100644 --- a/Mage.Sets/src/mage/cards/k/KorDirge.java +++ b/Mage.Sets/src/mage/cards/k/KorDirge.java @@ -10,7 +10,7 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.target.TargetSource; @@ -76,7 +76,7 @@ public void init(Ability source, Game game) { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/k/KorlashHeirToBlackblade.java b/Mage.Sets/src/mage/cards/k/KorlashHeirToBlackblade.java index c8b64fab390e..f39bfe11f00f 100644 --- a/Mage.Sets/src/mage/cards/k/KorlashHeirToBlackblade.java +++ b/Mage.Sets/src/mage/cards/k/KorlashHeirToBlackblade.java @@ -52,7 +52,7 @@ public KorlashHeirToBlackblade(UUID ownerId, CardSetInfo setInfo) { // Grandeur - Discard another card named Korlash, Heir to Blackblade: Search your library for up to two Swamp cards, put them onto the battlefield tapped, then shuffle your library. effect = new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 2, filterCard), true, true); - effect.setText("Search your library for up to two Swamp cards, put them onto the battlefield tapped, then shuffle your library."); + effect.setText("Search your library for up to two Swamp cards, put them onto the battlefield tapped, then shuffle."); this.addAbility(new GrandeurAbility(effect, "Korlash, Heir to Blackblade")); } diff --git a/Mage.Sets/src/mage/cards/k/KorvoldFaeCursedKing.java b/Mage.Sets/src/mage/cards/k/KorvoldFaeCursedKing.java index 2d6b7a4d255e..4c0597dbb447 100644 --- a/Mage.Sets/src/mage/cards/k/KorvoldFaeCursedKing.java +++ b/Mage.Sets/src/mage/cards/k/KorvoldFaeCursedKing.java @@ -15,7 +15,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; diff --git a/Mage.Sets/src/mage/cards/k/KozilekTheGreatDistortion.java b/Mage.Sets/src/mage/cards/k/KozilekTheGreatDistortion.java index 696859a6247f..24c768ee9261 100644 --- a/Mage.Sets/src/mage/cards/k/KozilekTheGreatDistortion.java +++ b/Mage.Sets/src/mage/cards/k/KozilekTheGreatDistortion.java @@ -26,7 +26,7 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.stack.Spell; import mage.game.stack.StackObject; @@ -51,13 +51,13 @@ public KozilekTheGreatDistortion(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new ConditionalInterveningIfTriggeredAbility( new CastSourceTriggeredAbility(new KozilekDrawEffect(), false), new CardsInHandCondition(ComparisonType.FEWER_THAN, 7), - "When you cast {this}, if you have fewer than seven cards in hand, draw cards equal to the difference.")); + "When you cast this spell, if you have fewer than seven cards in hand, draw cards equal to the difference.")); // Menace this.addAbility(new MenaceAbility()); // Discard a card with converted mana cost X: Counter target spell with converted mana cost X. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CounterTargetEffect(), new KozilekDiscardCost()); - ability.addTarget(new TargetSpell(new FilterSpell("spell with converted mana cost X"))); + ability.addTarget(new TargetSpell(new FilterSpell("spell with mana value X"))); this.addAbility(ability); } @@ -101,7 +101,7 @@ public boolean apply(Game game, Ability source) { class KozilekDiscardCost extends CostImpl { public KozilekDiscardCost() { - this.text = "discard a card with converted mana cost X"; + this.text = "discard a card with mana value X"; } public KozilekDiscardCost(final KozilekDiscardCost cost) { @@ -118,8 +118,8 @@ public boolean pay(Ability ability, Game game, Ability source, UUID controllerId if (player == null) { return false; } - FilterCard filter = new FilterCard("card with converted mana cost of " + targetSpell.getConvertedManaCost()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, targetSpell.getConvertedManaCost())); + FilterCard filter = new FilterCard("card with mana value of " + targetSpell.getManaValue()); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, targetSpell.getManaValue())); TargetCardInHand target = new TargetCardInHand(filter); this.getTargets().clear(); this.getTargets().add(target); @@ -150,13 +150,13 @@ public boolean canPay(Ability ability, Ability source, UUID controllerId, Game g Set stackCMC = new HashSet<>(); for (StackObject stackObject : game.getStack()) { if (stackObject instanceof Spell) { - stackCMC.add(stackObject.getConvertedManaCost()); + stackCMC.add(stackObject.getManaValue()); } } Player controller = game.getPlayer(ability.getControllerId()); if(controller != null) { for (Card card : controller.getHand().getCards(game)) { - if (stackCMC.contains(card.getConvertedManaCost())) { + if (stackCMC.contains(card.getManaValue())) { return true; } } diff --git a/Mage.Sets/src/mage/cards/k/KozileksReturn.java b/Mage.Sets/src/mage/cards/k/KozileksReturn.java index b6d19e650985..484b6acdf37a 100644 --- a/Mage.Sets/src/mage/cards/k/KozileksReturn.java +++ b/Mage.Sets/src/mage/cards/k/KozileksReturn.java @@ -15,7 +15,7 @@ import mage.constants.Zone; import mage.filter.FilterSpell; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; /** * @@ -23,12 +23,12 @@ */ public final class KozileksReturn extends CardImpl { - private static final FilterSpell filter = new FilterSpell("an Eldrazi creature spell with converted mana cost 7 or greater"); + private static final FilterSpell filter = new FilterSpell("an Eldrazi creature spell with mana value 7 or greater"); static { filter.add(SubType.ELDRAZI.getPredicate()); filter.add(CardType.CREATURE.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 6)); + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 6)); } public KozileksReturn(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/k/KrallenhordeHowler.java b/Mage.Sets/src/mage/cards/k/KrallenhordeHowler.java index 66bce10b4547..6d7fcccf9360 100644 --- a/Mage.Sets/src/mage/cards/k/KrallenhordeHowler.java +++ b/Mage.Sets/src/mage/cards/k/KrallenhordeHowler.java @@ -1,39 +1,27 @@ - package mage.cards.k; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.Predicates; +import mage.filter.common.FilterCreatureCard; + +import java.util.UUID; /** - * * @author fireshoes */ public final class KrallenhordeHowler extends CardImpl { - private static final FilterCard FILTER = new FilterCard("Creature spells"); - - static { - FILTER.add(Predicates.or(CardType.CREATURE.getPredicate())); - } + private static final FilterCard FILTER = new FilterCreatureCard("creature spells"); public KrallenhordeHowler(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.WEREWOLF); this.power = new MageInt(3); this.toughness = new MageInt(3); @@ -44,11 +32,10 @@ public KrallenhordeHowler(UUID ownerId, CardSetInfo setInfo) { this.transformable = true; // Creature spells you cast cost {1} less to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(FILTER, 1))); + this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(FILTER, 1))); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Krallenhorde Howler. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private KrallenhordeHowler(final KrallenhordeHowler card) { diff --git a/Mage.Sets/src/mage/cards/k/KrallenhordeKiller.java b/Mage.Sets/src/mage/cards/k/KrallenhordeKiller.java index 917c6f731359..71088f54106d 100644 --- a/Mage.Sets/src/mage/cards/k/KrallenhordeKiller.java +++ b/Mage.Sets/src/mage/cards/k/KrallenhordeKiller.java @@ -1,29 +1,26 @@ - package mage.cards.k; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.LimitedTimesPerTurnActivatedAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; + +import java.util.UUID; /** - * * @author Loki */ public final class KrallenhordeKiller extends CardImpl { public KrallenhordeKiller(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.WEREWOLF); this.color.setGreen(true); @@ -37,8 +34,7 @@ public KrallenhordeKiller(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new LimitedTimesPerTurnActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(4, 4, Duration.EndOfTurn), new ManaCostsImpl("{3}{G}"))); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Krallenhorde Killer. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private KrallenhordeKiller(final KrallenhordeKiller card) { diff --git a/Mage.Sets/src/mage/cards/k/KrallenhordeWantons.java b/Mage.Sets/src/mage/cards/k/KrallenhordeWantons.java index 4cfceca02a93..abd6712e7e56 100644 --- a/Mage.Sets/src/mage/cards/k/KrallenhordeWantons.java +++ b/Mage.Sets/src/mage/cards/k/KrallenhordeWantons.java @@ -1,28 +1,21 @@ - package mage.cards.k; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; -import mage.abilities.keyword.TransformAbility; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** * @author nantuko */ public final class KrallenhordeWantons extends CardImpl { - public KrallenhordeWantons(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.WEREWOLF); this.color.setGreen(true); @@ -34,8 +27,7 @@ public KrallenhordeWantons(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(7); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Krallenhorde Wantons. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private KrallenhordeWantons(final KrallenhordeWantons card) { diff --git a/Mage.Sets/src/mage/cards/k/KrenkoTinStreetKingpin.java b/Mage.Sets/src/mage/cards/k/KrenkoTinStreetKingpin.java index 10b4d902e58d..4373e2d6af8e 100644 --- a/Mage.Sets/src/mage/cards/k/KrenkoTinStreetKingpin.java +++ b/Mage.Sets/src/mage/cards/k/KrenkoTinStreetKingpin.java @@ -72,6 +72,6 @@ public boolean apply(Game game, Ability source) { new AddCountersSourceEffect(CounterType.P1P1.createInstance()).apply(game, source); game.getState().processAction(game); int xValue = permanent.getPower().getValue(); - return new CreateTokenEffect(new GoblinToken("WAR"), xValue).apply(game, source); + return new CreateTokenEffect(new GoblinToken(), xValue).apply(game, source); } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/k/KrosanDrover.java b/Mage.Sets/src/mage/cards/k/KrosanDrover.java index 0c37e4fb3d34..f92153a82388 100644 --- a/Mage.Sets/src/mage/cards/k/KrosanDrover.java +++ b/Mage.Sets/src/mage/cards/k/KrosanDrover.java @@ -13,7 +13,7 @@ import mage.constants.ComparisonType; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; /** * @@ -21,11 +21,11 @@ */ public final class KrosanDrover extends CardImpl { - private static final FilterCard filter = new FilterCard("Creature spells with converted mana cost 6 or greater"); + private static final FilterCard filter = new FilterCard("Creature spells with mana value 6 or greater"); static { filter.add(CardType.CREATURE.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 5)); + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 5)); } public KrosanDrover(UUID ownerId, CardSetInfo setInfo) { @@ -36,7 +36,7 @@ public KrosanDrover(UUID ownerId, CardSetInfo setInfo) { // Creature spells you cast with converted mana cost 6 or greater cost {2} less to cast. Effect effect = new SpellsCostReductionControllerEffect(filter, 2); - effect.setText("Creature spells you cast with converted mana cost 6 or greater cost {2} less to cast."); + effect.setText("Creature spells you cast with mana value 6 or greater cost {2} less to cast."); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); } diff --git a/Mage.Sets/src/mage/cards/k/KrosanReclamation.java b/Mage.Sets/src/mage/cards/k/KrosanReclamation.java index a7541ec6f4e6..fb95f86803c2 100644 --- a/Mage.Sets/src/mage/cards/k/KrosanReclamation.java +++ b/Mage.Sets/src/mage/cards/k/KrosanReclamation.java @@ -1,38 +1,29 @@ - package mage.cards.k; -import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.TargetPlayerShufflesTargetCardsEffect; import mage.abilities.keyword.FlashbackAbility; -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.TimingRule; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.game.Game; -import mage.players.Player; import mage.target.TargetPlayer; -import mage.target.common.TargetCardInGraveyard; +import mage.target.common.TargetCardInTargetPlayersGraveyard; + +import java.util.UUID; /** - * * @author fireshoes */ public final class KrosanReclamation extends CardImpl { public KrosanReclamation(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); // Target player shuffles up to two target cards from their graveyard into their library. - this.getSpellAbility().addEffect(new KrosanReclamationEffect()); + this.getSpellAbility().addEffect(new TargetPlayerShufflesTargetCardsEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); - this.getSpellAbility().addTarget(new KrosanReclamationTarget()); + this.getSpellAbility().addTarget(new TargetCardInTargetPlayersGraveyard(2)); // Flashback {1}{G} this.addAbility(new FlashbackAbility(new ManaCostsImpl("{1}{G}"), TimingRule.INSTANT)); @@ -47,57 +38,3 @@ public KrosanReclamation copy() { return new KrosanReclamation(this); } } - -class KrosanReclamationEffect extends OneShotEffect { - - public KrosanReclamationEffect() { - super(Outcome.Neutral); - this.staticText = "Target player shuffles up to two target cards from their graveyard into their library"; - } - - public KrosanReclamationEffect(final KrosanReclamationEffect effect) { - super(effect); - } - - @Override - public KrosanReclamationEffect copy() { - return new KrosanReclamationEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player targetPlayer = game.getPlayer(source.getFirstTarget()); - if (targetPlayer != null) { - return targetPlayer.shuffleCardsToLibrary(new CardsImpl(source.getTargets().get(1).getTargets()), game, source); - } - return false; - } -} - -class KrosanReclamationTarget extends TargetCardInGraveyard { - - public KrosanReclamationTarget() { - super(0, 2, new FilterCard()); - } - - public KrosanReclamationTarget(final KrosanReclamationTarget target) { - super(target); - } - - @Override - public boolean canTarget(UUID id, Ability source, Game game) { - Card card = game.getCard(id); - if (card != null && game.getState().getZone(card.getId()) == Zone.GRAVEYARD) { - UUID firstTarget = source.getFirstTarget(); - if (firstTarget != null && game.getPlayer(firstTarget).getGraveyard().contains(id)) { - return filter.match(card, game); - } - } - return false; - } - - @Override - public KrosanReclamationTarget copy() { - return new KrosanReclamationTarget(this); - } -} diff --git a/Mage.Sets/src/mage/cards/k/KrosanVerge.java b/Mage.Sets/src/mage/cards/k/KrosanVerge.java index 2e17314ce313..680706114123 100644 --- a/Mage.Sets/src/mage/cards/k/KrosanVerge.java +++ b/Mage.Sets/src/mage/cards/k/KrosanVerge.java @@ -37,7 +37,7 @@ public KrosanVerge(UUID ownerId, CardSetInfo setInfo) { // {2}, {T}, Sacrifice Krosan Verge: Search your library for a Forest card and a Plains card and put them onto the battlefield tapped. Then shuffle your library. Ability ability = new SimpleActivatedAbility(new SearchLibraryPutInPlayEffect( new KrosanVergeTarget(), true, Outcome.PutLandInPlay - ).setText("search your library for a Forest card and a Plains card, put them onto the battlefield tapped, then shuffle your library"), new GenericManaCost(2)); + ).setText("search your library for a Forest card and a Plains card, put them onto the battlefield tapped, then shuffle"), new GenericManaCost(2)); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/k/KrovikanVampire.java b/Mage.Sets/src/mage/cards/k/KrovikanVampire.java index 053e2239df31..62c46d695718 100644 --- a/Mage.Sets/src/mage/cards/k/KrovikanVampire.java +++ b/Mage.Sets/src/mage/cards/k/KrovikanVampire.java @@ -1,8 +1,5 @@ package mage.cards.k; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; @@ -11,26 +8,22 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.SacrificeTargetEffect; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.constants.WatcherScope; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; import mage.watchers.Watcher; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class KrovikanVampire extends CardImpl { @@ -149,10 +142,15 @@ public KrovikanVampireCreaturesDamagedWatcher() { @Override public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.DAMAGED_CREATURE - && sourceId.equals(event.getSourceId())) { - damagedBySource.add(event.getTargetId()); + if (event.getType() != GameEvent.EventType.DAMAGED_PERMANENT + || !sourceId.equals(event.getSourceId())) { + return; + } + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent == null || !permanent.isCreature()) { + return; } + damagedBySource.add(event.getTargetId()); } public Set getDamagedBySource() { diff --git a/Mage.Sets/src/mage/cards/k/KruinOutlaw.java b/Mage.Sets/src/mage/cards/k/KruinOutlaw.java index 8011d822beb2..63c8a27ebc3a 100644 --- a/Mage.Sets/src/mage/cards/k/KruinOutlaw.java +++ b/Mage.Sets/src/mage/cards/k/KruinOutlaw.java @@ -1,21 +1,15 @@ - package mage.cards.k; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.keyword.FirstStrikeAbility; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** * @author North @@ -37,8 +31,7 @@ public KruinOutlaw(UUID ownerId, CardSetInfo setInfo) { this.addAbility(FirstStrikeAbility.getInstance()); // At the beginning of each upkeep, if no spells were cast last turn, transform Kruin Outlaw. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private KruinOutlaw(final KruinOutlaw card) { diff --git a/Mage.Sets/src/mage/cards/k/KruphixGodOfHorizons.java b/Mage.Sets/src/mage/cards/k/KruphixGodOfHorizons.java index c5bbad1ce637..e1523b10d90f 100644 --- a/Mage.Sets/src/mage/cards/k/KruphixGodOfHorizons.java +++ b/Mage.Sets/src/mage/cards/k/KruphixGodOfHorizons.java @@ -4,7 +4,7 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.common.DevotionCount; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.continuous.LoseCreatureTypeSourceEffect; import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect; import mage.abilities.keyword.IndestructibleAbility; @@ -12,7 +12,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; -import mage.game.events.GameEvent; +import mage.players.Player; import java.util.UUID; @@ -41,9 +41,9 @@ public KruphixGodOfHorizons(UUID ownerId, CardSetInfo setInfo) { Integer.MAX_VALUE, Duration.WhileOnBattlefield, MaximumHandSizeControllerEffect.HandSizeModification.SET ))); + // If unused mana would empty from your mana pool, that mana becomes colorless instead. this.addAbility(new SimpleStaticAbility(new KruphixGodOfHorizonsEffect())); - } private KruphixGodOfHorizons(final KruphixGodOfHorizons card) { @@ -56,11 +56,11 @@ public KruphixGodOfHorizons copy() { } } -class KruphixGodOfHorizonsEffect extends ReplacementEffectImpl { +class KruphixGodOfHorizonsEffect extends ContinuousEffectImpl { KruphixGodOfHorizonsEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "If you would lose unspent mana, that mana becomes colorless instead."; + super(Duration.WhileOnBattlefield, Layer.RulesEffects, SubLayer.NA, Outcome.Benefit); + staticText = "if you would lose unspent mana, that mana becomes colorless instead"; } private KruphixGodOfHorizonsEffect(final KruphixGodOfHorizonsEffect effect) { @@ -74,21 +74,10 @@ public KruphixGodOfHorizonsEffect copy() { @Override public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player player = game.getPlayer(source.getControllerId()); + if (player != null) { + player.getManaPool().setManaBecomesColorless(true); + } return true; } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.EMPTY_MANA_POOL; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - return event.getPlayerId().equals(source.getControllerId()); - } } diff --git a/Mage.Sets/src/mage/cards/k/KryShield.java b/Mage.Sets/src/mage/cards/k/KryShield.java index 0278c23736c6..47162385fa16 100644 --- a/Mage.Sets/src/mage/cards/k/KryShield.java +++ b/Mage.Sets/src/mage/cards/k/KryShield.java @@ -7,7 +7,7 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.dynamicvalue.common.TargetConvertedManaCost; +import mage.abilities.dynamicvalue.common.TargetManaValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.PreventDamageByTargetEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; @@ -31,8 +31,8 @@ public KryShield(UUID ownerId, CardSetInfo setInfo) { Effect effect = new PreventDamageByTargetEffect(Duration.EndOfTurn); effect.setText("Prevent all damage that would be dealt this turn by target creature you control"); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new GenericManaCost(2)); - ability.addEffect(new BoostTargetEffect(StaticValue.get(0), TargetConvertedManaCost.instance, Duration.EndOfTurn, true) - .setText("That creature gets +0/+X until end of turn, where X is its converted mana cost")); + ability.addEffect(new BoostTargetEffect(StaticValue.get(0), TargetManaValue.instance, Duration.EndOfTurn, true) + .setText("That creature gets +0/+X until end of turn, where X is its mana value")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/k/KuldothaForgemaster.java b/Mage.Sets/src/mage/cards/k/KuldothaForgemaster.java index 4adb47b1cc36..60d08870141e 100644 --- a/Mage.Sets/src/mage/cards/k/KuldothaForgemaster.java +++ b/Mage.Sets/src/mage/cards/k/KuldothaForgemaster.java @@ -12,6 +12,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.filter.common.FilterArtifactCard; import mage.filter.common.FilterControlledArtifactPermanent; import mage.target.common.TargetCardInLibrary; @@ -34,7 +35,7 @@ public KuldothaForgemaster(UUID ownerId, CardSetInfo setInfo) { SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(new FilterArtifactCard())), new TapSourceCost()); - ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(3, 3, new FilterControlledArtifactPermanent("three artifacts you control"), false))); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(3, 3, StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT, false))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/k/KuldothaPhoenix.java b/Mage.Sets/src/mage/cards/k/KuldothaPhoenix.java index 71a7c5739012..64f5f39a0c36 100644 --- a/Mage.Sets/src/mage/cards/k/KuldothaPhoenix.java +++ b/Mage.Sets/src/mage/cards/k/KuldothaPhoenix.java @@ -36,7 +36,7 @@ public KuldothaPhoenix(UUID ownerId, CardSetInfo setInfo) { // Metalcraft — {4}: Return Kuldotha Phoenix from your graveyard to the battlefield. // Activate this ability only during your upkeep and only if you control three or more artifacts. Ability ability = new ConditionalActivatedAbility(Zone.GRAVEYARD, - new ReturnSourceFromGraveyardToBattlefieldEffect(), + new ReturnSourceFromGraveyardToBattlefieldEffect(false, true), new ManaCostsImpl("{4}"), new CompoundCondition("during your upkeep and only if you control three or more artifacts", new IsStepCondition(PhaseStep.UPKEEP), MetalcraftCondition.instance) diff --git a/Mage.Sets/src/mage/cards/k/KumanosBlessing.java b/Mage.Sets/src/mage/cards/k/KumanosBlessing.java index e032b6d1f667..282c12f1cba5 100644 --- a/Mage.Sets/src/mage/cards/k/KumanosBlessing.java +++ b/Mage.Sets/src/mage/cards/k/KumanosBlessing.java @@ -13,10 +13,8 @@ import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; import mage.watchers.Watcher; @@ -110,12 +108,14 @@ public DamagedByEnchantedWatcher() { @Override public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.DAMAGED_CREATURE) { + if (event.getType() == GameEvent.EventType.DAMAGED_PERMANENT) { Permanent enchantment = game.getPermanent(this.getSourceId()); if (enchantment != null && enchantment.isAttachedTo(event.getSourceId())) { - MageObjectReference mor = new MageObjectReference(event.getTargetId(), game); - damagedCreatures.add(mor); - + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null && permanent.isCreature()) { + MageObjectReference mor = new MageObjectReference(event.getTargetId(), game); + damagedCreatures.add(mor); + } } } } diff --git a/Mage.Sets/src/mage/cards/k/KumenaTyrantOfOrazca.java b/Mage.Sets/src/mage/cards/k/KumenaTyrantOfOrazca.java index 26279caba569..f6f5d5dc3546 100644 --- a/Mage.Sets/src/mage/cards/k/KumenaTyrantOfOrazca.java +++ b/Mage.Sets/src/mage/cards/k/KumenaTyrantOfOrazca.java @@ -18,7 +18,7 @@ import mage.counters.CounterType; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TappedPredicate; import mage.target.common.TargetControlledPermanent; diff --git a/Mage.Sets/src/mage/cards/k/Kurgadon.java b/Mage.Sets/src/mage/cards/k/Kurgadon.java index 039b6b722f9e..eac1b3aeff7f 100644 --- a/Mage.Sets/src/mage/cards/k/Kurgadon.java +++ b/Mage.Sets/src/mage/cards/k/Kurgadon.java @@ -12,7 +12,7 @@ import mage.constants.ComparisonType; import mage.counters.CounterType; import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; /** * @@ -20,11 +20,11 @@ */ public final class Kurgadon extends CardImpl { - private static final FilterSpell filterSpell = new FilterSpell("a creature spell with converted mana cost 6 or greater"); + private static final FilterSpell filterSpell = new FilterSpell("a creature spell with mana value 6 or greater"); static { filterSpell.add(CardType.CREATURE.getPredicate()); - filterSpell.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 5)); + filterSpell.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 5)); } public Kurgadon(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/k/KurkeshOnakkeAncient.java b/Mage.Sets/src/mage/cards/k/KurkeshOnakkeAncient.java index cfb30d49880f..fedfda95bc4f 100644 --- a/Mage.Sets/src/mage/cards/k/KurkeshOnakkeAncient.java +++ b/Mage.Sets/src/mage/cards/k/KurkeshOnakkeAncient.java @@ -1,33 +1,31 @@ - package mage.cards.k; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.costs.mana.ColoredManaCost; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CopyStackAbilityEffect; +import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; import mage.game.stack.StackAbility; -import mage.players.Player; + +import java.util.UUID; /** - * * @author emerald000 */ public final class KurkeshOnakkeAncient extends CardImpl { public KurkeshOnakkeAncient(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.OGRE); this.subtype.add(SubType.SPIRIT); @@ -52,7 +50,7 @@ public KurkeshOnakkeAncient copy() { class KurkeshOnakkeAncientTriggeredAbility extends TriggeredAbilityImpl { KurkeshOnakkeAncientTriggeredAbility() { - super(Zone.BATTLEFIELD, new KurkeshOnakkeAncientEffect(), false); + super(Zone.BATTLEFIELD, new DoIfCostPaid(new CopyStackAbilityEffect(), new GenericManaCost(1))); } KurkeshOnakkeAncientTriggeredAbility(final KurkeshOnakkeAncientTriggeredAbility ability) { @@ -71,18 +69,19 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getPlayerId().equals(getControllerId())) { - Card source = game.getPermanentOrLKIBattlefield(event.getSourceId()); - if (source != null && source.isArtifact()) { - StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); - if (!(stackAbility.getStackAbility() instanceof ActivatedManaAbilityImpl)) { - Effect effect = this.getEffects().get(0); - effect.setValue("stackAbility", stackAbility); - return true; - } - } + if (!event.getPlayerId().equals(getControllerId())) { + return false; + } + Card source = game.getPermanentOrLKIBattlefield(event.getSourceId()); + if (source == null || !source.isArtifact()) { + return false; } - return false; + StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); + if (stackAbility == null || stackAbility.getStackAbility() instanceof ActivatedManaAbilityImpl) { + return false; + } + this.getEffects().setValue("stackAbility", stackAbility); + return true; } @Override @@ -90,42 +89,3 @@ public String getRule() { return "Whenever you activate an ability of an artifact, if it isn't a mana ability" + super.getRule(); } } - -class KurkeshOnakkeAncientEffect extends OneShotEffect { - - KurkeshOnakkeAncientEffect() { - super(Outcome.Benefit); - this.staticText = ", you may pay {R}. If you do, copy that ability. You may choose new targets for the copy"; - } - - KurkeshOnakkeAncientEffect(final KurkeshOnakkeAncientEffect effect) { - super(effect); - } - - @Override - public KurkeshOnakkeAncientEffect copy() { - return new KurkeshOnakkeAncientEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - ColoredManaCost cost = new ColoredManaCost(ColoredManaSymbol.R); - if (player != null) { - if (player.chooseUse(Outcome.Benefit, "Pay " + cost.getText() + "? If you do, copy that ability. You may choose new targets for the copy.", source, game)) { - if (cost.pay(source, game, source, source.getControllerId(), false)) { - StackAbility ability = (StackAbility) getValue("stackAbility"); - Player controller = game.getPlayer(source.getControllerId()); - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (ability != null && controller != null) { - ability.createCopyOnStack(game, source, source.getControllerId(), true); - game.informPlayers(sourcePermanent.getName() + ": " + controller.getLogName() + " copied activated ability"); - return true; - } - return false; - } - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/k/KusariGama.java b/Mage.Sets/src/mage/cards/k/KusariGama.java index 1d7345d908b6..3a99387f2320 100644 --- a/Mage.Sets/src/mage/cards/k/KusariGama.java +++ b/Mage.Sets/src/mage/cards/k/KusariGama.java @@ -79,7 +79,7 @@ public KusariGamaAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/k/KyokiSanitysEclipse.java b/Mage.Sets/src/mage/cards/k/KyokiSanitysEclipse.java index 066741c86c31..f602e4985b48 100644 --- a/Mage.Sets/src/mage/cards/k/KyokiSanitysEclipse.java +++ b/Mage.Sets/src/mage/cards/k/KyokiSanitysEclipse.java @@ -1,7 +1,5 @@ - package mage.cards.k; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SpellCastControllerTriggeredAbility; @@ -12,18 +10,18 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.constants.Zone; -import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class KyokiSanitysEclipse extends CardImpl { public KyokiSanitysEclipse(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{B}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.DEMON); this.subtype.add(SubType.SPIRIT); @@ -32,10 +30,12 @@ public KyokiSanitysEclipse(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(4); // Whenever you cast a Spirit or Arcane spell, target opponent exiles a card from their hand. - Ability ability = new SpellCastControllerTriggeredAbility(new ExileFromZoneTargetEffect(Zone.HAND, null, "", new FilterCard()), StaticFilters.SPIRIT_OR_ARCANE_CARD, false); + Ability ability = new SpellCastControllerTriggeredAbility( + new ExileFromZoneTargetEffect(Zone.HAND, false), + StaticFilters.SPIRIT_OR_ARCANE_CARD, false + ); ability.addTarget(new TargetOpponent()); this.addAbility(ability); - } private KyokiSanitysEclipse(final KyokiSanitysEclipse card) { diff --git a/Mage.Sets/src/mage/cards/l/LadySun.java b/Mage.Sets/src/mage/cards/l/LadySun.java index 8d653c65d54e..c4a42248bfc8 100644 --- a/Mage.Sets/src/mage/cards/l/LadySun.java +++ b/Mage.Sets/src/mage/cards/l/LadySun.java @@ -17,7 +17,7 @@ import mage.constants.SuperType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/l/LagonnaBandStoryteller.java b/Mage.Sets/src/mage/cards/l/LagonnaBandStoryteller.java index 8da903278f43..a4d23f6b0618 100644 --- a/Mage.Sets/src/mage/cards/l/LagonnaBandStoryteller.java +++ b/Mage.Sets/src/mage/cards/l/LagonnaBandStoryteller.java @@ -58,7 +58,7 @@ class LagonnaBandStorytellerEffect extends OneShotEffect { LagonnaBandStorytellerEffect() { super(Outcome.Benefit); staticText = "put target enchantment card from your graveyard on top of your library. " - + "If you do, you gain life equal to its converted mana cost."; + + "If you do, you gain life equal to its mana value."; } private LagonnaBandStorytellerEffect(final LagonnaBandStorytellerEffect effect) { @@ -80,7 +80,7 @@ public boolean apply(Game game, Ability source) { || game.getState().getZone(card.getId()) != Zone.GRAVEYARD) { return false; } - int cmc = card.getConvertedManaCost(); + int cmc = card.getManaValue(); if (controller.putCardsOnTopOfLibrary(new CardsImpl(card), game, source, false)) { controller.gainLife(cmc, game, source); return true; diff --git a/Mage.Sets/src/mage/cards/l/LambholtButcher.java b/Mage.Sets/src/mage/cards/l/LambholtButcher.java index 1dd87ea617a6..b44499d9a5fd 100644 --- a/Mage.Sets/src/mage/cards/l/LambholtButcher.java +++ b/Mage.Sets/src/mage/cards/l/LambholtButcher.java @@ -1,10 +1,10 @@ - package mage.cards.l; import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.TransformSourceEffect; @@ -33,8 +33,7 @@ public LambholtButcher(UUID ownerId, CardSetInfo setInfo) { this.transformable = true; // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Lambholt Butcher. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private LambholtButcher(final LambholtButcher card) { diff --git a/Mage.Sets/src/mage/cards/l/LambholtElder.java b/Mage.Sets/src/mage/cards/l/LambholtElder.java index f8d542336f6b..6ef1223ee394 100644 --- a/Mage.Sets/src/mage/cards/l/LambholtElder.java +++ b/Mage.Sets/src/mage/cards/l/LambholtElder.java @@ -1,20 +1,14 @@ - package mage.cards.l; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** * @author Loki @@ -34,8 +28,7 @@ public LambholtElder(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each upkeep, if no spells were cast last turn, transform Lambholt Elder. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private LambholtElder(final LambholtElder card) { diff --git a/Mage.Sets/src/mage/cards/l/LambholtPacifist.java b/Mage.Sets/src/mage/cards/l/LambholtPacifist.java index ca4179d79727..023c468e6c2b 100644 --- a/Mage.Sets/src/mage/cards/l/LambholtPacifist.java +++ b/Mage.Sets/src/mage/cards/l/LambholtPacifist.java @@ -2,13 +2,9 @@ import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.effects.RestrictionEffect; -import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -41,8 +37,7 @@ public LambholtPacifist(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each upkeep, if no spells were cast last turn, transform Lambholt Pacifist. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private LambholtPacifist(final LambholtPacifist card) { diff --git a/Mage.Sets/src/mage/cards/l/LammastideWeave.java b/Mage.Sets/src/mage/cards/l/LammastideWeave.java index fc9365efdbc8..8512c0eb0db9 100644 --- a/Mage.Sets/src/mage/cards/l/LammastideWeave.java +++ b/Mage.Sets/src/mage/cards/l/LammastideWeave.java @@ -30,7 +30,7 @@ public LammastideWeave(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addTarget(new TargetPlayer()); // Draw a card. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); } @@ -49,7 +49,7 @@ class LammastideWeaveEffect extends OneShotEffect { LammastideWeaveEffect() { super(Outcome.DrawCard); this.staticText = ", then target player mills a card. If a card with the chosen name was milled this way, " + - "you gain life equal to its converted mana cost."; + "you gain life equal to its mana value."; } private LammastideWeaveEffect(final LammastideWeaveEffect effect) { @@ -71,7 +71,7 @@ public boolean apply(Game game, Ability source) { } for (Card card : targetPlayer.millCards(1, source, game).getCards(game)) { if (CardUtil.haveSameNames(card, cardName, game)) { - controller.gainLife(card.getConvertedManaCost(), game, source); + controller.gainLife(card.getManaValue(), game, source); } } return true; diff --git a/Mage.Sets/src/mage/cards/l/LamplighterOfSelhoff.java b/Mage.Sets/src/mage/cards/l/LamplighterOfSelhoff.java index 14a7109a0eb0..a354078460a0 100644 --- a/Mage.Sets/src/mage/cards/l/LamplighterOfSelhoff.java +++ b/Mage.Sets/src/mage/cards/l/LamplighterOfSelhoff.java @@ -12,7 +12,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/l/LandTax.java b/Mage.Sets/src/mage/cards/l/LandTax.java index 8a9257ee3141..00664975ede5 100644 --- a/Mage.Sets/src/mage/cards/l/LandTax.java +++ b/Mage.Sets/src/mage/cards/l/LandTax.java @@ -27,7 +27,7 @@ public LandTax(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new ConditionalInterveningIfTriggeredAbility( new BeginningOfUpkeepTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 3, StaticFilters.FILTER_CARD_BASIC_LAND), true, true), TargetController.YOU, true), new OpponentControlsMoreCondition(StaticFilters.FILTER_LANDS), - "At the beginning of your upkeep, if an opponent controls more lands than you, you may search your library for up to three basic land cards, reveal them, and put them into your hand. If you do, shuffle your library" + "At the beginning of your upkeep, if an opponent controls more lands than you, you may search your library for up to three basic land cards, reveal them, put them into your hand, then shuffle." )); } diff --git a/Mage.Sets/src/mage/cards/l/LanternOfInsight.java b/Mage.Sets/src/mage/cards/l/LanternOfInsight.java index f5f32b054d9b..828e535770ae 100644 --- a/Mage.Sets/src/mage/cards/l/LanternOfInsight.java +++ b/Mage.Sets/src/mage/cards/l/LanternOfInsight.java @@ -1,7 +1,5 @@ - package mage.cards.l; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; @@ -12,23 +10,23 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author emerald000 */ public final class LanternOfInsight extends CardImpl { public LanternOfInsight(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{1}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); // Each player plays with the top card of their library revealed. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayWithTheTopCardRevealedEffect(true))); - + this.addAbility(new SimpleStaticAbility(new PlayWithTheTopCardRevealedEffect(true))); + // {tap}, Sacrifice Lantern of Insight: Target player shuffles their library. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ShuffleLibraryTargetEffect(), new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(new ShuffleLibraryTargetEffect().setText("target player shuffles"), new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); ability.addTarget(new TargetPlayer()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/l/LashOfMalice.java b/Mage.Sets/src/mage/cards/l/LashOfMalice.java new file mode 100644 index 000000000000..693019c3d379 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LashOfMalice.java @@ -0,0 +1,32 @@ +package mage.cards.l; + +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LashOfMalice extends CardImpl { + + public LashOfMalice(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}"); + + // Target creature gets +2/-2 until end of turn. + this.getSpellAbility().addEffect(new BoostTargetEffect(2, -2)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private LashOfMalice(final LashOfMalice card) { + super(card); + } + + @Override + public LashOfMalice copy() { + return new LashOfMalice(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LashknifeBarrier.java b/Mage.Sets/src/mage/cards/l/LashknifeBarrier.java index 8d25e0ce915b..620f2fd552af 100644 --- a/Mage.Sets/src/mage/cards/l/LashknifeBarrier.java +++ b/Mage.Sets/src/mage/cards/l/LashknifeBarrier.java @@ -71,13 +71,13 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE; + return event.getType() == EventType.DAMAGE_PERMANENT; } @Override public boolean applies(GameEvent event, Ability source, Game game) { Permanent creature = game.getPermanent(event.getTargetId()); - return creature != null && creature.isControlledBy(source.getControllerId()); + return creature != null && creature.isPlaneswalker() && creature.isControlledBy(source.getControllerId()); } } diff --git a/Mage.Sets/src/mage/cards/l/LastLaugh.java b/Mage.Sets/src/mage/cards/l/LastLaugh.java index 852a5960aec3..15e38cba7388 100644 --- a/Mage.Sets/src/mage/cards/l/LastLaugh.java +++ b/Mage.Sets/src/mage/cards/l/LastLaugh.java @@ -12,7 +12,7 @@ import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; diff --git a/Mage.Sets/src/mage/cards/l/LastRites.java b/Mage.Sets/src/mage/cards/l/LastRites.java index 245ccc571a7b..0d06036cdbcf 100644 --- a/Mage.Sets/src/mage/cards/l/LastRites.java +++ b/Mage.Sets/src/mage/cards/l/LastRites.java @@ -7,19 +7,14 @@ import mage.abilities.effects.common.discard.DiscardCardYouChooseTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.TargetController; import mage.filter.FilterCard; -import mage.filter.StaticFilters; import mage.filter.predicate.Predicates; import mage.game.Game; import mage.players.Player; -import mage.target.TargetCard; import mage.target.TargetPlayer; -import mage.target.common.TargetDiscard; import mage.target.targetpointer.FixedTarget; import java.util.UUID; @@ -74,12 +69,7 @@ public boolean apply(Game game, Ability source) { if (controller == null || targetPlayer == null) { return false; } - Cards cardsInHand = controller.getHand().copy(); - TargetCard target = new TargetDiscard( - 0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD_CARDS, controller.getId() - ); - controller.chooseTarget(Outcome.AIDontUseIt, cardsInHand, target, source, game); - int discardCount = controller.discard(new CardsImpl(target.getTargets()), false, source, game).size(); + int discardCount = controller.discard(0, Integer.MAX_VALUE, false, source, game).size(); FilterCard filter = new FilterCard((discardCount > 1 ? "" : "a") + " nonland card" + (discardCount > 1 ? "s" : "")); filter.add(Predicates.not(CardType.LAND.getPredicate())); diff --git a/Mage.Sets/src/mage/cards/l/LathlissDragonQueen.java b/Mage.Sets/src/mage/cards/l/LathlissDragonQueen.java index c97bc9b3ca20..d032a5b4f7b7 100644 --- a/Mage.Sets/src/mage/cards/l/LathlissDragonQueen.java +++ b/Mage.Sets/src/mage/cards/l/LathlissDragonQueen.java @@ -18,7 +18,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.permanent.token.DragonToken2; diff --git a/Mage.Sets/src/mage/cards/l/LavabrinkVenturer.java b/Mage.Sets/src/mage/cards/l/LavabrinkVenturer.java index ef18a42db35d..3b11d4b46715 100644 --- a/Mage.Sets/src/mage/cards/l/LavabrinkVenturer.java +++ b/Mage.Sets/src/mage/cards/l/LavabrinkVenturer.java @@ -12,7 +12,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.FilterObject; -import mage.filter.predicate.mageobject.ConvertedManaCostParityPredicate; +import mage.filter.predicate.mageobject.ManaValueParityPredicate; import mage.game.Game; import java.util.UUID; @@ -53,20 +53,20 @@ public LavabrinkVenturer copy() { class LavabrinkVenturerEffect extends GainAbilitySourceEffect { private static final FilterObject nullFilter = new FilterObject("nothing"); - private static final FilterObject oddFilter = new FilterObject("odd converted mana costs"); - private static final FilterObject evenFilter = new FilterObject("even converted mana costs"); + private static final FilterObject oddFilter = new FilterObject("odd mana values"); + private static final FilterObject evenFilter = new FilterObject("even mana values"); static { - nullFilter.add(ConvertedManaCostParityPredicate.ODD); - nullFilter.add(ConvertedManaCostParityPredicate.EVEN); - oddFilter.add(ConvertedManaCostParityPredicate.ODD); - evenFilter.add(ConvertedManaCostParityPredicate.EVEN); + nullFilter.add(ManaValueParityPredicate.ODD); + nullFilter.add(ManaValueParityPredicate.EVEN); + oddFilter.add(ManaValueParityPredicate.ODD); + evenFilter.add(ManaValueParityPredicate.EVEN); } LavabrinkVenturerEffect() { super(new ProtectionAbility(nullFilter)); this.ability.setRuleVisible(false); - staticText = "{this} has protection from each converted mana cost of the chosen value. (Zero is even.)"; + staticText = "{this} has protection from each mana value of the chosen quality. (Zero is even.)"; } private LavabrinkVenturerEffect(final LavabrinkVenturerEffect effect) { diff --git a/Mage.Sets/src/mage/cards/l/LaviniaAzoriusRenegade.java b/Mage.Sets/src/mage/cards/l/LaviniaAzoriusRenegade.java index f65102a3dd7b..9419111fda67 100644 --- a/Mage.Sets/src/mage/cards/l/LaviniaAzoriusRenegade.java +++ b/Mage.Sets/src/mage/cards/l/LaviniaAzoriusRenegade.java @@ -16,7 +16,7 @@ import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -62,7 +62,7 @@ class LaviniaAzoriusRenegadeReplacementEffect extends ContinuousRuleModifyingEff LaviniaAzoriusRenegadeReplacementEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); - staticText = "Each opponent can't cast noncreature spells with converted mana cost greater than the number of lands that player controls."; + staticText = "Each opponent can't cast noncreature spells with mana value greater than the number of lands that player controls."; } LaviniaAzoriusRenegadeReplacementEffect(final LaviniaAzoriusRenegadeReplacementEffect effect) { @@ -73,7 +73,7 @@ class LaviniaAzoriusRenegadeReplacementEffect extends ContinuousRuleModifyingEff public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source.getSourceId()); if (mageObject != null) { - return "You can't cast noncreature spells with converted mana cost greater than " + getLandCount(source, event, game) + " (" + mageObject.getIdName() + ")."; + return "You can't cast noncreature spells with mana value greater than " + getLandCount(source, event, game) + " (" + mageObject.getIdName() + ")."; } return null; } @@ -88,7 +88,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { if (game.getPlayer(source.getControllerId()).hasOpponent(event.getPlayerId(), game)) { FilterCard filter = new FilterCard(); filter.add(Predicates.not(CardType.CREATURE.getPredicate())); - filter.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, getLandCount(source, event, game))); + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, getLandCount(source, event, game))); Card card = game.getCard(event.getSourceId()); return card != null && filter.match(card, game); @@ -153,6 +153,6 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { - return "Whenever an opponent casts a spell, if no mana was spent to cast it, counter that spell"; + return "Whenever an opponent casts a spell, if no mana was spent to cast it, counter that spell."; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/l/LaviniaOfTheTenth.java b/Mage.Sets/src/mage/cards/l/LaviniaOfTheTenth.java index af6af9736270..9b58cae057f5 100644 --- a/Mage.Sets/src/mage/cards/l/LaviniaOfTheTenth.java +++ b/Mage.Sets/src/mage/cards/l/LaviniaOfTheTenth.java @@ -17,7 +17,7 @@ import mage.constants.TargetController; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; /** * @@ -25,11 +25,11 @@ */ public final class LaviniaOfTheTenth extends CardImpl { - private static final FilterPermanent filterDetain = new FilterPermanent("each nonland permanent your opponents control with converted mana cost 4 or less"); + private static final FilterPermanent filterDetain = new FilterPermanent("each nonland permanent your opponents control with mana value 4 or less"); static { filterDetain.add(TargetController.OPPONENT.getControllerPredicate()); filterDetain.add(Predicates.not(CardType.LAND.getPredicate())); - filterDetain.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 5)); + filterDetain.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 5)); } public LaviniaOfTheTenth (UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/l/LawRuneEnforcer.java b/Mage.Sets/src/mage/cards/l/LawRuneEnforcer.java index 1105305a9cc6..16d37b2d0acb 100644 --- a/Mage.Sets/src/mage/cards/l/LawRuneEnforcer.java +++ b/Mage.Sets/src/mage/cards/l/LawRuneEnforcer.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetPermanent; import java.util.UUID; @@ -24,10 +24,10 @@ public final class LawRuneEnforcer extends CardImpl { private static final FilterPermanent filter - = new FilterCreaturePermanent("creature with converted mana cost 2 or greater"); + = new FilterCreaturePermanent("creature with mana value 2 or greater"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 1)); + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 1)); } public LawRuneEnforcer(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/l/LazavTheMultifarious.java b/Mage.Sets/src/mage/cards/l/LazavTheMultifarious.java index 6454fddadb35..e5f78b3f6155 100644 --- a/Mage.Sets/src/mage/cards/l/LazavTheMultifarious.java +++ b/Mage.Sets/src/mage/cards/l/LazavTheMultifarious.java @@ -21,7 +21,7 @@ import mage.constants.Outcome; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; @@ -74,8 +74,8 @@ enum LazavTheMultifariousAdjuster implements TargetAdjuster { @Override public void adjustTargets(Ability ability, Game game) { int xValue = ability.getManaCostsToPay().getX(); - FilterCard filterCard = new FilterCreatureCard("creature card with converted mana cost " + xValue + " in your graveyard"); - filterCard.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, xValue)); + FilterCard filterCard = new FilterCreatureCard("creature card with mana value " + xValue + " in your graveyard"); + filterCard.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, xValue)); ability.getTargets().clear(); ability.getTargets().add(new TargetCardInYourGraveyard(filterCard)); } @@ -86,7 +86,7 @@ class LazavTheMultifariousEffect extends OneShotEffect { LazavTheMultifariousEffect() { super(Outcome.Copy); staticText = "{this} becomes a copy of target creature card " - + "in your graveyard with converted mana cost X, " + + "in your graveyard with mana value X, " + "except its name is Lazav, the Multifarious, " + "it's legendary in addition to its other types, " + "and it has this ability"; diff --git a/Mage.Sets/src/mage/cards/l/LeadershipVacuum.java b/Mage.Sets/src/mage/cards/l/LeadershipVacuum.java index de1d303adf79..98d1ab4538eb 100644 --- a/Mage.Sets/src/mage/cards/l/LeadershipVacuum.java +++ b/Mage.Sets/src/mage/cards/l/LeadershipVacuum.java @@ -11,7 +11,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.CommanderPredicate; +import mage.filter.predicate.mageobject.CommanderPredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; diff --git a/Mage.Sets/src/mage/cards/l/LeagueGuildmage.java b/Mage.Sets/src/mage/cards/l/LeagueGuildmage.java index 0dccbe71455c..e51f1a594b7a 100644 --- a/Mage.Sets/src/mage/cards/l/LeagueGuildmage.java +++ b/Mage.Sets/src/mage/cards/l/LeagueGuildmage.java @@ -16,7 +16,7 @@ import mage.constants.TargetController; import mage.filter.FilterSpell; import mage.filter.common.FilterInstantOrSorcerySpell; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.target.TargetSpell; import mage.target.targetadjustment.TargetAdjuster; @@ -27,7 +27,7 @@ */ public final class LeagueGuildmage extends CardImpl { - private static final FilterSpell filter = new FilterInstantOrSorcerySpell("instant or sorcery you control with converted mana cost X"); + private static final FilterSpell filter = new FilterInstantOrSorcerySpell("instant or sorcery you control with mana value X"); static { filter.add(TargetController.YOU.getControllerPredicate()); @@ -76,9 +76,9 @@ enum LeagueGuildmageAdjuster implements TargetAdjuster { @Override public void adjustTargets(Ability ability, Game game) { int xValue = ability.getManaCostsToPay().getX(); - FilterSpell spellFilter = new FilterInstantOrSorcerySpell("instant or sorcery you control with converted mana cost " + xValue); + FilterSpell spellFilter = new FilterInstantOrSorcerySpell("instant or sorcery you control with mana value " + xValue); spellFilter.add(TargetController.YOU.getControllerPredicate()); - spellFilter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, xValue)); + spellFilter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, xValue)); ability.getTargets().clear(); ability.addTarget(new TargetSpell(spellFilter)); } diff --git a/Mage.Sets/src/mage/cards/l/Leapfrog.java b/Mage.Sets/src/mage/cards/l/Leapfrog.java index 124a7980620e..b0f2cd6f660b 100644 --- a/Mage.Sets/src/mage/cards/l/Leapfrog.java +++ b/Mage.Sets/src/mage/cards/l/Leapfrog.java @@ -1,26 +1,27 @@ package mage.cards.l; -import java.util.List; -import java.util.UUID; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.keyword.FlyingAbility; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; import mage.game.Game; import mage.game.stack.Spell; import mage.watchers.common.SpellsCastWatcher; +import java.util.List; +import java.util.Objects; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class Leapfrog extends CardImpl { @@ -33,17 +34,12 @@ public Leapfrog(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(1); // Leapfrog has flying as long as you've cast an instant or sorcery spell this turn. - this.addAbility(new SimpleStaticAbility( - Zone.BATTLEFIELD, - new ConditionalContinuousEffect( - new GainAbilitySourceEffect( - FlyingAbility.getInstance(), - Duration.WhileOnBattlefield - ), LeapfrogCondition.instance, - "{this} has flying as long as you've cast " - + "an instant or sorcery spell this turn." - ) - ), new SpellsCastWatcher()); + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect( + FlyingAbility.getInstance(), Duration.WhileOnBattlefield + ), LeapfrogCondition.instance, "{this} has flying as long as " + + "you've cast an instant or sorcery spell this turn." + )), new SpellsCastWatcher()); } private Leapfrog(final Leapfrog card) { @@ -61,23 +57,16 @@ enum LeapfrogCondition implements Condition { @Override public boolean apply(Game game, Ability source) { - SpellsCastWatcher watcher - = game.getState().getWatcher( - SpellsCastWatcher.class - ); + SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class); if (watcher == null) { return false; } List spells = watcher.getSpellsCastThisTurn(source.getControllerId()); - if (spells == null) { - return false; - } - for (Spell spell : spells) { - if (!spell.getSourceId().equals(source.getSourceId()) - && spell.isInstantOrSorcery()) { - return true; - } - } - return false; + return spells != null && spells + .stream() + .filter(Objects::nonNull) + .filter(MageObject::isInstantOrSorcery) + .map(Spell::getSourceId) + .anyMatch(source.getSourceId()::equals); } } diff --git a/Mage.Sets/src/mage/cards/l/LeaveChance.java b/Mage.Sets/src/mage/cards/l/LeaveChance.java index a756848d9a59..53c989c86e42 100644 --- a/Mage.Sets/src/mage/cards/l/LeaveChance.java +++ b/Mage.Sets/src/mage/cards/l/LeaveChance.java @@ -1,23 +1,15 @@ package mage.cards.l; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.common.discard.DiscardAndDrawThatManyEffect; import mage.abilities.keyword.AftermathAbility; import mage.cards.CardSetInfo; -import mage.cards.CardsImpl; import mage.cards.SplitCard; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SpellAbilityType; import mage.constants.TargetController; import mage.filter.FilterPermanent; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.players.Player; -import mage.target.TargetCard; import mage.target.TargetPermanent; -import mage.target.common.TargetDiscard; import java.util.UUID; @@ -45,7 +37,7 @@ public LeaveChance(UUID ownerId, CardSetInfo setInfo) { getRightHalfCard().addAbility(new AftermathAbility().setRuleAtTheTop(true)); // Discard any number of cards, then draw that many cards. - getRightHalfCard().getSpellAbility().addEffect(new ChanceEffect()); + getRightHalfCard().getSpellAbility().addEffect(new DiscardAndDrawThatManyEffect(Integer.MAX_VALUE)); } private LeaveChance(final LeaveChance card) { @@ -57,33 +49,3 @@ public LeaveChance copy() { return new LeaveChance(this); } } - -class ChanceEffect extends OneShotEffect { - - ChanceEffect() { - super(Outcome.DrawCard); - this.staticText = "Discard any number of cards, then draw that many cards"; - } - - private ChanceEffect(final ChanceEffect effect) { - super(effect); - } - - @Override - public ChanceEffect copy() { - return new ChanceEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { - return false; - } - TargetCard target = new TargetDiscard(0, controller.getHand().size(), StaticFilters.FILTER_CARD_CARDS, controller.getId()); - controller.chooseTarget(outcome, controller.getHand(), target, source, game); - int amount = controller.discard(new CardsImpl(target.getTargets()), false, source, game).size(); - controller.drawCards(amount, source, game); - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/l/LeechBonder.java b/Mage.Sets/src/mage/cards/l/LeechBonder.java index e7035d561df4..c6a617def398 100644 --- a/Mage.Sets/src/mage/cards/l/LeechBonder.java +++ b/Mage.Sets/src/mage/cards/l/LeechBonder.java @@ -19,7 +19,7 @@ import mage.counters.Counter; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/l/LeechFanatic.java b/Mage.Sets/src/mage/cards/l/LeechFanatic.java new file mode 100644 index 000000000000..dd4f73ffbd00 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LeechFanatic.java @@ -0,0 +1,46 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MyTurnCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.hint.common.MyTurnHint; +import mage.abilities.keyword.LifelinkAbility; +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 LeechFanatic extends CardImpl { + + public LeechFanatic(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // As long as it's your turn, Leech Fanatic has lifelink. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(LifelinkAbility.getInstance(), Duration.WhileOnBattlefield), + MyTurnCondition.instance, "as long as it's your turn, {this} has lifelink" + )).addHint(MyTurnHint.instance)); + } + + private LeechFanatic(final LeechFanatic card) { + super(card); + } + + @Override + public LeechFanatic copy() { + return new LeechFanatic(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LeechingBite.java b/Mage.Sets/src/mage/cards/l/LeechingBite.java index 1bb7e7198f71..35fb9da8bf23 100644 --- a/Mage.Sets/src/mage/cards/l/LeechingBite.java +++ b/Mage.Sets/src/mage/cards/l/LeechingBite.java @@ -12,7 +12,7 @@ import mage.constants.Outcome; import mage.constants.SubLayer; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/l/LegacyOfTheBeloved.java b/Mage.Sets/src/mage/cards/l/LegacyOfTheBeloved.java index 022dd2de50d8..74046ce6721a 100644 --- a/Mage.Sets/src/mage/cards/l/LegacyOfTheBeloved.java +++ b/Mage.Sets/src/mage/cards/l/LegacyOfTheBeloved.java @@ -16,7 +16,7 @@ import mage.constants.Zone; import static mage.filter.StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -51,11 +51,11 @@ public LegacyOfTheBeloved copy() { class LegacyOfTheBelovedEffect extends OneShotEffect { - private static final FilterCreatureCard filter = new FilterCreatureCard("up to two creatures cards that each have a lower converted mana cost that sacrificied creature's converted mana cost"); + private static final FilterCreatureCard filter = new FilterCreatureCard("up to two creatures cards that each have a lower mana value that sacrificied creature's mana value"); public LegacyOfTheBelovedEffect() { super(Outcome.DrawCard); - this.staticText = "Search you library for up to two creatures cards that each have a lower converted mana cost that sacrificied creature's converted mana cost, reveal them and put them onto the battlefield, then shuffle you library"; + this.staticText = "Search you library for up to two creatures cards that each have a lower mana value that sacrificied creature's mana value, reveal them and put them onto the battlefield, then shuffle you library"; } public LegacyOfTheBelovedEffect(final LegacyOfTheBelovedEffect effect) { @@ -75,7 +75,7 @@ public boolean apply(Game game, Ability source) { if (cost instanceof SacrificeTargetCost) { Permanent p = (Permanent) game.getLastKnownInformation(((SacrificeTargetCost) cost).getPermanents().get(0).getId(), Zone.BATTLEFIELD); if (p != null) { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, p.getConvertedManaCost())); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, p.getManaValue())); TargetCardInLibrary target = new TargetCardInLibrary(0, 2, filter); Player player = game.getPlayer(source.getControllerId()); if (player != null && player.searchLibrary(target, source, game)) { diff --git a/Mage.Sets/src/mage/cards/l/Legerdemain.java b/Mage.Sets/src/mage/cards/l/Legerdemain.java index 165ddd4822c1..422d59c73c79 100644 --- a/Mage.Sets/src/mage/cards/l/Legerdemain.java +++ b/Mage.Sets/src/mage/cards/l/Legerdemain.java @@ -12,7 +12,7 @@ import mage.filter.predicate.ObjectSourcePlayer; import mage.filter.predicate.ObjectSourcePlayerPredicate; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.StackObject; diff --git a/Mage.Sets/src/mage/cards/l/LegionConquistador.java b/Mage.Sets/src/mage/cards/l/LegionConquistador.java index 1c1eb3eecaee..7ad23daa8671 100644 --- a/Mage.Sets/src/mage/cards/l/LegionConquistador.java +++ b/Mage.Sets/src/mage/cards/l/LegionConquistador.java @@ -37,7 +37,7 @@ public LegionConquistador(UUID ownerId, CardSetInfo setInfo) { // When Legion Conquistador enters the battlefield, you may search your library for any number of cards named Legion Conquistador, reveal them, put them into your hand, then shuffle your library TargetCardInLibrary target = new TargetCardInLibrary(0, Integer.MAX_VALUE, filter); Effect effect = new SearchLibraryPutInHandEffect(target, true, true); - effect.setText("you may search your library for any number of cards named Legion Conquistador, reveal them, put them into your hand, then shuffle your library"); + effect.setText("you may search your library for any number of cards named Legion Conquistador, reveal them, put them into your hand, then shuffle"); this.addAbility(new EntersBattlefieldTriggeredAbility(effect, true)); } diff --git a/Mage.Sets/src/mage/cards/l/LegionGuildmage.java b/Mage.Sets/src/mage/cards/l/LegionGuildmage.java index aceb775e5177..379c9785c83f 100644 --- a/Mage.Sets/src/mage/cards/l/LegionGuildmage.java +++ b/Mage.Sets/src/mage/cards/l/LegionGuildmage.java @@ -14,7 +14,7 @@ import mage.constants.CardType; import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/l/LegionsEnd.java b/Mage.Sets/src/mage/cards/l/LegionsEnd.java index c96c788c009c..6041696698de 100644 --- a/Mage.Sets/src/mage/cards/l/LegionsEnd.java +++ b/Mage.Sets/src/mage/cards/l/LegionsEnd.java @@ -13,7 +13,7 @@ import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterOpponentsCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -27,10 +27,10 @@ public final class LegionsEnd extends CardImpl { private static final FilterPermanent filter - = new FilterOpponentsCreaturePermanent("creature an opponent controls with converted mana cost 2 or less"); + = new FilterOpponentsCreaturePermanent("creature an opponent controls with mana value 2 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); } public LegionsEnd(UUID ownerId, CardSetInfo setInfo) { @@ -55,7 +55,7 @@ class LegionsEndEffect extends OneShotEffect { LegionsEndEffect() { super(Outcome.Benefit); - staticText = "Exile target creature an opponent controls with converted mana cost 2 or less " + + staticText = "Exile target creature an opponent controls with mana value 2 or less " + "and all other creatures that player controls with the same name as that creature. " + "Then that player reveals their hand and exiles all cards with that name from their hand and graveyard."; } diff --git a/Mage.Sets/src/mage/cards/l/LegionsInitiative.java b/Mage.Sets/src/mage/cards/l/LegionsInitiative.java index 85d6dffd82d4..228eecf1a867 100644 --- a/Mage.Sets/src/mage/cards/l/LegionsInitiative.java +++ b/Mage.Sets/src/mage/cards/l/LegionsInitiative.java @@ -1,5 +1,6 @@ package mage.cards.l; +import mage.MageObject; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -7,32 +8,43 @@ import mage.abilities.common.delayed.AtTheBeginOfCombatDelayedTriggeredAbility; import mage.abilities.costs.common.ExileSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; 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.constants.*; -import mage.filter.FilterPermanent; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.ExileZone; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; +import mage.players.Player; +import mage.target.targetpointer.FixedTargets; +import mage.util.CardUtil; +import java.util.List; +import java.util.Objects; import java.util.UUID; +import java.util.stream.Collectors; /** * @author LevelX2 */ public final class LegionsInitiative extends CardImpl { - private static final FilterCreaturePermanent filterRedCreature = new FilterCreaturePermanent("Red creatures"); - private static final FilterCreaturePermanent filterWhiteCreature = new FilterCreaturePermanent("White creatures"); + private static final FilterCreaturePermanent filterRedCreature + = new FilterCreaturePermanent("Red creatures"); + private static final FilterCreaturePermanent filterWhiteCreature + = new FilterCreaturePermanent("White creatures"); static { filterRedCreature.add(new ColorPredicate(ObjectColor.RED)); @@ -43,13 +55,20 @@ public LegionsInitiative(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{R}{W}"); // Red creatures you control get +1/+0. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 0, Duration.WhileOnBattlefield, filterRedCreature))); + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 1, 0, Duration.WhileOnBattlefield, filterRedCreature + ))); // White creatures you control get +0/+1. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(0, 1, Duration.WhileOnBattlefield, filterWhiteCreature))); + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 0, 1, Duration.WhileOnBattlefield, filterWhiteCreature + ))); // {R}{W}, Exile Legion's Initiative: Exile all creatures you control. At the beginning of the next combat, return those cards to the battlefield under their owner's control and those creatures gain haste until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new LegionsInitiativeExileEffect(), new ManaCostsImpl("{R}{W}")); + Ability ability = new SimpleActivatedAbility(new LegionsInitiativeExileEffect(), new ManaCostsImpl("{R}{W}")); + ability.addEffect(new CreateDelayedTriggeredAbilityEffect( + new AtTheBeginOfCombatDelayedTriggeredAbility(new LegionsInitiativeReturnFromExileEffect()) + )); ability.addCost(new ExileSourceCost()); this.addAbility(ability); } @@ -66,38 +85,31 @@ public LegionsInitiative copy() { class LegionsInitiativeExileEffect extends OneShotEffect { - private static final FilterPermanent filter = new FilterPermanent("all creatures you control"); - - static { - filter.add(TargetController.YOU.getControllerPredicate()); - filter.add(CardType.CREATURE.getPredicate()); - } - - public LegionsInitiativeExileEffect() { + LegionsInitiativeExileEffect() { super(Outcome.Detriment); - staticText = "Exile all creatures you control. At the beginning of the next combat, return those cards to the battlefield under their owner's control and those creatures gain haste until end of turn"; + staticText = "Exile all creatures you control."; } - public LegionsInitiativeExileEffect(final LegionsInitiativeExileEffect effect) { + private LegionsInitiativeExileEffect(final LegionsInitiativeExileEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { - boolean creatureExiled = false; - for (Permanent creature : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - if (creature != null) { - if (creature.moveToExile(source.getSourceId(), "Legion's Initiative", source, game)) { - creatureExiled = true; - } - } - } - if (creatureExiled) { - //create delayed triggered ability - AtTheBeginOfCombatDelayedTriggeredAbility delayedAbility = new AtTheBeginOfCombatDelayedTriggeredAbility(new LegionsInitiativeReturnFromExileEffect()); - game.addDelayedTriggeredAbility(delayedAbility, source); + Player player = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObject(game); + if (player == null || sourceObject == null) { + return false; } - return true; + Cards cards = new CardsImpl(); + game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_CONTROLLED_CREATURE, + source.getControllerId(), source.getSourceId(), game + ).stream().filter(Objects::nonNull).forEach(cards::add); + return player.moveCardsToExile( + cards.getCards(game), source, game, true, + CardUtil.getExileZoneId(game, source), sourceObject.getIdName() + ); } @Override @@ -108,12 +120,13 @@ public LegionsInitiativeExileEffect copy() { class LegionsInitiativeReturnFromExileEffect extends OneShotEffect { - public LegionsInitiativeReturnFromExileEffect() { + LegionsInitiativeReturnFromExileEffect() { super(Outcome.PutCardInPlay); - staticText = "At the beginning of the next combat, return those cards to the battlefield under their owner's control and those creatures gain haste until end of turn"; + staticText = "return those cards to the battlefield under their owner's control " + + "and those creatures gain haste until end of turn"; } - public LegionsInitiativeReturnFromExileEffect(final LegionsInitiativeReturnFromExileEffect effect) { + private LegionsInitiativeReturnFromExileEffect(final LegionsInitiativeReturnFromExileEffect effect) { super(effect); } @@ -124,25 +137,20 @@ public LegionsInitiativeReturnFromExileEffect copy() { @Override public boolean apply(Game game, Ability source) { - ExileZone exile = game.getExile().getExileZone(source.getSourceId()); - if (exile != null) { - exile = exile.copy(); - for (UUID cardId : exile) { - Card card = game.getCard(cardId); - if (card != null) { - card.moveToZone(Zone.BATTLEFIELD, source, game, false); - Permanent returnedCreature = game.getPermanent(cardId); - if (returnedCreature != null) { - ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(returnedCreature.getId())); - game.addEffect(effect, source); - } - } - } - game.getExile().getExileZone(source.getSourceId()).clear(); - return true; + Player player = game.getPlayer(source.getControllerId()); + ExileZone exile = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + if (player == null || exile == null || exile.isEmpty()) { + return false; + } + Cards cards = new CardsImpl(exile); + player.moveCards(cards, Zone.BATTLEFIELD, source, game); + List permanents = cards.stream().map(game::getPermanent).filter(Objects::nonNull).collect(Collectors.toList()); + if (permanents.isEmpty()) { + return false; } - return false; + game.addEffect(new GainAbilityTargetEffect( + HasteAbility.getInstance(), Duration.EndOfTurn + ).setTargetPointer(new FixedTargets(permanents, game)), source); + return true; } - } diff --git a/Mage.Sets/src/mage/cards/l/LensOfClarity.java b/Mage.Sets/src/mage/cards/l/LensOfClarity.java index 528f64cdad96..12f9baa99521 100644 --- a/Mage.Sets/src/mage/cards/l/LensOfClarity.java +++ b/Mage.Sets/src/mage/cards/l/LensOfClarity.java @@ -17,7 +17,7 @@ import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.other.FaceDownPredicate; +import mage.filter.predicate.card.FaceDownPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/l/LeoninLightscribe.java b/Mage.Sets/src/mage/cards/l/LeoninLightscribe.java new file mode 100644 index 000000000000..0ee51b0eff0c --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LeoninLightscribe.java @@ -0,0 +1,39 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +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 LeoninLightscribe extends CardImpl { + + public LeoninLightscribe(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.CAT); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, creatures you control get +1/+1 until end of turn, + this.addAbility(new MagecraftAbility(new BoostControlledEffect(1, 1, Duration.EndOfTurn))); + } + + private LeoninLightscribe(final LeoninLightscribe card) { + super(card); + } + + @Override + public LeoninLightscribe copy() { + return new LeoninLightscribe(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LeoninSquire.java b/Mage.Sets/src/mage/cards/l/LeoninSquire.java index 876e6ff4195e..eac7cfde6be4 100644 --- a/Mage.Sets/src/mage/cards/l/LeoninSquire.java +++ b/Mage.Sets/src/mage/cards/l/LeoninSquire.java @@ -12,7 +12,7 @@ import mage.constants.SubType; import mage.constants.ComparisonType; import mage.filter.common.FilterArtifactCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInYourGraveyard; /** @@ -21,9 +21,9 @@ */ public final class LeoninSquire extends CardImpl { - private static final FilterArtifactCard filter = new FilterArtifactCard("target artifact card with converted mana cost 1 or less from your graveyard"); + private static final FilterArtifactCard filter = new FilterArtifactCard("artifact card with mana value 1 or less from your graveyard"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 2)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 2)); } public LeoninSquire(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/l/LetterOfAcceptance.java b/Mage.Sets/src/mage/cards/l/LetterOfAcceptance.java new file mode 100644 index 000000000000..af2e54c53b17 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LetterOfAcceptance.java @@ -0,0 +1,44 @@ +package mage.cards.l; + +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.DrawCardSourceControllerEffect; +import mage.abilities.mana.AnyColorManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LetterOfAcceptance extends CardImpl { + + public LetterOfAcceptance(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + // {T}: Add one mana of any color. + this.addAbility(new AnyColorManaAbility()); + + // {2}, {T}, Sacrifice Letter of Acceptance: Draw a card. + Ability ability = new SimpleActivatedAbility( + new DrawCardSourceControllerEffect(1), new GenericManaCost(2) + ); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeSourceCost()); + this.addAbility(ability); + } + + private LetterOfAcceptance(final LetterOfAcceptance card) { + super(card); + } + + @Override + public LetterOfAcceptance copy() { + return new LetterOfAcceptance(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LeylineInvocation.java b/Mage.Sets/src/mage/cards/l/LeylineInvocation.java new file mode 100644 index 000000000000..426d3dcb8021 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LeylineInvocation.java @@ -0,0 +1,36 @@ +package mage.cards.l; + +import mage.abilities.dynamicvalue.common.LandsYouControlCount; +import mage.abilities.hint.common.LandsYouControlHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.permanent.token.QuandrixToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LeylineInvocation extends CardImpl { + + public LeylineInvocation(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{G}"); + + // Create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it, where X is the number of lands you control. + this.getSpellAbility().addEffect(QuandrixToken.getEffect( + LandsYouControlCount.instance, "Put X +1/+1 counters on it, " + + "where X is the number of lands you control" + )); + this.getSpellAbility().addHint(LandsYouControlHint.instance); + } + + private LeylineInvocation(final LeylineInvocation card) { + super(card); + } + + @Override + public LeylineInvocation copy() { + return new LeylineInvocation(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LeylineOfCombustion.java b/Mage.Sets/src/mage/cards/l/LeylineOfCombustion.java index 6f5f49d017bc..cc442623c376 100644 --- a/Mage.Sets/src/mage/cards/l/LeylineOfCombustion.java +++ b/Mage.Sets/src/mage/cards/l/LeylineOfCombustion.java @@ -12,8 +12,11 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.stack.StackObject; +import mage.players.Player; import mage.target.targetpointer.FixedTarget; +import java.util.HashSet; +import java.util.Set; import java.util.UUID; /** @@ -64,29 +67,33 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { StackObject sourceObject = game.getStack().getStackObject(event.getSourceId()); - if (sourceObject == null || this.controllerId.equals(sourceObject.getControllerId())) { + if (sourceObject == null) { return false; } - if (sourceObject - .getStackAbility() - .getTargets() - .stream() - .noneMatch(target -> target - .getTargets() - .stream() - .anyMatch(targetId -> { - if (this.controllerId.equals(targetId)) { - return true; - } - Permanent permanent = game.getPermanent(targetId); - return permanent != null && permanent.getControllerId().equals(this.controllerId); - }) - )) { + Player targetter = game.getPlayer(event.getPlayerId()); + if (targetter == null || !targetter.hasOpponent(this.controllerId, game)) { return false; } + if (!event.getTargetId().equals(this.controllerId)) { + Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); + if (permanent == null || !permanent.isControlledBy(this.controllerId)) { + return false; + } + } + // If a spell targets you and/or a permanent you control multiple times, + // or if a spell targets you and one or more permanents you control, + // Leyline of Combustion’s triggered ability triggers once. + Set sourceObjects = (Set) game.getState().getValue("sourceObjects" + this.id); + if (sourceObjects == null) { + sourceObjects = new HashSet<>(); + } + if (!sourceObjects.add(sourceObject.getId())) { + return false; + } + game.getState().setValue("sourceObjects" + this.id, sourceObjects); this.getEffects().clear(); Effect effect = new DamageTargetEffect(2); - effect.setTargetPointer(new FixedTarget(sourceObject.getControllerId(), game)); + effect.setTargetPointer(new FixedTarget(event.getPlayerId(), game)); this.addEffect(effect); return true; } diff --git a/Mage.Sets/src/mage/cards/l/LeylinePhantom.java b/Mage.Sets/src/mage/cards/l/LeylinePhantom.java index 7dbd70df82b9..8fd8e7f96519 100644 --- a/Mage.Sets/src/mage/cards/l/LeylinePhantom.java +++ b/Mage.Sets/src/mage/cards/l/LeylinePhantom.java @@ -58,7 +58,8 @@ public LeylinePhantomTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER || event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT + || event.getType() == GameEvent.EventType.DAMAGED_PLAYER; } @Override diff --git a/Mage.Sets/src/mage/cards/l/Liberate.java b/Mage.Sets/src/mage/cards/l/Liberate.java index 657282f18f76..dab284fb181f 100644 --- a/Mage.Sets/src/mage/cards/l/Liberate.java +++ b/Mage.Sets/src/mage/cards/l/Liberate.java @@ -1,17 +1,18 @@ package mage.cards.l; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.targetpointer.FixedTarget; @@ -42,29 +43,31 @@ public Liberate copy() { class LiberateEffect extends OneShotEffect { - public LiberateEffect() { + LiberateEffect() { super(Outcome.Detriment); - staticText = "exile target creature you control. Return that card to the battlefield under its owner's control at the beginning of the next end step"; + staticText = "exile target creature you control. Return that card to the battlefield " + + "under its owner's control at the beginning of the next end step"; } - public LiberateEffect(final LiberateEffect effect) { + private LiberateEffect(final LiberateEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanent(source.getFirstTarget()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (permanent != null && sourceObject != null) { - if (permanent.moveToExile(source.getSourceId(), sourceObject.getIdName(), source, game)) { - Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); - effect.setText("Return that card to the battlefield under its owner's control at the beginning of the next end step"); - effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); - game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source); - return true; - } + if (player == null || permanent == null) { + return false; } - return false; + Card card = permanent.getMainCard(); + player.moveCards(permanent, Zone.EXILED, source, game); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility( + new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false) + .setText("Return that card to the battlefield under its owner's control at the beginning of the next end step") + .setTargetPointer(new FixedTarget(card, game)) + ), source); + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/l/Lichenthrope.java b/Mage.Sets/src/mage/cards/l/Lichenthrope.java index 8d9d96d0fe28..da95aba932d6 100644 --- a/Mage.Sets/src/mage/cards/l/Lichenthrope.java +++ b/Mage.Sets/src/mage/cards/l/Lichenthrope.java @@ -18,7 +18,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; -import mage.game.events.DamageCreatureEvent; +import mage.game.events.DamageEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -66,7 +66,7 @@ class LichenthropeEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - DamageCreatureEvent damageEvent = (DamageCreatureEvent) event; + DamageEvent damageEvent = (DamageEvent) event; Permanent p = game.getPermanent(source.getSourceId()); if (p != null) { p.addCounters(CounterType.M1M1.createInstance(damageEvent.getAmount()), source.getControllerId(), source, game); @@ -76,7 +76,7 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/l/LichsMirror.java b/Mage.Sets/src/mage/cards/l/LichsMirror.java index 4ea3d25c5dca..84c5c7858acb 100644 --- a/Mage.Sets/src/mage/cards/l/LichsMirror.java +++ b/Mage.Sets/src/mage/cards/l/LichsMirror.java @@ -11,7 +11,7 @@ import mage.cards.CardsImpl; import mage.constants.*; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/l/LiciaSanguineTribune.java b/Mage.Sets/src/mage/cards/l/LiciaSanguineTribune.java index d43b1d685136..3e3a919f1524 100644 --- a/Mage.Sets/src/mage/cards/l/LiciaSanguineTribune.java +++ b/Mage.Sets/src/mage/cards/l/LiciaSanguineTribune.java @@ -7,8 +7,7 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.MyTurnCondition; import mage.abilities.costs.common.PayLifeCost; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.ControllerGotLifeCount; import mage.abilities.effects.common.cost.CostModificationEffectImpl; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.hint.common.MyTurnHint; @@ -40,7 +39,9 @@ public LiciaSanguineTribune(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(4); // Licia, Sanguine Tribune costs 1 less to cast for each 1 life you gained this turn. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new LiciaSanguineTribuneCostReductionEffect()), new PlayerGainedLifeWatcher()); + this.addAbility(new SimpleStaticAbility( + Zone.ALL, new LiciaSanguineTribuneCostReductionEffect() + ).addHint(ControllerGotLifeCount.getHint()), new PlayerGainedLifeWatcher()); // First strike this.addAbility(FirstStrikeAbility.getInstance()); @@ -49,8 +50,11 @@ public LiciaSanguineTribune(UUID ownerId, CardSetInfo setInfo) { this.addAbility(LifelinkAbility.getInstance()); // Pay 5 life: Put three +1/+1 counters on Licia. Activate this ability only on your turn and only once each turn. - this.addAbility(new LimitedTimesPerTurnActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance(3)), new PayLifeCost(5), 1, MyTurnCondition.instance) - .addHint(MyTurnHint.instance)); + this.addAbility(new LimitedTimesPerTurnActivatedAbility( + Zone.BATTLEFIELD, + new AddCountersSourceEffect(CounterType.P1P1.createInstance(3)), + new PayLifeCost(5), 1, MyTurnCondition.instance + ).addHint(MyTurnHint.instance)); } private LiciaSanguineTribune(final LiciaSanguineTribune card) { @@ -67,7 +71,7 @@ class LiciaSanguineTribuneCostReductionEffect extends CostModificationEffectImpl LiciaSanguineTribuneCostReductionEffect() { super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "{this} costs {1} less to cast for each 1 life you have gained this turn."; + staticText = "this spell costs {1} less to cast for each 1 life you gained this turn."; } LiciaSanguineTribuneCostReductionEffect(LiciaSanguineTribuneCostReductionEffect effect) { @@ -78,8 +82,7 @@ class LiciaSanguineTribuneCostReductionEffect extends CostModificationEffectImpl public boolean apply(Game game, Ability source, Ability abilityToModify) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { - DynamicValue reductionAmount = new YouGainLifeCount(); - CardUtil.reduceCost(abilityToModify, reductionAmount.calculate(game, source, this)); + CardUtil.reduceCost(abilityToModify, ControllerGotLifeCount.instance.calculate(game, source, this)); return true; } return false; @@ -98,34 +101,3 @@ public LiciaSanguineTribuneCostReductionEffect copy() { return new LiciaSanguineTribuneCostReductionEffect(this); } } - -class YouGainLifeCount implements DynamicValue { - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - return this.calculate(game, sourceAbility.getControllerId()); - } - - public int calculate(Game game, UUID controllerId) { - PlayerGainedLifeWatcher watcher = game.getState().getWatcher(PlayerGainedLifeWatcher.class); - if (watcher != null) { - return watcher.getLifeGained(controllerId); - } - return 0; - } - - @Override - public YouGainLifeCount copy() { - return new YouGainLifeCount(); - } - - @Override - public String toString() { - return "X"; - } - - @Override - public String getMessage() { - return "the total life you gained this turn"; - } -} diff --git a/Mage.Sets/src/mage/cards/l/LiegeOfThePit.java b/Mage.Sets/src/mage/cards/l/LiegeOfThePit.java index 30ea51d9c37b..c38c7f76f896 100644 --- a/Mage.Sets/src/mage/cards/l/LiegeOfThePit.java +++ b/Mage.Sets/src/mage/cards/l/LiegeOfThePit.java @@ -12,7 +12,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/l/LiegeOfTheTangle.java b/Mage.Sets/src/mage/cards/l/LiegeOfTheTangle.java index 3f9239bbc116..c8a7ca7b414c 100644 --- a/Mage.Sets/src/mage/cards/l/LiegeOfTheTangle.java +++ b/Mage.Sets/src/mage/cards/l/LiegeOfTheTangle.java @@ -156,7 +156,7 @@ public LiegeOfTheTangleEffect copy() { public boolean hasLayer(Layer layer) { return layer == Layer.PTChangingEffects_7 || layer == Layer.ColorChangingEffects_5 - || layer == layer.TypeChangingEffects_4; + || layer == Layer.TypeChangingEffects_4; } } diff --git a/Mage.Sets/src/mage/cards/l/LieutenantsOfTheGuard.java b/Mage.Sets/src/mage/cards/l/LieutenantsOfTheGuard.java index 7b97f5c80d58..afe700eca806 100644 --- a/Mage.Sets/src/mage/cards/l/LieutenantsOfTheGuard.java +++ b/Mage.Sets/src/mage/cards/l/LieutenantsOfTheGuard.java @@ -1,14 +1,12 @@ package mage.cards.l; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.CouncilsDilemmaVoteEffect; -import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.choices.TwoChoiceVote; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; @@ -16,11 +14,11 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.SoldierToken; -import mage.players.Player; + +import java.util.UUID; /** - * - * @author JRHerlehy + * @author JRHerlehy, TheElk801 */ public final class LieutenantsOfTheGuard extends CardImpl { @@ -35,7 +33,9 @@ public LieutenantsOfTheGuard(UUID ownerId, CardSetInfo setInfo) { // Council's dilemma — When Lieutenants of the Guard enters the battlefield, starting with you, // each player votes for strength or numbers. Put a +1/+1 counter on Lieutenants of the Guard for each // strength vote and put a 1/1 white Soldier creature token onto the battlefield for each numbers vote. - this.addAbility(new EntersBattlefieldTriggeredAbility(new LieutenantsOfTheGuardDilemmaEffect(), false, "Council's dilemma — ")); + this.addAbility(new EntersBattlefieldTriggeredAbility( + new LieutenantsOfTheGuardEffect(), false, "Council's dilemma — " + )); } private LieutenantsOfTheGuard(final LieutenantsOfTheGuard card) { @@ -48,47 +48,40 @@ public LieutenantsOfTheGuard copy() { } } -class LieutenantsOfTheGuardDilemmaEffect extends CouncilsDilemmaVoteEffect { +class LieutenantsOfTheGuardEffect extends OneShotEffect { - public LieutenantsOfTheGuardDilemmaEffect() { + LieutenantsOfTheGuardEffect() { super(Outcome.Benefit); - this.staticText = "starting with you, each player votes for strength or numbers. Put a +1/+1 counter on {this} for each strength vote and put a 1/1 white Soldier creature token onto the battlefield for each numbers vote."; + this.staticText = "starting with you, each player votes for strength or numbers. " + + "Put a +1/+1 counter on {this} for each strength vote " + + "and create a 1/1 white Soldier creature token for each numbers vote."; } - public LieutenantsOfTheGuardDilemmaEffect(final LieutenantsOfTheGuardDilemmaEffect effect) { + private LieutenantsOfTheGuardEffect(final LieutenantsOfTheGuardEffect effect) { super(effect); } @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - - //If no controller, exit out here and do not vote. - if (controller == null) { - return false; - } - - this.vote("strength", "numbers", controller, game, source); - - Permanent permanent = game.getPermanent(source.getSourceId()); - - //Strength Votes - //If strength received zero votes or the permanent is no longer on the battlefield, do not attempt to put P1P1 counters on it. - if (voteOneCount > 0 && permanent != null) { - permanent.addCounters(CounterType.P1P1.createInstance(voteOneCount), source.getControllerId(), source, game); - } - - //Numbers Votes - if (voteTwoCount > 0) { - Effect tokenEffect = new CreateTokenEffect(new SoldierToken(), voteTwoCount); - tokenEffect.apply(game, source); - } - - return true; + public LieutenantsOfTheGuardEffect copy() { + return new LieutenantsOfTheGuardEffect(this); } @Override - public LieutenantsOfTheGuardDilemmaEffect copy() { - return new LieutenantsOfTheGuardDilemmaEffect(this); + public boolean apply(Game game, Ability source) { + // Outcome.Benefit - AI will boost all the time (Strength choice) + // TODO: add AI hint logic in the choice method, see Tyrant's Choice as example + TwoChoiceVote vote = new TwoChoiceVote("Strength (+1/+1 counter)", "Numbers (1/1 token)", Outcome.Benefit); + vote.doVotes(source, game); + + int strengthCount = vote.getVoteCount(true); + int numbersCount = vote.getVoteCount(false); + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (strengthCount > 0 && permanent != null) { + permanent.addCounters(CounterType.P1P1.createInstance(strengthCount), source.getControllerId(), source, game); + } + if (numbersCount > 0) { + new SoldierToken().putOntoBattlefield(numbersCount, game, source, source.getControllerId()); + } + return strengthCount + numbersCount > 0; } } diff --git a/Mage.Sets/src/mage/cards/l/LifeDeath.java b/Mage.Sets/src/mage/cards/l/LifeDeath.java index 790e36ac15ed..a4e61f26fc4d 100644 --- a/Mage.Sets/src/mage/cards/l/LifeDeath.java +++ b/Mage.Sets/src/mage/cards/l/LifeDeath.java @@ -58,7 +58,7 @@ class DeathEffect extends OneShotEffect { public DeathEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "return target creature card from your graveyard to the battlefield. You lose life equal to its converted mana cost"; + this.staticText = "return target creature card from your graveyard to the battlefield. You lose life equal to its mana value"; } public DeathEffect(final DeathEffect effect) { @@ -79,7 +79,7 @@ public boolean apply(Game game, Ability source) { if (game.getState().getZone(creatureCard.getId()) == Zone.GRAVEYARD) { controller.moveCards(creatureCard, Zone.BATTLEFIELD, source, game); } - controller.loseLife(creatureCard.getConvertedManaCost(), game, source, false); + controller.loseLife(creatureCard.getManaValue(), game, source, false); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/l/LifeMatrix.java b/Mage.Sets/src/mage/cards/l/LifeMatrix.java index 13c5bf2b1daa..842430660a96 100644 --- a/Mage.Sets/src/mage/cards/l/LifeMatrix.java +++ b/Mage.Sets/src/mage/cards/l/LifeMatrix.java @@ -38,7 +38,7 @@ public LifeMatrix(UUID ownerId, CardSetInfo setInfo) { new GenericManaCost(4), new IsStepCondition(PhaseStep.UPKEEP), "Put a matrix counter on target creature and " + "that creature gains “Remove a matrix counter from this creature: " - + "Regenerate this creature.” Activate this ability only during your upkeep."); + + "Regenerate this creature.” Activate only during your upkeep."); Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new RemoveCountersSourceCost(CounterType.MATRIX.createInstance())); diff --git a/Mage.Sets/src/mage/cards/l/LifesFinale.java b/Mage.Sets/src/mage/cards/l/LifesFinale.java index a15ed1d2d21c..87dae2f204c9 100644 --- a/Mage.Sets/src/mage/cards/l/LifesFinale.java +++ b/Mage.Sets/src/mage/cards/l/LifesFinale.java @@ -47,7 +47,7 @@ class LifesFinaleEffect extends OneShotEffect { public LifesFinaleEffect() { super(Outcome.DestroyPermanent); - staticText = "Destroy all creatures, then search target opponent's library for up to three creature cards and put them into their graveyard. Then that player shuffles their library"; + staticText = "Destroy all creatures, then search target opponent's library for up to three creature cards and put them into their graveyard. Then that player shuffles"; } public LifesFinaleEffect(final LifesFinaleEffect effect) { diff --git a/Mage.Sets/src/mage/cards/l/LightOfSanction.java b/Mage.Sets/src/mage/cards/l/LightOfSanction.java index 9e7b26ca1920..af50bd818a21 100644 --- a/Mage.Sets/src/mage/cards/l/LightOfSanction.java +++ b/Mage.Sets/src/mage/cards/l/LightOfSanction.java @@ -54,9 +54,9 @@ public LightOfSanctionEffect(LightOfSanctionEffect effect) { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.DAMAGE_CREATURE) { + if (event.getType() == GameEvent.EventType.DAMAGE_PERMANENT) { Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.isControlledBy(source.getControllerId())) { + if (permanent != null && permanent.isCreature() && permanent.isControlledBy(source.getControllerId())) { MageObject damageSource = game.getObject(event.getSourceId()); if (damageSource instanceof Controllable) { return ((Controllable) damageSource).isControlledBy(source.getControllerId()); diff --git a/Mage.Sets/src/mage/cards/l/Lightsaber.java b/Mage.Sets/src/mage/cards/l/Lightsaber.java index 0b83f493286f..e280ef9ba8f0 100644 --- a/Mage.Sets/src/mage/cards/l/Lightsaber.java +++ b/Mage.Sets/src/mage/cards/l/Lightsaber.java @@ -3,6 +3,7 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.CostAdjuster; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.InfoEffect; import mage.abilities.effects.common.continuous.BoostEquippedEffect; @@ -11,15 +12,17 @@ import mage.abilities.keyword.FirstStrikeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.SubType; import mage.game.Game; import mage.game.permanent.Permanent; import mage.util.CardUtil; +import java.util.Objects; import java.util.UUID; /** - * * @author Styxo */ public final class Lightsaber extends CardImpl { @@ -29,25 +32,18 @@ public Lightsaber(UUID ownerId, CardSetInfo setInfo) { this.subtype.add(SubType.EQUIPMENT); // Equiped creature gets +1/+0 and has firsttrike - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(1, 0))); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(FirstStrikeAbility.getInstance(), AttachmentType.EQUIPMENT))); + this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(1, 0))); + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( + FirstStrikeAbility.getInstance(), AttachmentType.EQUIPMENT + ).setText("and has first strike"))); // Equip 3 - this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(3))); + Ability ability = new EquipAbility(3); + ability.setCostAdjuster(LightsaberAdjuster.instance); + this.addAbility(ability); // Lightsaber's equip ability costs {1} if it targets a Jedi or Sith. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new InfoEffect("{this}'s equip ability costs {1} if it targets a Jedi or Sith"))); - - } - - @Override - public void adjustCosts(Ability ability, Game game) { - if (ability instanceof EquipAbility) { - Permanent targetCreature = game.getPermanent(ability.getTargets().getFirstTarget()); - if (targetCreature != null && (targetCreature.hasSubtype(SubType.SITH, game) || targetCreature.hasSubtype(SubType.JEDI, game))) { - CardUtil.increaseCost(ability, 1 - ability.getManaCostsToPay().convertedManaCost()); - } - } + this.addAbility(new SimpleStaticAbility(new InfoEffect("{this}'s equip ability costs {1} if it targets a Jedi or Sith"))); } private Lightsaber(final Lightsaber card) { @@ -59,3 +55,30 @@ public Lightsaber copy() { return new Lightsaber(this); } } + +enum LightsaberAdjuster implements CostAdjuster { + instance; + + @Override + public void adjustCosts(Ability ability, Game game) { + if (game.inCheckPlayableState()) { + if (CardUtil + .getAllPossibleTargets(ability, game) + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .noneMatch(permanent -> permanent.hasSubtype(SubType.SITH, game) + || permanent.hasSubtype(SubType.JEDI, game))) { + return; + } + } else { + Permanent permanent = game.getPermanent(ability.getFirstTarget()); + if (permanent == null || !(permanent.hasSubtype(SubType.SITH, game) + || permanent.hasSubtype(SubType.JEDI, game))) { + return; + } + } + ability.getCosts().clear(); + ability.addCost(new GenericManaCost(1)); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/l/LilianaDefiantNecromancer.java b/Mage.Sets/src/mage/cards/l/LilianaDefiantNecromancer.java index 836670fe3b3e..53587507f17d 100644 --- a/Mage.Sets/src/mage/cards/l/LilianaDefiantNecromancer.java +++ b/Mage.Sets/src/mage/cards/l/LilianaDefiantNecromancer.java @@ -1,6 +1,5 @@ package mage.cards.l; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; @@ -11,23 +10,27 @@ import mage.abilities.effects.common.discard.DiscardEachPlayerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.command.emblems.LilianaDefiantNecromancerEmblem; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetadjustment.TargetAdjuster; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class LilianaDefiantNecromancer extends CardImpl { - protected static final FilterCreatureCard filter = new FilterCreatureCard("nonlegendary creature with converted mana cost X from your graveyard"); + protected static final FilterCreatureCard filter = new FilterCreatureCard("nonlegendary creature card with mana value X from your graveyard"); static { filter.add(Predicates.not(SuperType.LEGENDARY.getPredicate())); @@ -78,7 +81,7 @@ public void adjustTargets(Ability ability, Game game) { } } FilterCard newFilter = LilianaDefiantNecromancer.filter.copy(); - newFilter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, cmc)); + newFilter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, cmc)); ability.getTargets().clear(); ability.addTarget(new TargetCardInYourGraveyard(newFilter)); } diff --git a/Mage.Sets/src/mage/cards/l/LilianaHereticalHealer.java b/Mage.Sets/src/mage/cards/l/LilianaHereticalHealer.java index bf4a19e73fdf..f6423ff4b867 100644 --- a/Mage.Sets/src/mage/cards/l/LilianaHereticalHealer.java +++ b/Mage.Sets/src/mage/cards/l/LilianaHereticalHealer.java @@ -17,7 +17,7 @@ import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.permanent.token.ZombieToken; diff --git a/Mage.Sets/src/mage/cards/l/LilianasShade.java b/Mage.Sets/src/mage/cards/l/LilianasShade.java index 23bcdbf3745f..7b612d17bbb0 100644 --- a/Mage.Sets/src/mage/cards/l/LilianasShade.java +++ b/Mage.Sets/src/mage/cards/l/LilianasShade.java @@ -23,7 +23,7 @@ */ public final class LilianasShade extends CardImpl { - private static final FilterLandCard filter = new FilterLandCard("Swamp"); + private static final FilterLandCard filter = new FilterLandCard("Swamp card"); static { filter.add(SubType.SWAMP.getPredicate()); @@ -37,7 +37,7 @@ public LilianasShade(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(1); // When Liliana's Shade enters the battlefield, you may search your library for a Swamp card, reveal it, put it into your hand, then shuffle your library. - this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true, true))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true, true), true)); // {B}: Liliana's Shade gets +1/+1 until end of turn. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 1, Duration.EndOfTurn), new ManaCostsImpl("{B}"))); } diff --git a/Mage.Sets/src/mage/cards/l/LimDulsHex.java b/Mage.Sets/src/mage/cards/l/LimDulsHex.java index 5f36812a75af..5af0a76c39a8 100644 --- a/Mage.Sets/src/mage/cards/l/LimDulsHex.java +++ b/Mage.Sets/src/mage/cards/l/LimDulsHex.java @@ -63,8 +63,7 @@ public boolean apply(Game game, Ability source) { if (player != null) { OrCost costToPay = new OrCost(new ManaCostsImpl("{B}"), new ManaCostsImpl("{3}"), "{B} or {3}"); costToPay.clearPaid(); - String message = "Would you like to pay " + costToPay.getText() + " to prevent 1 damage from " + sourcePermanent.getLogName() + "?"; - if (!(player.chooseUse(Outcome.Benefit, message, source, game) && costToPay.pay(source, game, source, player.getId(), false, null))) { + if (!(player.chooseUse(Outcome.Benefit, "Pay {B} or {3}?", source, game) && costToPay.pay(source, game, source, player.getId(), false, null))) { game.informPlayers(player.getLogName() + " chooses not to pay " + costToPay.getText() + " to prevent 1 damage from " + sourcePermanent.getLogName()); player.damage(1, sourcePermanent.getId(), source, game); } else { diff --git a/Mage.Sets/src/mage/cards/l/LimDulsVault.java b/Mage.Sets/src/mage/cards/l/LimDulsVault.java index c18e5491841e..167e9d8467ea 100644 --- a/Mage.Sets/src/mage/cards/l/LimDulsVault.java +++ b/Mage.Sets/src/mage/cards/l/LimDulsVault.java @@ -41,7 +41,7 @@ public LimDulsVaultEffect() { super(Outcome.Benefit); this.staticText = "Look at the top five cards of your library. As many times as you choose, " + "you may pay 1 life, put those cards on the bottom of your library in any order, then look at the top five cards of your library. " - + "Then shuffle your library and put the last cards you looked at this way on top of it in any order"; + + "Then shuffle and put the last cards you looked at this way on top of it in any order"; } public LimDulsVaultEffect(final LimDulsVaultEffect effect) { @@ -73,7 +73,11 @@ public boolean apply(Game game, Ability source) { player.shuffleLibrary(source, game); player.putCardsOnTopOfLibrary(cards, game, source, true); } - } while (doAgain && player.isHuman()); // AI must stop using it as infinite + // AI must stop using it as infinite + if (player.isComputer()) { + break; + } + } while (doAgain); return true; } diff --git a/Mage.Sets/src/mage/cards/l/LinSivviDefiantHero.java b/Mage.Sets/src/mage/cards/l/LinSivviDefiantHero.java index 0bedca2045bc..4e22fbe3f531 100644 --- a/Mage.Sets/src/mage/cards/l/LinSivviDefiantHero.java +++ b/Mage.Sets/src/mage/cards/l/LinSivviDefiantHero.java @@ -16,7 +16,7 @@ import mage.constants.*; import mage.filter.FilterCard; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; @@ -72,7 +72,7 @@ class LinSivviDefiantHeroEffect extends OneShotEffect { public LinSivviDefiantHeroEffect() { super(Outcome.DrawCard); - this.staticText = "Search your library for a Rebel permanent card with converted mana cost X or less and put it onto the battlefield. Then shuffle your library"; + this.staticText = "Search your library for a Rebel permanent card with mana value X or less, put it onto the battlefield, then shuffle"; } public LinSivviDefiantHeroEffect(final LinSivviDefiantHeroEffect effect) { @@ -93,8 +93,8 @@ public boolean apply(Game game, Ability source) { int xCost = source.getManaCostsToPay().getX(); - FilterPermanentCard filter = new FilterPermanentCard("Rebel permanent card with converted mana cost " + xCost + " or less"); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, xCost + 1)); + FilterPermanentCard filter = new FilterPermanentCard("Rebel permanent card with mana value " + xCost + " or less"); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xCost + 1)); filter.add(SubType.REBEL.getPredicate()); TargetCardInLibrary target = new TargetCardInLibrary(filter); diff --git a/Mage.Sets/src/mage/cards/l/LinessaZephyrMage.java b/Mage.Sets/src/mage/cards/l/LinessaZephyrMage.java index ff0e83fdabd5..2156ede808c1 100644 --- a/Mage.Sets/src/mage/cards/l/LinessaZephyrMage.java +++ b/Mage.Sets/src/mage/cards/l/LinessaZephyrMage.java @@ -34,7 +34,7 @@ */ public final class LinessaZephyrMage extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("creature with converted mana cost X"); + private static final FilterPermanent filter = new FilterPermanent("creature with mana value X"); static { filter.add(CardType.CREATURE.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/l/LionsEyeDiamond.java b/Mage.Sets/src/mage/cards/l/LionsEyeDiamond.java index cc0079c35e13..e286be23906b 100644 --- a/Mage.Sets/src/mage/cards/l/LionsEyeDiamond.java +++ b/Mage.Sets/src/mage/cards/l/LionsEyeDiamond.java @@ -72,6 +72,6 @@ public LionsEyeDiamondAbility copy() { @Override public String getRule() { - return super.getRule() + " Activate this ability only any time you could cast an instant."; + return super.getRule() + " Activate only as an instant."; } } diff --git a/Mage.Sets/src/mage/cards/l/Liquify.java b/Mage.Sets/src/mage/cards/l/Liquify.java index f5c7c89924e7..0a342aeda8c8 100644 --- a/Mage.Sets/src/mage/cards/l/Liquify.java +++ b/Mage.Sets/src/mage/cards/l/Liquify.java @@ -9,7 +9,7 @@ import mage.constants.ComparisonType; import mage.constants.Zone; import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetSpell; /** @@ -18,10 +18,10 @@ */ public final class Liquify extends CardImpl { - private static final FilterSpell filter = new FilterSpell("spell with converted mana cost 3 or less"); + private static final FilterSpell filter = new FilterSpell("spell with mana value 3 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public Liquify(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/l/LithoformEngine.java b/Mage.Sets/src/mage/cards/l/LithoformEngine.java index eefc40e75107..5994af0f036b 100644 --- a/Mage.Sets/src/mage/cards/l/LithoformEngine.java +++ b/Mage.Sets/src/mage/cards/l/LithoformEngine.java @@ -109,7 +109,6 @@ public boolean apply(Game game, Ability source) { Permanent sourcePermanent = game.getPermanent(source.getSourceId()); if (controller != null && sourcePermanent != null) { stackAbility.createCopyOnStack(game, source, source.getControllerId(), true); - game.informPlayers(sourcePermanent.getIdName() + ": " + controller.getLogName() + " copied ability"); return true; } } diff --git a/Mage.Sets/src/mage/cards/l/LivingArmor.java b/Mage.Sets/src/mage/cards/l/LivingArmor.java index c3631592d1ad..38d5be12ef27 100644 --- a/Mage.Sets/src/mage/cards/l/LivingArmor.java +++ b/Mage.Sets/src/mage/cards/l/LivingArmor.java @@ -46,7 +46,7 @@ static class LivingArmorEffect extends OneShotEffect { public LivingArmorEffect() { super(Outcome.BoostCreature); - this.staticText = "Put X +0/+1 counters on target creature, where X is that creature's converted mana cost"; + this.staticText = "Put X +0/+1 counters on target creature, where X is that creature's mana value"; } public LivingArmorEffect(final LivingArmorEffect effect) { @@ -62,7 +62,7 @@ public LivingArmorEffect copy() { public boolean apply(Game game, Ability source) { Permanent creature = game.getPermanent(source.getTargets().getFirstTarget()); if (creature != null) { - int amount = creature.getConvertedManaCost(); + int amount = creature.getManaValue(); creature.addCounters(new BoostCounter(0, 1, amount), source.getControllerId(), source, game); return true; } diff --git a/Mage.Sets/src/mage/cards/l/LivingDestiny.java b/Mage.Sets/src/mage/cards/l/LivingDestiny.java index 51d81120e71a..09879698dbaa 100644 --- a/Mage.Sets/src/mage/cards/l/LivingDestiny.java +++ b/Mage.Sets/src/mage/cards/l/LivingDestiny.java @@ -46,7 +46,7 @@ class LivingDestinyEffect extends OneShotEffect { public LivingDestinyEffect() { super(Outcome.GainLife); - staticText = "You gain life equal to its converted mana cost"; + staticText = "You gain life equal to its mana value"; } public LivingDestinyEffect(LivingDestinyEffect effect) { @@ -58,7 +58,7 @@ public boolean apply(Game game, Ability source) { RevealTargetFromHandCost cost = (RevealTargetFromHandCost) source.getCosts().get(0); if (cost != null) { Player player = game.getPlayer(source.getControllerId()); - int CMC = cost.convertedManaCosts; + int CMC = cost.manaValues; if (player != null) { player.gainLife(CMC, game, source); } diff --git a/Mage.Sets/src/mage/cards/l/LivingLore.java b/Mage.Sets/src/mage/cards/l/LivingLore.java index 2d134f83b274..b45827d5ea77 100644 --- a/Mage.Sets/src/mage/cards/l/LivingLore.java +++ b/Mage.Sets/src/mage/cards/l/LivingLore.java @@ -100,7 +100,7 @@ class LivingLoreSetPowerToughnessSourceEffect extends ContinuousEffectImpl { public LivingLoreSetPowerToughnessSourceEffect() { super(Duration.Custom, Layer.PTChangingEffects_7, SubLayer.SetPT_7b, Outcome.BoostCreature); - staticText = "{this}'s power and toughness are each equal to the exiled card's converted mana cost"; + staticText = "{this}'s power and toughness are each equal to the exiled card's mana value"; } public LivingLoreSetPowerToughnessSourceEffect(final LivingLoreSetPowerToughnessSourceEffect effect) { @@ -135,7 +135,7 @@ public boolean apply(Game game, Ability source) { break; } if (exiledCard != null) { - int value = exiledCard.getConvertedManaCost(); + int value = exiledCard.getManaValue(); permanent.getPower().setValue(value); permanent.getToughness().setValue(value); } @@ -183,7 +183,7 @@ && new MageObjectReference(permanent, game).refersTo(mageObject, game)) { } } if (exiledCard != null) { - if (exiledCard.getSpellAbility().canChooseTarget(game)) { + if (exiledCard.getSpellAbility().canChooseTarget(game, controller.getId())) { game.getState().setValue("PlayFromNotOwnHandZone" + exiledCard.getId(), Boolean.TRUE); controller.cast(controller.chooseAbilityForCast(exiledCard, game, true), game, true, new ApprovingObject(source, game)); diff --git a/Mage.Sets/src/mage/cards/l/LivingTotem.java b/Mage.Sets/src/mage/cards/l/LivingTotem.java index 5b2c46f1d8bd..abe82bcf4370 100644 --- a/Mage.Sets/src/mage/cards/l/LivingTotem.java +++ b/Mage.Sets/src/mage/cards/l/LivingTotem.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/l/LivingWish.java b/Mage.Sets/src/mage/cards/l/LivingWish.java index 5d9c539bb414..8302027b2996 100644 --- a/Mage.Sets/src/mage/cards/l/LivingWish.java +++ b/Mage.Sets/src/mage/cards/l/LivingWish.java @@ -16,7 +16,7 @@ */ public final class LivingWish extends CardImpl { - private static final FilterCard filter = new FilterCard("creature or land card"); + private static final FilterCard filter = new FilterCard("a creature or land card"); static { filter.add(Predicates.or( @@ -31,7 +31,7 @@ public LivingWish(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new WishEffect(filter)); // Exile Living Wish. - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private LivingWish(final LivingWish card) { diff --git a/Mage.Sets/src/mage/cards/l/LivioOathswornSentinel.java b/Mage.Sets/src/mage/cards/l/LivioOathswornSentinel.java index a27f3e0523ae..e835b0a746b8 100644 --- a/Mage.Sets/src/mage/cards/l/LivioOathswornSentinel.java +++ b/Mage.Sets/src/mage/cards/l/LivioOathswornSentinel.java @@ -14,7 +14,7 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/l/LlanowarMentor.java b/Mage.Sets/src/mage/cards/l/LlanowarMentor.java index 7997c44076e5..f9338c39838f 100644 --- a/Mage.Sets/src/mage/cards/l/LlanowarMentor.java +++ b/Mage.Sets/src/mage/cards/l/LlanowarMentor.java @@ -1,4 +1,3 @@ - package mage.cards.l; import java.util.UUID; @@ -29,7 +28,7 @@ public LlanowarMentor(UUID ownerId, CardSetInfo setInfo) { this.power = new MageInt(1); this.toughness = new MageInt(1); - // {G}, {tap}, Discard a card: Create a 1/1 green Elf Druid creature token named Llanowar Elves. It has "{tap}: Add {G}." + // {G}, {T}, Discard a card: Create a 1/1 green Elf Druid creature token named Llanowar Elves. It has "{T}: Add {G}." Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new LlanowarElvesToken()), new ManaCostsImpl("{G}")); ability.addCost(new TapSourceCost()); ability.addCost(new DiscardCardCost()); diff --git a/Mage.Sets/src/mage/cards/l/LlanowarSentinel.java b/Mage.Sets/src/mage/cards/l/LlanowarSentinel.java index 84b44d4b0966..4393c77dd1ce 100644 --- a/Mage.Sets/src/mage/cards/l/LlanowarSentinel.java +++ b/Mage.Sets/src/mage/cards/l/LlanowarSentinel.java @@ -51,7 +51,7 @@ class LlanowarSentinelEffect extends OneShotEffect { LlanowarSentinelEffect() { super(Outcome.Benefit); - this.staticText = "you may pay {1}{G}. If you do, search your library for a card named Llanowar Sentinel and put that card onto the battlefield. Then shuffle your library"; } + this.staticText = "you may pay {1}{G}. If you do, search your library for a card named Llanowar Sentinel and put that card onto the battlefield. Then shuffle"; } LlanowarSentinelEffect(final LlanowarSentinelEffect effect) { super(effect); diff --git a/Mage.Sets/src/mage/cards/l/LoamingShaman.java b/Mage.Sets/src/mage/cards/l/LoamingShaman.java index 4bf72ad511b1..07f172952ec4 100644 --- a/Mage.Sets/src/mage/cards/l/LoamingShaman.java +++ b/Mage.Sets/src/mage/cards/l/LoamingShaman.java @@ -1,28 +1,19 @@ - package mage.cards.l; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; +import mage.abilities.effects.common.TargetPlayerShufflesTargetCardsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.game.Game; -import mage.players.Player; import mage.target.TargetPlayer; -import mage.target.common.TargetCardInGraveyard; +import mage.target.common.TargetCardInTargetPlayersGraveyard; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class LoamingShaman extends CardImpl { @@ -36,9 +27,9 @@ public LoamingShaman(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(2); // When Loaming Shaman enters the battlefield, target player shuffles any number of target cards from their graveyard into their library. - Ability ability = new EntersBattlefieldTriggeredAbility(new LoamingShamanEffect(), false); + Ability ability = new EntersBattlefieldTriggeredAbility(new TargetPlayerShufflesTargetCardsEffect(), false); ability.addTarget(new TargetPlayer()); - ability.addTarget(new LoamingShamanTargetCardsInGraveyard(0, Integer.MAX_VALUE, new FilterCard("cards in target player's graveyard"))); + ability.addTarget(new TargetCardInTargetPlayersGraveyard(Integer.MAX_VALUE)); this.addAbility(ability); } @@ -51,65 +42,3 @@ public LoamingShaman copy() { return new LoamingShaman(this); } } - -class LoamingShamanEffect extends OneShotEffect { - - public LoamingShamanEffect() { - super(Outcome.Benefit); - this.staticText = "target player shuffles any number of target cards from their graveyard into their library"; - } - - public LoamingShamanEffect(final LoamingShamanEffect effect) { - super(effect); - } - - @Override - public LoamingShamanEffect copy() { - return new LoamingShamanEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player targetPlayer = game.getPlayer(source.getFirstTarget()); - if (targetPlayer != null) { - Cards cards = new CardsImpl(source.getTargets().get(1).getTargets()); - targetPlayer.moveCards(cards, Zone.LIBRARY, source, game); - targetPlayer.shuffleLibrary(source, game); - return true; - } - return false; - } -} - -class LoamingShamanTargetCardsInGraveyard extends TargetCardInGraveyard { - - public LoamingShamanTargetCardsInGraveyard(int minNumTargets, int maxNumTargets, FilterCard filter) { - super(minNumTargets, maxNumTargets, filter); - } - - public LoamingShamanTargetCardsInGraveyard(final LoamingShamanTargetCardsInGraveyard target) { - super(target); - } - - @Override - public boolean canTarget(UUID id, Ability source, Game game) { - UUID targetPlayerId = source.getFirstTarget(); - UUID firstTarget = this.getFirstTarget(); - Card targetCard = game.getCard(id); - if (firstTarget != null) { - Card card = game.getCard(firstTarget); - if (card == null || targetCard == null - || !card.isOwnedBy(targetCard.getOwnerId())) { - return false; - } - } else if (targetCard == null || !targetCard.isOwnedBy(targetPlayerId)) { - return false; - } - return super.canTarget(id, source, game); - } - - @Override - public LoamingShamanTargetCardsInGraveyard copy() { - return new LoamingShamanTargetCardsInGraveyard(this); - } -} diff --git a/Mage.Sets/src/mage/cards/l/Lobotomy.java b/Mage.Sets/src/mage/cards/l/Lobotomy.java index 1964d4e2fdf8..ecc9d9194448 100644 --- a/Mage.Sets/src/mage/cards/l/Lobotomy.java +++ b/Mage.Sets/src/mage/cards/l/Lobotomy.java @@ -53,7 +53,7 @@ class LobotomyEffect extends OneShotEffect { public LobotomyEffect() { super(Outcome.Benefit); - staticText = "Target player reveals their hand, then you choose a card other than a basic land card from it. Search that player's graveyard, hand, and library for all cards with the same name as the chosen card and exile them. Then that player shuffles their library"; + staticText = "Target player reveals their hand, then you choose a card other than a basic land card from it. Search that player's graveyard, hand, and library for all cards with the same name as the chosen card and exile them. Then that player shuffles"; } public LobotomyEffect(final LobotomyEffect effect) { diff --git a/Mage.Sets/src/mage/cards/l/LoneWolfOfTheNatterknolls.java b/Mage.Sets/src/mage/cards/l/LoneWolfOfTheNatterknolls.java index 24c5cd9216d6..1f6020f6f6e3 100644 --- a/Mage.Sets/src/mage/cards/l/LoneWolfOfTheNatterknolls.java +++ b/Mage.Sets/src/mage/cards/l/LoneWolfOfTheNatterknolls.java @@ -1,23 +1,18 @@ package mage.cards.l; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SpellCastOpponentTriggeredAbility; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.condition.common.MyTurnCondition; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.hint.common.MyTurnHint; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import java.util.UUID; @@ -38,14 +33,13 @@ public LoneWolfOfTheNatterknolls(UUID ownerId, CardSetInfo setInfo) { // Whenever an opponent cast a spell during your turn, draw two cards. this.addAbility(new ConditionalTriggeredAbility( - new SpellCastOpponentTriggeredAbility(new DrawCardSourceControllerEffect(2), new FilterSpell("a spell"), true), + new SpellCastOpponentTriggeredAbility(new DrawCardSourceControllerEffect(2), StaticFilters.FILTER_SPELL_A, true), MyTurnCondition.instance, "Whenever an opponent casts a spell during your turn, draw two cards." ).addHint(MyTurnHint.instance)); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Lone Wolf of the Natterknolls. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private LoneWolfOfTheNatterknolls(final LoneWolfOfTheNatterknolls card) { diff --git a/Mage.Sets/src/mage/cards/l/LongTermPlans.java b/Mage.Sets/src/mage/cards/l/LongTermPlans.java index da2dc88ec539..3b799b4e2a21 100644 --- a/Mage.Sets/src/mage/cards/l/LongTermPlans.java +++ b/Mage.Sets/src/mage/cards/l/LongTermPlans.java @@ -40,7 +40,7 @@ class LongTermPlansEffect extends OneShotEffect { LongTermPlansEffect() { super(Outcome.Benefit); - this.staticText = "Search your library for a card, shuffle your library, then put that card third from the top"; + this.staticText = "Search your library for a card, then shuffle and put that card third from the top"; } LongTermPlansEffect(final LongTermPlansEffect effect) { diff --git a/Mage.Sets/src/mage/cards/l/LordOfThePit.java b/Mage.Sets/src/mage/cards/l/LordOfThePit.java index 9b6cfe5340b9..bf22afa10ebc 100644 --- a/Mage.Sets/src/mage/cards/l/LordOfThePit.java +++ b/Mage.Sets/src/mage/cards/l/LordOfThePit.java @@ -10,7 +10,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/l/LoreholdApprentice.java b/Mage.Sets/src/mage/cards/l/LoreholdApprentice.java new file mode 100644 index 000000000000..50860817a58f --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LoreholdApprentice.java @@ -0,0 +1,51 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LoreholdApprentice extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent(SubType.SPIRIT, ""); + + public LoreholdApprentice(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, until end of turn, Spirit creatures you control gain "{T}: This creature deals 1 damage to each opponent." + this.addAbility(new MagecraftAbility(new GainAbilityControlledEffect( + new SimpleActivatedAbility(new DamagePlayersEffect( + 1, TargetController.OPPONENT, "this creature" + ), new TapSourceCost()), Duration.WhileOnBattlefield, filter + ).setText("until end of turn, Spirit creatures you control gain \"{T}: This creature deals 1 damage to each opponent.\""))); + } + + private LoreholdApprentice(final LoreholdApprentice card) { + super(card); + } + + @Override + public LoreholdApprentice copy() { + return new LoreholdApprentice(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LoreholdCampus.java b/Mage.Sets/src/mage/cards/l/LoreholdCampus.java new file mode 100644 index 000000000000..b0630d256c50 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LoreholdCampus.java @@ -0,0 +1,46 @@ +package mage.cards.l; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.mana.RedManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LoreholdCampus extends CardImpl { + + public LoreholdCampus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // Lorehold Campus enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {R} or {W}. + this.addAbility(new RedManaAbility()); + this.addAbility(new WhiteManaAbility()); + + // {4}, {T}: Scry 1. + Ability ability = new SimpleActivatedAbility(new ScryEffect(1), new GenericManaCost(4)); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private LoreholdCampus(final LoreholdCampus card) { + super(card); + } + + @Override + public LoreholdCampus copy() { + return new LoreholdCampus(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LoreholdCommand.java b/Mage.Sets/src/mage/cards/l/LoreholdCommand.java new file mode 100644 index 000000000000..0eb8f24319b1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LoreholdCommand.java @@ -0,0 +1,111 @@ +package mage.cards.l; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.SacrificeControllerEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.LoreholdToken; +import mage.players.Player; +import mage.target.TargetPlayer; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LoreholdCommand extends CardImpl { + + public LoreholdCommand(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{R}{W}"); + + // Choose two — + this.getSpellAbility().getModes().setMinModes(2); + this.getSpellAbility().getModes().setMaxModes(2); + + // • Create a 3/2 red and white Spirit creature token. + this.getSpellAbility().addEffect(new CreateTokenEffect(new LoreholdToken())); + + // • Creatures you control get +1/+0 and gain indestructible and haste until end of turn. + Mode mode = new Mode(new BoostControlledEffect( + 1, 0, Duration.EndOfTurn + ).setText("creatures you control get +1/+0")); + mode.addEffect(new GainAbilityControlledEffect( + IndestructibleAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_PERMANENT_CREATURES + ).setText("and gain indestructible")); + mode.addEffect(new GainAbilityControlledEffect( + HasteAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_PERMANENT_CREATURES + ).setText("and haste until end of turn")); + this.getSpellAbility().addMode(mode); + + // • Lorehold Command deals 3 damage to any target. Target player gains 3 life. + mode = new Mode(new LoreholdCommandEffect()); + mode.addTarget(new TargetAnyTarget().withChooseHint("To deal damage")); + mode.addTarget(new TargetPlayer().withChooseHint("To gain life")); + this.getSpellAbility().addMode(mode); + + // • Sacrifice a permanent, then draw two cards. + mode = new Mode(new SacrificeControllerEffect(StaticFilters.FILTER_PERMANENT, 1, "")); + mode.addEffect(new DrawCardSourceControllerEffect(2).concatBy(", then")); + this.getSpellAbility().addMode(mode); + } + + private LoreholdCommand(final LoreholdCommand card) { + super(card); + } + + @Override + public LoreholdCommand copy() { + return new LoreholdCommand(this); + } +} + +class LoreholdCommandEffect extends OneShotEffect { + + LoreholdCommandEffect() { + super(Outcome.Benefit); + staticText = "{this} deals 3 damage to any target. Target player gains 3 life"; + } + + private LoreholdCommandEffect(final LoreholdCommandEffect effect) { + super(effect); + } + + @Override + public LoreholdCommandEffect copy() { + return new LoreholdCommandEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player targetPlayer = game.getPlayer(source.getFirstTarget()); + if (targetPlayer != null) { + targetPlayer.damage(3, source.getSourceId(), source, game); + } + Permanent targetPermanent = game.getPermanent(source.getFirstTarget()); + if (targetPermanent != null) { + targetPermanent.damage(3, source.getSourceId(), source, game); + } + Player player = game.getPlayer(source.getTargets().get(1).getFirstTarget()); + if (player != null) { + player.gainLife(3, game, source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/l/LoreholdExcavation.java b/Mage.Sets/src/mage/cards/l/LoreholdExcavation.java new file mode 100644 index 000000000000..985faf4194b7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LoreholdExcavation.java @@ -0,0 +1,88 @@ +package mage.cards.l; + +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.ExileFromGraveCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.token.LoreholdToken; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LoreholdExcavation extends CardImpl { + + public LoreholdExcavation(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{R}{W}"); + + // At the beginning of your end step, mill a card. If a land card was milled this way, you gain 1 life. Otherwise, Lorehold Excavation deals 1 damage to each opponent. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + new LoreholdExcavationEffect(), TargetController.YOU, false + )); + + // {5}, Exile a creature card from your graveyard: Create a tapped 3/2 red and white Spirit creature token. + Ability ability = new SimpleActivatedAbility(new CreateTokenEffect( + new LoreholdToken(), 1, true, false + ), new GenericManaCost(5)); + ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_A))); + this.addAbility(ability); + } + + private LoreholdExcavation(final LoreholdExcavation card) { + super(card); + } + + @Override + public LoreholdExcavation copy() { + return new LoreholdExcavation(this); + } +} + +class LoreholdExcavationEffect extends OneShotEffect { + + private static final Effect effect = new DamagePlayersEffect(1, TargetController.OPPONENT); + + LoreholdExcavationEffect() { + super(Outcome.Benefit); + staticText = "mill a card. If a land card was milled this way, you gain 1 life. " + + "Otherwise, {this} deals 1 damage to each opponent"; + } + + private LoreholdExcavationEffect(final LoreholdExcavationEffect effect) { + super(effect); + } + + @Override + public LoreholdExcavationEffect copy() { + return new LoreholdExcavationEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + if (player.millCards(1, source, game).count(StaticFilters.FILTER_CARD_LAND, game) > 0) { + player.gainLife(1, game, source); + return true; + } + effect.apply(game, source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/l/LoreholdPledgemage.java b/Mage.Sets/src/mage/cards/l/LoreholdPledgemage.java new file mode 100644 index 000000000000..6150311afea0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LoreholdPledgemage.java @@ -0,0 +1,43 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.FirstStrikeAbility; +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 LoreholdPledgemage extends CardImpl { + + public LoreholdPledgemage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R/W}{R/W}"); + + this.subtype.add(SubType.KOR); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, Lorehold Pledgemage gets +1/+0 until end of turn. + this.addAbility(new MagecraftAbility(new BoostSourceEffect(1, 0, Duration.EndOfTurn))); + } + + private LoreholdPledgemage(final LoreholdPledgemage card) { + super(card); + } + + @Override + public LoreholdPledgemage copy() { + return new LoreholdPledgemage(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LosheelClockworkScholar.java b/Mage.Sets/src/mage/cards/l/LosheelClockworkScholar.java new file mode 100644 index 000000000000..119405d27956 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LosheelClockworkScholar.java @@ -0,0 +1,61 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.PreventAllDamageToAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterArtifactCreaturePermanent; +import mage.filter.predicate.permanent.AttackingPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LosheelClockworkScholar extends CardImpl { + + private static final FilterPermanent filter + = new FilterArtifactCreaturePermanent("attacking artifact creatures you control"); + private static final FilterPermanent filter2 + = new FilterArtifactCreaturePermanent("one or more artifact creatures"); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + filter.add(AttackingPredicate.instance); + filter2.add(TargetController.YOU.getControllerPredicate()); + } + + public LosheelClockworkScholar(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ELEPHANT); + this.subtype.add(SubType.ARTIFICER); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Prevent all combat damage that would be dealt to attacking artifact creatures you control. + this.addAbility(new SimpleStaticAbility(new PreventAllDamageToAllEffect( + Duration.WhileOnBattlefield, filter, true + ))); + + // Whenever one or more artifact creatures enter the battlefield under your control, draw a card. This ability triggers only once each turn. + this.addAbility(new EntersBattlefieldControlledTriggeredAbility( + new DrawCardSourceControllerEffect(1), filter2 + ).setTriggersOnce(true)); + } + + private LosheelClockworkScholar(final LosheelClockworkScholar card) { + super(card); + } + + @Override + public LosheelClockworkScholar copy() { + return new LosheelClockworkScholar(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LostAuramancers.java b/Mage.Sets/src/mage/cards/l/LostAuramancers.java index 4f3a5f70e78a..c4d91966e906 100644 --- a/Mage.Sets/src/mage/cards/l/LostAuramancers.java +++ b/Mage.Sets/src/mage/cards/l/LostAuramancers.java @@ -86,6 +86,6 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { - return "When {this} is put into a graveyard from play, if it had no time counters on it, you may search your library for an enchantment card and put it into play. If you do, shuffle your library."; + return "When {this} dies, if it had no time counters on it, you may search your library for an enchantment card and put it onto the battlefield. If you do, shuffle."; } } diff --git a/Mage.Sets/src/mage/cards/l/LostInTheMist.java b/Mage.Sets/src/mage/cards/l/LostInTheMist.java index ea38f004f23f..6a5f00f972b1 100644 --- a/Mage.Sets/src/mage/cards/l/LostInTheMist.java +++ b/Mage.Sets/src/mage/cards/l/LostInTheMist.java @@ -1,7 +1,5 @@ - package mage.cards.l; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CounterTargetEffect; @@ -12,18 +10,19 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.TargetPermanent; import mage.target.TargetSpell; +import java.util.UUID; + /** - * * @author North */ public final class LostInTheMist extends CardImpl { public LostInTheMist(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{U}{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}{U}"); // Counter target spell. Return target permanent to its owner's hand. this.getSpellAbility().addTarget(new TargetSpell()); @@ -44,12 +43,12 @@ public LostInTheMist copy() { class LostInTheMistEffect extends OneShotEffect { - public LostInTheMistEffect() { + LostInTheMistEffect() { super(Outcome.ReturnToHand); this.staticText = "Return target permanent to its owner's hand"; } - public LostInTheMistEffect(final LostInTheMistEffect effect) { + private LostInTheMistEffect(final LostInTheMistEffect effect) { super(effect); } @@ -60,10 +59,10 @@ public LostInTheMistEffect copy() { @Override public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if (permanent != null) { - return permanent.moveToZone(Zone.HAND, source, game, false); - } - return false; + return player != null + && permanent != null + && player.moveCards(permanent, Zone.HAND, source, game); } } diff --git a/Mage.Sets/src/mage/cards/l/LostLegacy.java b/Mage.Sets/src/mage/cards/l/LostLegacy.java index 6235185706f1..7cbd30a66b68 100644 --- a/Mage.Sets/src/mage/cards/l/LostLegacy.java +++ b/Mage.Sets/src/mage/cards/l/LostLegacy.java @@ -75,6 +75,6 @@ public LostLegacyEffect copy() { @Override public String getText(Mode mode) { - return "Search target player's graveyard, hand and library for any number of cards with that name and exile them. That player shuffles their library, then draws a card for each card exiled from hand this way"; + return "Search target player's graveyard, hand, and library for any number of cards with that name and exile them. That player shuffles, then draws a card for each card exiled from their hand this way"; } } diff --git a/Mage.Sets/src/mage/cards/l/LukkaCoppercoatOutcast.java b/Mage.Sets/src/mage/cards/l/LukkaCoppercoatOutcast.java index 2eeb84855359..3a91a6827fcf 100644 --- a/Mage.Sets/src/mage/cards/l/LukkaCoppercoatOutcast.java +++ b/Mage.Sets/src/mage/cards/l/LukkaCoppercoatOutcast.java @@ -129,7 +129,7 @@ class LukkaCoppercoatOutcastPolymorphEffect extends OneShotEffect { LukkaCoppercoatOutcastPolymorphEffect() { super(Outcome.Benefit); staticText = "Exile target creature you control, then reveal cards from the top of your library " + - "until you reveal a creature card with higher converted mana cost. " + + "until you reveal a creature card with higher mana value. " + "Put that card onto the battlefield and the rest on the bottom of your library in a random order."; } @@ -149,13 +149,13 @@ public boolean apply(Game game, Ability source) { if (permanent == null || player == null) { return false; } - int cmc = permanent.getConvertedManaCost(); + int cmc = permanent.getManaValue(); player.moveCards(permanent, Zone.EXILED, source, game); Card toBattlefield = null; Cards toReveal = new CardsImpl(); for (Card card : player.getLibrary().getCards(game)) { toReveal.add(card); - if (card.isCreature() && card.getConvertedManaCost() > cmc) { + if (card.isCreature() && card.getManaValue() > cmc) { toBattlefield = card; break; } diff --git a/Mage.Sets/src/mage/cards/l/LullmagesDomination.java b/Mage.Sets/src/mage/cards/l/LullmagesDomination.java index 01c3df74fea4..338e184b0319 100644 --- a/Mage.Sets/src/mage/cards/l/LullmagesDomination.java +++ b/Mage.Sets/src/mage/cards/l/LullmagesDomination.java @@ -15,7 +15,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicate; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -49,7 +49,7 @@ Zone.ALL, new SpellCostReductionSourceEffect(3, condition).setCanWorksOnStackOnl // Gain control of target creature with converted mana cost X. this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.Custom) - .setText("gain control of target creature with converted mana cost X")); + .setText("gain control of target creature with mana value X")); this.getSpellAbility().setTargetAdjuster(LullmagesDominationAdjuster.instance); } @@ -80,8 +80,8 @@ enum LullmagesDominationAdjuster implements TargetAdjuster { public void adjustTargets(Ability ability, Game game) { ability.getTargets().clear(); int xValue = ability.getManaCostsToPay().getX(); - FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with converted mana cost " + xValue); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, xValue)); + FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with mana value " + xValue); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, xValue)); ability.addTarget(new TargetCreaturePermanent(filter)); } } diff --git a/Mage.Sets/src/mage/cards/l/LumberingBattlement.java b/Mage.Sets/src/mage/cards/l/LumberingBattlement.java index 83e2c4925d19..771737348ab4 100644 --- a/Mage.Sets/src/mage/cards/l/LumberingBattlement.java +++ b/Mage.Sets/src/mage/cards/l/LumberingBattlement.java @@ -21,7 +21,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.ExileZone; import mage.game.Game; diff --git a/Mage.Sets/src/mage/cards/l/LuminarchAscension.java b/Mage.Sets/src/mage/cards/l/LuminarchAscension.java index 59e7e327069c..95fdabc5156a 100644 --- a/Mage.Sets/src/mage/cards/l/LuminarchAscension.java +++ b/Mage.Sets/src/mage/cards/l/LuminarchAscension.java @@ -1,13 +1,11 @@ package mage.cards.l; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.condition.Condition; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; +import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; @@ -15,32 +13,40 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.TargetController; import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.token.AngelToken; import mage.watchers.common.PlayerLostLifeWatcher; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class LuminarchAscension extends CardImpl { - private static final String rule = "At the beginning of each opponent's end step, if you didn't lose life this turn, you may put a quest counter on {this}. (Damage causes loss of life.)"; + private static final String rule = "At the beginning of each opponent's end step, " + + "if you didn't lose life this turn, you may put a quest counter on {this}."; + private static final Condition condition = new SourceHasCounterCondition(CounterType.QUEST, 4); public LuminarchAscension(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); // At the beginning of each opponent's end step, if you didn't lose life this turn, you may put a quest counter on Luminarch Ascension. - this.addAbility(new ConditionalInterveningIfTriggeredAbility(new LuminarchAscensionTriggeredAbility(), YouLostNoLifeThisTurnCondition.instance, rule)); + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new BeginningOfEndStepTriggeredAbility( + new AddCountersSourceEffect(CounterType.QUEST.createInstance()), + TargetController.OPPONENT, true + ), LuminarchAscensionCondition.instance, rule + )); // {1}{W}: Create a 4/4 white Angel creature token with flying. Activate this ability only if Luminarch Ascension has four or more quest counters on it. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new AngelToken()), new ManaCostsImpl("{1}{W}")); - ability.addCost(new SourceHasCountersCost(4, CounterType.QUEST)); - this.addAbility(ability); + this.addAbility(new ActivateIfConditionActivatedAbility( + Zone.BATTLEFIELD, new CreateTokenEffect(new AngelToken()), + new ManaCostsImpl<>("{1}{W}"), condition + )); } private LuminarchAscension(final LuminarchAscension card) { @@ -53,68 +59,7 @@ public LuminarchAscension copy() { } } -class LuminarchAscensionTriggeredAbility extends TriggeredAbilityImpl { - - public LuminarchAscensionTriggeredAbility() { - super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.QUEST.createInstance()), true); - } - - public LuminarchAscensionTriggeredAbility(LuminarchAscensionTriggeredAbility ability) { - super(ability); - } - - @Override - public LuminarchAscensionTriggeredAbility copy() { - return new LuminarchAscensionTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.END_TURN_STEP_PRE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return game.getOpponents(controllerId).contains(event.getPlayerId()); - } -} - -class SourceHasCountersCost extends CostImpl { - - private final int counters; - private final CounterType counterType; - - public SourceHasCountersCost(int counters, CounterType counterType) { - this.counters = counters; - this.counterType = counterType; - this.text = "Activate this ability only if Luminarch Ascension has 4 or more quest counters on it"; - } - - public SourceHasCountersCost(final SourceHasCountersCost cost) { - super(cost); - this.counters = cost.counters; - this.counterType = cost.counterType; - } - - @Override - public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { - return (game.getPermanent(source.getSourceId()).getCounters(game).getCount(counterType) >= counters); - } - - @Override - public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - this.paid = true; - return paid; - } - - @Override - public SourceHasCountersCost copy() { - return new SourceHasCountersCost(this); - } -} - -enum YouLostNoLifeThisTurnCondition implements Condition { - +enum LuminarchAscensionCondition implements Condition { instance; @Override diff --git a/Mage.Sets/src/mage/cards/l/LuminatePrimordial.java b/Mage.Sets/src/mage/cards/l/LuminatePrimordial.java index d6b0a2ad11eb..c46a57342e75 100644 --- a/Mage.Sets/src/mage/cards/l/LuminatePrimordial.java +++ b/Mage.Sets/src/mage/cards/l/LuminatePrimordial.java @@ -1,4 +1,3 @@ - package mage.cards.l; import mage.MageInt; @@ -11,6 +10,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; +import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; @@ -20,7 +20,8 @@ import mage.target.common.TargetCreaturePermanent; import mage.target.targetadjustment.TargetAdjuster; -import java.util.UUID; +import java.util.*; +import java.util.stream.Collectors; /** * @author LevelX2 @@ -34,7 +35,7 @@ public LuminatePrimordial(UUID ownerId, CardSetInfo setInfo) { this.power = new MageInt(4); this.toughness = new MageInt(7); - //Vigilance + // Vigilance this.addAbility(VigilanceAbility.getInstance()); // When Luminate Primordial enters the battlefield, for each opponent, exile up to one target creature @@ -74,12 +75,12 @@ public void adjustTargets(Ability ability, Game game) { class LuminatePrimordialEffect extends OneShotEffect { - public LuminatePrimordialEffect() { + LuminatePrimordialEffect() { super(Outcome.Benefit); this.staticText = "for each opponent, exile up to one target creature that player controls and that player gains life equal to its power"; } - public LuminatePrimordialEffect(final LuminatePrimordialEffect effect) { + private LuminatePrimordialEffect(final LuminatePrimordialEffect effect) { super(effect); } @@ -90,18 +91,26 @@ public LuminatePrimordialEffect copy() { @Override public boolean apply(Game game, Ability source) { - for (Target target : source.getTargets()) { - if (target instanceof TargetCreaturePermanent) { - Permanent targetCreature = game.getPermanent(target.getFirstTarget()); - if (targetCreature != null && !targetCreature.isControlledBy(source.getControllerId())) { - int amountLife = targetCreature.getPower().getValue(); - Player controller = game.getPlayer(targetCreature.getControllerId()); - targetCreature.moveToExile(null, null, source, game); - if (controller != null && amountLife != 0) { - controller.gainLife(amountLife, game, source); - } - } + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Set permanents = source + .getTargets() + .stream() + .map(Target::getFirstTarget) + .map(game::getPermanent) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + Map map = new HashMap<>(); + permanents.stream().map(p -> map.put(p.getControllerId(), p.getPower().getValue())); + controller.moveCards(permanents, Zone.EXILED, source, game); + for (Map.Entry entry : map.entrySet()) { + Player player = game.getPlayer(entry.getKey()); + if (player == null || entry.getValue() < 1) { + continue; } + player.gainLife(entry.getValue(), game, source); } return true; } diff --git a/Mage.Sets/src/mage/cards/l/Luminesce.java b/Mage.Sets/src/mage/cards/l/Luminesce.java index 7e3ada0d9399..7aa6198705d5 100644 --- a/Mage.Sets/src/mage/cards/l/Luminesce.java +++ b/Mage.Sets/src/mage/cards/l/Luminesce.java @@ -56,8 +56,7 @@ public boolean apply(Game game, Ability source) { public boolean applies(GameEvent event, Ability source, Game game) { if (super.applies(event, source, game)) { if (event.getType() == GameEvent.EventType.DAMAGE_PLAYER - || event.getType() == GameEvent.EventType.DAMAGE_CREATURE - || event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER) { + || event.getType() == GameEvent.EventType.DAMAGE_PERMANENT) { MageObject sourceObject = game.getObject(event.getSourceId()); if (sourceObject != null && (sourceObject.getColor(game).shares(ObjectColor.BLACK) || sourceObject.getColor(game).shares(ObjectColor.RED))) { diff --git a/Mage.Sets/src/mage/cards/l/LuminousBroodmoth.java b/Mage.Sets/src/mage/cards/l/LuminousBroodmoth.java index daa259ac9ce0..c40c03cb8c16 100644 --- a/Mage.Sets/src/mage/cards/l/LuminousBroodmoth.java +++ b/Mage.Sets/src/mage/cards/l/LuminousBroodmoth.java @@ -80,8 +80,7 @@ public boolean checkTrigger(GameEvent event, Game game) { Permanent permanent = game.getPermanentOrLKIBattlefield(zEvent.getTarget().getId()); if (permanent != null - && zEvent.getToZone() == Zone.GRAVEYARD - && zEvent.getFromZone() == Zone.BATTLEFIELD + && zEvent.isDiesEvent() && permanent.isCreature() && !permanent.getAbilities().containsKey(FlyingAbility.getInstance().getId()) && permanent.isControlledBy(this.controllerId)) { diff --git a/Mage.Sets/src/mage/cards/l/LunarForce.java b/Mage.Sets/src/mage/cards/l/LunarForce.java index 42815c489f31..58c71fea93e4 100644 --- a/Mage.Sets/src/mage/cards/l/LunarForce.java +++ b/Mage.Sets/src/mage/cards/l/LunarForce.java @@ -13,6 +13,7 @@ import mage.constants.SetTargetPointer; import mage.constants.Zone; import mage.filter.FilterSpell; +import mage.filter.StaticFilters; /** * @@ -25,7 +26,7 @@ public LunarForce(UUID ownerId, CardSetInfo setInfo) { // Whenever an opponent casts a spell, sacrifice Lunar Force and counter that spell. Ability ability = new SpellCastOpponentTriggeredAbility(Zone.BATTLEFIELD, new SacrificeSourceEffect(), - new FilterSpell("a spell"), false, SetTargetPointer.SPELL); + StaticFilters.FILTER_SPELL_A, false, SetTargetPointer.SPELL); Effect effect = new CounterTargetEffect(); effect.setText("and counter that spell"); ability.addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/l/LurkingJackals.java b/Mage.Sets/src/mage/cards/l/LurkingJackals.java index 46542b54fa4c..cdb3803a9df9 100644 --- a/Mage.Sets/src/mage/cards/l/LurkingJackals.java +++ b/Mage.Sets/src/mage/cards/l/LurkingJackals.java @@ -79,10 +79,10 @@ public boolean canTrigger(Game game) { } @Override - public void trigger(Game game, UUID controllerId) { + public void trigger(Game game, UUID controllerId, GameEvent triggeringEvent) { //20100716 - 603.8 game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.TRUE); - super.trigger(game, controllerId); + super.trigger(game, controllerId, triggeringEvent); } @Override diff --git a/Mage.Sets/src/mage/cards/l/LurrusOfTheDreamDen.java b/Mage.Sets/src/mage/cards/l/LurrusOfTheDreamDen.java index 5a85f496f793..300e4886a3c4 100644 --- a/Mage.Sets/src/mage/cards/l/LurrusOfTheDreamDen.java +++ b/Mage.Sets/src/mage/cards/l/LurrusOfTheDreamDen.java @@ -63,14 +63,14 @@ enum LurrusOfTheDreamDenCompanionCondition implements CompanionCondition { @Override public String getRule() { - return "Each permanent card in your starting deck has converted mana cost 2 or less."; + return "Each permanent card in your starting deck has mana value 2 or less."; } @Override public boolean isLegal(Set deck, int startingSize) { return deck.stream() .filter(MageObject::isPermanent) - .mapToInt(MageObject::getConvertedManaCost) + .mapToInt(MageObject::getManaValue) .max() .orElse(0) <= 2; } @@ -80,7 +80,7 @@ class LurrusOfTheDreamDenCastFromGraveyardEffect extends AsThoughEffectImpl { LurrusOfTheDreamDenCastFromGraveyardEffect() { super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit, true); - staticText = "During each of your turns, you may cast one permanent spell with converted mana cost 2 or less from your graveyard"; + staticText = "During each of your turns, you may cast one permanent spell with mana value 2 or less from your graveyard"; } private LurrusOfTheDreamDenCastFromGraveyardEffect(final LurrusOfTheDreamDenCastFromGraveyardEffect effect) { @@ -100,13 +100,14 @@ public LurrusOfTheDreamDenCastFromGraveyardEffect copy() { @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { if (source.isControlledBy(affectedControllerId) - && Zone.GRAVEYARD.equals(game.getState().getZone(objectId))) { + && Zone.GRAVEYARD.equals(game.getState().getZone(objectId)) + && game.isActivePlayer(source.getControllerId())) { // only during your turn Card objectCard = game.getCard(objectId); Permanent sourceObject = game.getPermanent(source.getSourceId()); if (sourceObject != null && objectCard != null && objectCard.isPermanent() && objectCard.isOwnedBy(source.getControllerId()) - && objectCard.getConvertedManaCost() < 3 + && objectCard.getManaValue() < 3 && objectCard.getSpellAbility() != null && objectCard.getSpellAbility().spellCanBeActivatedRegularlyNow(affectedControllerId, game)) { LurrusOfTheDreamDenWatcher watcher = game.getState().getWatcher(LurrusOfTheDreamDenWatcher.class); diff --git a/Mage.Sets/src/mage/cards/l/LuxaRiverShrine.java b/Mage.Sets/src/mage/cards/l/LuxaRiverShrine.java index ae8b142bb69e..2c05b67606af 100644 --- a/Mage.Sets/src/mage/cards/l/LuxaRiverShrine.java +++ b/Mage.Sets/src/mage/cards/l/LuxaRiverShrine.java @@ -23,7 +23,7 @@ */ public final class LuxaRiverShrine extends CardImpl { - private static final String rule = "{T}: You gain 2 life. Activate this ability only if there are three or more brick counters on {this}."; + private static final String rule = "{T}: You gain 2 life. Activate only if there are three or more brick counters on {this}."; public LuxaRiverShrine(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); diff --git a/Mage.Sets/src/mage/cards/m/MadAuntie.java b/Mage.Sets/src/mage/cards/m/MadAuntie.java index 91e3865afe22..87fc95fefd5e 100644 --- a/Mage.Sets/src/mage/cards/m/MadAuntie.java +++ b/Mage.Sets/src/mage/cards/m/MadAuntie.java @@ -17,7 +17,7 @@ import mage.constants.Zone; import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/m/MaddeningImp.java b/Mage.Sets/src/mage/cards/m/MaddeningImp.java index 87f97513ece5..59d88458f8e5 100644 --- a/Mage.Sets/src/mage/cards/m/MaddeningImp.java +++ b/Mage.Sets/src/mage/cards/m/MaddeningImp.java @@ -54,7 +54,7 @@ public MaddeningImp(UUID ownerId, CardSetInfo setInfo) { new TapSourceCost(), new MaddeningImpTurnCondition(), "{T}: Non-Wall creatures the active player controls attack this turn if able. " + "At the beginning of the next end step, destroy each of those creatures that didn't attack this turn. " - + "Activate this ability only during an opponent's turn and only before combat."); + + "Activate only during an opponent's turn and only before combat."); ability.addEffect(new MaddeningImpCreateDelayedTriggeredAbilityEffect()); this.addAbility(ability, new AttackedThisTurnWatcher()); diff --git a/Mage.Sets/src/mage/cards/m/MaelstromArchangel.java b/Mage.Sets/src/mage/cards/m/MaelstromArchangel.java index 6753e8642b70..2385d6d59618 100644 --- a/Mage.Sets/src/mage/cards/m/MaelstromArchangel.java +++ b/Mage.Sets/src/mage/cards/m/MaelstromArchangel.java @@ -81,7 +81,7 @@ public boolean apply(Game game, Ability source) { while (controller.canRespond() && !cancel) { if (controller.chooseTarget(outcome, target, source, game)) { cardToCast = game.getCard(target.getFirstTarget()); - if (cardToCast != null && cardToCast.getSpellAbility().canChooseTarget(game)) { + if (cardToCast != null && cardToCast.getSpellAbility().canChooseTarget(game, controller.getId())) { cancel = true; } } else { diff --git a/Mage.Sets/src/mage/cards/m/MaelstromMuse.java b/Mage.Sets/src/mage/cards/m/MaelstromMuse.java new file mode 100644 index 000000000000..f590efbe3905 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MaelstromMuse.java @@ -0,0 +1,142 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.util.CardUtil; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MaelstromMuse extends CardImpl { + + public MaelstromMuse(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{U/R}{R}"); + + this.subtype.add(SubType.DJINN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever Maelstrom Muse attacks, the next instant or sorcery spell you cast this turn costs {X} less to cast, where X is Maelstrom Muse's power as this ability resolves. + this.addAbility(new AttacksTriggeredAbility( + new MaelstromMuseEffect(), false + ), new MaelstromMuseWatcher()); + } + + private MaelstromMuse(final MaelstromMuse card) { + super(card); + } + + @Override + public MaelstromMuse copy() { + return new MaelstromMuse(this); + } +} + +class MaelstromMuseEffect extends CostModificationEffectImpl { + + private int spellsCast; + private int sourcePower = 0; + + MaelstromMuseEffect() { + super(Duration.EndOfTurn, Outcome.Benefit, CostModificationType.REDUCE_COST); + staticText = "the next instant or sorcery spell you cast this turn costs {X} less to cast, " + + "where X is {this}'s power as this ability resolves"; + } + + private MaelstromMuseEffect(final MaelstromMuseEffect effect) { + super(effect); + this.spellsCast = effect.spellsCast; + this.sourcePower = effect.sourcePower; + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + MaelstromMuseWatcher watcher = game.getState().getWatcher(MaelstromMuseWatcher.class); + if (watcher != null) { + spellsCast = watcher.getCount(source.getControllerId()); + } + Permanent permanent = source.getSourcePermanentOrLKI(game); + sourcePower = permanent != null ? Math.max(permanent.getPower().getValue(), 0) : 0; + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + CardUtil.reduceCost(abilityToModify, sourcePower); + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + MaelstromMuseWatcher watcher = game.getState().getWatcher(MaelstromMuseWatcher.class); + if (watcher == null) { + return false; + } + if (watcher.getCount(source.getControllerId()) > spellsCast) { + discard(); // only one use + return false; + } + if (!(abilityToModify instanceof SpellAbility) + || !abilityToModify.isControlledBy(source.getControllerId())) { + return false; + } + Card spellCard = ((SpellAbility) abilityToModify).getCharacteristics(game); + return spellCard != null && spellCard.isInstantOrSorcery(); + } + + @Override + public MaelstromMuseEffect copy() { + return new MaelstromMuseEffect(this); + } +} + +class MaelstromMuseWatcher extends Watcher { + + private final Map playerMap = new HashMap<>(); + + MaelstromMuseWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.SPELL_CAST) { + return; + } + Spell spell = game.getSpell(event.getSourceId()); + if (spell != null && spell.isInstantOrSorcery()) { + playerMap.compute(event.getPlayerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + } + } + + @Override + public void reset() { + super.reset(); + playerMap.clear(); + } + + int getCount(UUID playerId) { + return playerMap.getOrDefault(playerId, 0); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MageDuel.java b/Mage.Sets/src/mage/cards/m/MageDuel.java new file mode 100644 index 000000000000..e776bf15e526 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MageDuel.java @@ -0,0 +1,84 @@ +package mage.cards.m; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.effects.common.FightTargetsEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.watchers.common.SpellsCastWatcher; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MageDuel extends CardImpl { + + private static final Hint hint = new ConditionHint( + MageDuelCondition.instance, "You've cast an instant or sorcery this turn" + ); + + public MageDuel(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{G}"); + + // This spell costs {2} less to cast if you've cast another instant or sorcery spell this turn. + this.addAbility(new SimpleStaticAbility( + Zone.ALL, new SpellCostReductionSourceEffect(2, MageDuelCondition.instance).setCanWorksOnStackOnly(true) + ).setRuleAtTheTop(true)); + + // Target creature you control gets +1/+2 until end of turn. Then it fights target creature you don't control. + this.getSpellAbility().addEffect(new BoostTargetEffect(1, 2)); + this.getSpellAbility().addEffect(new FightTargetsEffect().setText("Then it fights target creature you don't control")); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); + this.getSpellAbility().addWatcher(new SpellsCastWatcher()); + } + + private MageDuel(final MageDuel card) { + super(card); + } + + @Override + public MageDuel copy() { + return new MageDuel(this); + } +} + +enum MageDuelCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class); + if (watcher == null) { + return false; + } + List spells = watcher.getSpellsCastThisTurn(source.getControllerId()); + return spells != null && spells + .stream() + .filter(Objects::nonNull) + .filter(MageObject::isInstantOrSorcery) + .map(Spell::getSourceId) + .anyMatch(source.getSourceId()::equals); + } + + @Override + public String toString() { + return "you've cast another instant or sorcery spell this turn"; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MageHunter.java b/Mage.Sets/src/mage/cards/m/MageHunter.java new file mode 100644 index 000000000000..5bb0320092c8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MageHunter.java @@ -0,0 +1,81 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MageHunter extends CardImpl { + + public MageHunter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.HORROR); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Whenever an opponent casts or copies an instant or sorcery spell, they lose 1 life. + this.addAbility(new MageHunterTriggeredAbility()); + } + + private MageHunter(final MageHunter card) { + super(card); + } + + @Override + public MageHunter copy() { + return new MageHunter(this); + } +} + +class MageHunterTriggeredAbility extends TriggeredAbilityImpl { + + MageHunterTriggeredAbility() { + super(Zone.BATTLEFIELD, new LoseLifeTargetEffect(1), false); + } + + private MageHunterTriggeredAbility(final MageHunterTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.COPIED_STACKOBJECT + || event.getType() == GameEvent.EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Spell spell = game.getSpell(event.getTargetId()); + if (spell == null + || !game.getOpponents(getControllerId()).contains(spell.getControllerId()) + || !spell.isInstantOrSorcery()) { + return false; + } + getEffects().setTargetPointer(new FixedTarget(spell.getControllerId())); + return true; + } + + @Override + public String getRule() { + return "Whenever an opponent casts or copies an instant or sorcery spell, they lose 1 life."; + } + + @Override + public MageHunterTriggeredAbility copy() { + return new MageHunterTriggeredAbility(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MageHuntersOnslaught.java b/Mage.Sets/src/mage/cards/m/MageHuntersOnslaught.java new file mode 100644 index 000000000000..7b512387e3e9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MageHuntersOnslaught.java @@ -0,0 +1,81 @@ +package mage.cards.m; + +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreatureOrPlaneswalker; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MageHuntersOnslaught extends CardImpl { + + public MageHuntersOnslaught(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}{B}"); + + // Destroy target creature or planeswalker. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); + + // Whenever a creature blocks this turn, its controller loses 1 life. + this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect( + new MageHuntersOnslaughtDelayedTriggeredAbility() + ).concatBy("
")); + } + + private MageHuntersOnslaught(final MageHuntersOnslaught card) { + super(card); + } + + @Override + public MageHuntersOnslaught copy() { + return new MageHuntersOnslaught(this); + } +} + +class MageHuntersOnslaughtDelayedTriggeredAbility extends DelayedTriggeredAbility { + + MageHuntersOnslaughtDelayedTriggeredAbility() { + super(new LoseLifeTargetEffect(1), Duration.EndOfTurn, false, false); + } + + private MageHuntersOnslaughtDelayedTriggeredAbility(final MageHuntersOnslaughtDelayedTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.BLOCKER_DECLARED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent permanent = game.getPermanent(event.getSourceId()); + if (permanent == null) { + return false; + } + getEffects().setTargetPointer(new FixedTarget(permanent.getControllerId())); + return true; + } + + @Override + public MageHuntersOnslaughtDelayedTriggeredAbility copy() { + return new MageHuntersOnslaughtDelayedTriggeredAbility(this); + } + + @Override + public String getRule() { + return "Whenever a creature blocks this turn, its controller loses 1 life."; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MageRingNetwork.java b/Mage.Sets/src/mage/cards/m/MageRingNetwork.java index 5d9bfaa43bb9..935e6f0fb9cf 100644 --- a/Mage.Sets/src/mage/cards/m/MageRingNetwork.java +++ b/Mage.Sets/src/mage/cards/m/MageRingNetwork.java @@ -44,7 +44,7 @@ public MageRingNetwork(UUID ownerId, CardSetInfo setInfo) { "Add {C} for each storage counter removed this way", true, new CountersSourceCount(CounterType.STORAGE)); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.STORAGE.createInstance(), - "Remove X storage counters from {this}")); + "Remove any number of storage counters from {this}")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/MagesContest.java b/Mage.Sets/src/mage/cards/m/MagesContest.java index 3164d6732bac..317062d274af 100644 --- a/Mage.Sets/src/mage/cards/m/MagesContest.java +++ b/Mage.Sets/src/mage/cards/m/MagesContest.java @@ -70,13 +70,15 @@ public boolean apply(Game game, Ability source) { do { if (currentPlayer.canRespond()) { int newBid = 0; - if (!currentPlayer.isHuman()) { + if (currentPlayer.isComputer()) { + // AI hint // make AI evaluate value of the spell to decide on bidding, should be reworked - int maxBid = Math.min(RandomUtil.nextInt(Math.max(currentPlayer.getLife(), 1)) + RandomUtil.nextInt(Math.max(spell.getConvertedManaCost(), 1)), currentPlayer.getLife()); + int maxBid = Math.min(RandomUtil.nextInt(Math.max(currentPlayer.getLife(), 1)) + RandomUtil.nextInt(Math.max(spell.getManaValue(), 1)), currentPlayer.getLife()); if (highBid + 1 < maxBid) { newBid = highBid + 1; } } else if (currentPlayer.chooseUse(Outcome.Benefit, winner.getLogName() + " has bet " + highBid + " life. Top the bid?", source, game)) { + // Human choose newBid = currentPlayer.getAmount(highBid + 1, Integer.MAX_VALUE, "Choose bid", game); } if (newBid > highBid) { diff --git a/Mage.Sets/src/mage/cards/m/MagetaTheLion.java b/Mage.Sets/src/mage/cards/m/MagetaTheLion.java index f67569aa6f40..ac7e6ffb6141 100644 --- a/Mage.Sets/src/mage/cards/m/MagetaTheLion.java +++ b/Mage.Sets/src/mage/cards/m/MagetaTheLion.java @@ -17,7 +17,7 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCardInHand; /** diff --git a/Mage.Sets/src/mage/cards/m/MagisterOfWorth.java b/Mage.Sets/src/mage/cards/m/MagisterOfWorth.java index fe686d6dc414..119266926e25 100644 --- a/Mage.Sets/src/mage/cards/m/MagisterOfWorth.java +++ b/Mage.Sets/src/mage/cards/m/MagisterOfWorth.java @@ -1,30 +1,33 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyAllEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.choices.TwoChoiceVote; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.players.Player; +import java.util.Collection; +import java.util.Objects; +import java.util.UUID; + /** - * - * @author fireshoes + * @author fireshoes, TheElk801 */ public final class MagisterOfWorth extends CardImpl { @@ -38,9 +41,7 @@ public MagisterOfWorth(UUID ownerId, CardSetInfo setInfo) { this.addAbility(FlyingAbility.getInstance()); // Will of the council - When Magister of Worth enters the battlefield, starting with you, each player votes for grace or condemnation. If grace gets more votes, each player returns each creature card from their graveyard to the battlefield. If condemnation gets more votes or the vote is tied, destroy all creatures other than Magister of Worth. - Effect effect = new MagisterOfWorthVoteEffect(); - effect.setText("Will of the council - When Magister of Worth enters the battlefield, starting with you, each player votes for grace or condemnation. If grace gets more votes, each player returns each creature card from their graveyard to the battlefield. If condemnation gets more votes or the vote is tied, destroy all creatures other than Magister of Worth"); - this.addAbility(new EntersBattlefieldTriggeredAbility(effect, false, true)); + this.addAbility(new EntersBattlefieldTriggeredAbility(new MagisterOfWorthEffect(), false, true)); } private MagisterOfWorth(final MagisterOfWorth card) { @@ -53,83 +54,65 @@ public MagisterOfWorth copy() { } } -class MagisterOfWorthVoteEffect extends OneShotEffect { +class MagisterOfWorthEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(AnotherPredicate.instance); + } - MagisterOfWorthVoteEffect() { + MagisterOfWorthEffect() { super(Outcome.Benefit); - this.staticText = "Will of the council — When {this} enters the battlefield, starting with you, each player votes for grace or condemnation. If grace gets more votes, each player returns each creature card from their graveyard to the battlefield. If condemnation gets more votes or the vote is tied, destroy all creatures other than {this}."; + staticText = "Will of the council — When {this} enters the battlefield, " + + "starting with you, each player votes for grace or condemnation. " + + "If grace gets more votes, each player returns each creature card from their graveyard to the battlefield. " + + "If condemnation gets more votes or the vote is tied, destroy all creatures other than {this}."; } - MagisterOfWorthVoteEffect(final MagisterOfWorthVoteEffect effect) { + private MagisterOfWorthEffect(final MagisterOfWorthEffect effect) { super(effect); } @Override - public MagisterOfWorthVoteEffect copy() { - return new MagisterOfWorthVoteEffect(this); + public MagisterOfWorthEffect copy() { + return new MagisterOfWorthEffect(this); } @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - int graceCount = 0; - int condemnationCount = 0; - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - if (player.chooseUse(Outcome.DestroyPermanent, "Choose grace?", source, game)) { - graceCount++; - game.informPlayers(player.getLogName() + " has chosen: grace"); - } else { - condemnationCount++; - game.informPlayers(player.getLogName() + " has chosen: condemnation"); - } - } - } - if (graceCount > condemnationCount) { - new MagisterOfWorthReturnFromGraveyardEffect().apply(game, source); - } else { - FilterPermanent filter = new FilterCreaturePermanent("creatures other than {this}"); - filter.add(AnotherPredicate.instance); - new DestroyAllEffect(filter).apply(game, source); - } - return true; + if (controller == null) { + return false; } - return false; - } -} -class MagisterOfWorthReturnFromGraveyardEffect extends OneShotEffect { - - public MagisterOfWorthReturnFromGraveyardEffect() { - super(Outcome.PutCreatureInPlay); - staticText = "each player returns each creature card from their graveyard to the battlefield"; - } + // Outcome.Benefit - AI will return from graveyard all the time (Grace choice) + // TODO: add AI hint logic in the choice method, see Tyrant's Choice as example + TwoChoiceVote vote = new TwoChoiceVote("Grace (return from graveyard)", "Condemnation (destroy all)", Outcome.Benefit); + vote.doVotes(source, game); - public MagisterOfWorthReturnFromGraveyardEffect(final MagisterOfWorthReturnFromGraveyardEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (controller != null && sourceObject != null) { - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - player.moveCards(player.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURE, game), Zone.BATTLEFIELD, source, game); - } - } - return true; + int graceCount = vote.getVoteCount(true); + int condemnationCount = vote.getVoteCount(false); + if (condemnationCount >= graceCount) { + return new DestroyAllEffect(filter).apply(game, source); } - return false; - } - @Override - public MagisterOfWorthReturnFromGraveyardEffect copy() { - return new MagisterOfWorthReturnFromGraveyardEffect(this); + // grace win - each player returns each creature card from their graveyard to the battlefield + Cards cards = new CardsImpl(); + game.getState() + .getPlayersInRange(source.getControllerId(), game) + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .map(Player::getGraveyard) + .map(g -> g.getCards(game)) + .flatMap(Collection::stream) + .filter(Objects::nonNull) + .filter(MageObject::isCreature) + .forEach(cards::add); + return controller.moveCards( + cards.getCards(game), Zone.BATTLEFIELD, source, game, + false, false, true, null + ); } - } diff --git a/Mage.Sets/src/mage/cards/m/MagmaOpus.java b/Mage.Sets/src/mage/cards/m/MagmaOpus.java new file mode 100644 index 000000000000..61a85f3d2c83 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MagmaOpus.java @@ -0,0 +1,89 @@ +package mage.cards.m; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DamageMultiEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.PrismariToken; +import mage.game.permanent.token.TreasureToken; +import mage.target.TargetPermanent; +import mage.target.common.TargetAnyTargetAmount; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MagmaOpus extends CardImpl { + + public MagmaOpus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{6}{U}{R}"); + + // Magma Opus deals 4 damage divided as you choose among any number of targets. Tap two target permanents. Create a 4/4 blue and red Elemental creature token. Draw two cards. + this.getSpellAbility().addEffect(new DamageMultiEffect(4)); + this.getSpellAbility().addTarget(new TargetAnyTargetAmount(4).withChooseHint("damage")); + this.getSpellAbility().addEffect(new MagmaOpusTapEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(2, StaticFilters.FILTER_PERMANENTS).withChooseHint("tap")); + this.getSpellAbility().addEffect(new CreateTokenEffect(new PrismariToken())); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2).setText("Draw two cards")); + + // {U/R}{U/R}, Discard Magma Opus: Create a Treasure token. + Ability ability = new SimpleActivatedAbility( + Zone.HAND, new CreateTokenEffect(new TreasureToken()), new ManaCostsImpl("{U/R}{U/R}") + ); + ability.addCost(new DiscardSourceCost()); + this.addAbility(ability); + } + + private MagmaOpus(final MagmaOpus card) { + super(card); + } + + @Override + public MagmaOpus copy() { + return new MagmaOpus(this); + } +} + +class MagmaOpusTapEffect extends OneShotEffect { + + MagmaOpusTapEffect() { + super(Outcome.Benefit); + staticText = "Tap two target permanents."; + } + + private MagmaOpusTapEffect(final MagmaOpusTapEffect effect) { + super(effect); + } + + @Override + public MagmaOpusTapEffect copy() { + return new MagmaOpusTapEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (UUID targetId : getTargetPointer().getTargets(game, source)) { + if (!source.getTargets().get(1).getTargets().contains(targetId)) { + continue; + } + Permanent permanent = game.getPermanent(targetId); + if (permanent != null) { + permanent.tap(source, game); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MagusOfTheFuture.java b/Mage.Sets/src/mage/cards/m/MagusOfTheFuture.java index 31ad2a0d6745..f03135109c76 100644 --- a/Mage.Sets/src/mage/cards/m/MagusOfTheFuture.java +++ b/Mage.Sets/src/mage/cards/m/MagusOfTheFuture.java @@ -1,7 +1,5 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.PlayTheTopCardEffect; @@ -12,14 +10,15 @@ import mage.constants.SubType; import mage.constants.Zone; +import java.util.UUID; + /** - * * @author emerald000 */ public final class MagusOfTheFuture extends CardImpl { public MagusOfTheFuture(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{U}{U}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WIZARD); @@ -29,7 +28,7 @@ public MagusOfTheFuture(UUID ownerId, CardSetInfo setInfo) { // Play with the top card of your library revealed. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayWithTheTopCardRevealedEffect())); - // You may play the top card of your library. + // You may play lands and cast spells from the top of your library. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayTheTopCardEffect())); } diff --git a/Mage.Sets/src/mage/cards/m/MagusOfTheJar.java b/Mage.Sets/src/mage/cards/m/MagusOfTheJar.java index 1f495f3b2f75..5e1d5d9046b3 100644 --- a/Mage.Sets/src/mage/cards/m/MagusOfTheJar.java +++ b/Mage.Sets/src/mage/cards/m/MagusOfTheJar.java @@ -1,23 +1,23 @@ package mage.cards.m; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.cards.*; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.Zone; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.*; +import mage.filter.FilterCard; import mage.game.Game; -import mage.game.events.GameEvent; import mage.players.Player; -import java.util.Iterator; +import java.util.Objects; import java.util.UUID; /** @@ -34,10 +34,9 @@ public MagusOfTheJar(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(3); // {tap}, Sacrifice Magus of the Jar: Each player exiles all cards from their hand face down and draws seven cards. At the beginning of the next end step, each player discards their hand and returns to their hand each card they exiled this way. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MagusoftheJarEffect(), new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(new MagusoftheJarEffect(), new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); - } private MagusOfTheJar(final MagusOfTheJar card) { @@ -52,32 +51,32 @@ public MagusOfTheJar copy() { class MagusoftheJarEffect extends OneShotEffect { - public MagusoftheJarEffect() { + MagusoftheJarEffect() { super(Outcome.DrawCard); - staticText = "Each player exiles all cards from their hand face down and draws seven cards. At the beginning of the next end step, each player discards their hand and returns to their hand each card they exiled this way."; + staticText = "Each player exiles all cards from their hand face down and draws seven cards. " + + "At the beginning of the next end step, each player discards their hand " + + "and returns to their hand each card they exiled this way."; } - public MagusoftheJarEffect(final MagusoftheJarEffect effect) { + private MagusoftheJarEffect(final MagusoftheJarEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { + MageObject sourceObject = source.getSourceObject(game); + if (sourceObject == null) { + return false; + } Cards cards = new CardsImpl(); //Exile hand for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { Player player = game.getPlayer(playerId); - if (player != null) { - Cards handCards = new CardsImpl(player.getHand()); - for (UUID cardId : handCards) { - Card card = handCards.get(cardId, game); - if (card != null) { - card.moveToExile(getId(), "Magus of the Jar", source, game); - card.setFaceDown(true, game); - cards.add(card); - } - } + if (player == null) { + continue; } + cards.addAll(player.getHand()); + player.moveCards(player.getHand(), Zone.EXILED, source, game); } //Draw 7 cards for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { @@ -86,10 +85,10 @@ public boolean apply(Game game, Ability source) { player.drawCards(7, source, game); } } + cards.removeIf(uuid -> game.getState().getZone(uuid) != Zone.EXILED); + cards.getCards(game).stream().filter(Objects::nonNull).forEach(card -> card.setFaceDown(true, game)); //Delayed ability - Effect effect = new MagusoftheJarDelayedEffect(); - effect.setValue("MagusoftheJarCards", cards); - game.addDelayedTriggeredAbility(new MagusoftheJarDelayedTriggeredAbility(effect), source); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new MagusoftheJarDelayedEffect(cards)), source); return true; } @@ -101,13 +100,23 @@ public MagusoftheJarEffect copy() { class MagusoftheJarDelayedEffect extends OneShotEffect { - public MagusoftheJarDelayedEffect() { + private static final FilterCard filter = new FilterCard(); + + static { + filter.add(TargetController.YOU.getOwnerPredicate()); + } + + private final Cards cards = new CardsImpl(); + + MagusoftheJarDelayedEffect(Cards cards) { super(Outcome.DrawCard); - staticText = "At the beginning of the next end step, each player discards their hand and returns to their hand each card they exiled this way"; + this.cards.addAll(cards); + staticText = "each player discards their hand and returns to their hand each card they exiled this way"; } - public MagusoftheJarDelayedEffect(final MagusoftheJarDelayedEffect effect) { + private MagusoftheJarDelayedEffect(final MagusoftheJarDelayedEffect effect) { super(effect); + this.cards.addAll(effect.cards); } @Override @@ -117,51 +126,14 @@ public MagusoftheJarDelayedEffect copy() { @Override public boolean apply(Game game, Ability source) { - Cards cards = (Cards) this.getValue("MagusoftheJarCards"); - - if (cards != null) { - //Discard - for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - player.discard(player.getHand().size(), false, false, source, game); - } - } - //Return to hand - for (Iterator it = cards.getCards(game).iterator(); it.hasNext(); ) { - Card card = it.next(); - card.moveToZone(Zone.HAND, source, game, true); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; } - return true; + player.discard(player.getHand(), false, source, game); + player.moveCards(cards.getCards(filter, source.getSourceId(), playerId, game), Zone.HAND, source, game); } - return false; - } - -} - -class MagusoftheJarDelayedTriggeredAbility extends DelayedTriggeredAbility { - - public MagusoftheJarDelayedTriggeredAbility(Effect effect) { - super(effect); - } - - public MagusoftheJarDelayedTriggeredAbility(final MagusoftheJarDelayedTriggeredAbility ability) { - super(ability); - } - - @Override - public MagusoftheJarDelayedTriggeredAbility copy() { - return new MagusoftheJarDelayedTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.END_TURN_STEP_PRE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { return true; } - } diff --git a/Mage.Sets/src/mage/cards/m/MagusOfTheMirror.java b/Mage.Sets/src/mage/cards/m/MagusOfTheMirror.java index a79dd4cec994..3b348e2cbcd6 100644 --- a/Mage.Sets/src/mage/cards/m/MagusOfTheMirror.java +++ b/Mage.Sets/src/mage/cards/m/MagusOfTheMirror.java @@ -8,7 +8,7 @@ import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.decorator.ConditionalActivatedAbility; -import mage.abilities.effects.common.ExchangeLifeTargetEffect; +import mage.abilities.effects.common.ExchangeLifeControllerTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -33,7 +33,7 @@ public MagusOfTheMirror(UUID ownerId, CardSetInfo setInfo) { // {tap}, Sacrifice Magus of the Mirror: Exchange life totals with target opponent. Activate this ability only during your upkeep. Ability ability = new ConditionalActivatedAbility( Zone.BATTLEFIELD, - new ExchangeLifeTargetEffect(), + new ExchangeLifeControllerTargetEffect(), new TapSourceCost(), new IsStepCondition(PhaseStep.UPKEEP), null); diff --git a/Mage.Sets/src/mage/cards/m/MagusOfTheOrder.java b/Mage.Sets/src/mage/cards/m/MagusOfTheOrder.java index 6a2bb7b977ed..6f88d216073a 100644 --- a/Mage.Sets/src/mage/cards/m/MagusOfTheOrder.java +++ b/Mage.Sets/src/mage/cards/m/MagusOfTheOrder.java @@ -18,7 +18,7 @@ import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetControlledPermanent; diff --git a/Mage.Sets/src/mage/cards/m/MajesticHeliopterus.java b/Mage.Sets/src/mage/cards/m/MajesticHeliopterus.java index 99d2ed446963..0a12d5963427 100644 --- a/Mage.Sets/src/mage/cards/m/MajesticHeliopterus.java +++ b/Mage.Sets/src/mage/cards/m/MajesticHeliopterus.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.constants.Duration; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.Target; import mage.target.common.TargetControlledCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/m/MakeAWish.java b/Mage.Sets/src/mage/cards/m/MakeAWish.java index f4c9f65a13d2..6ccc5a56dd1f 100644 --- a/Mage.Sets/src/mage/cards/m/MakeAWish.java +++ b/Mage.Sets/src/mage/cards/m/MakeAWish.java @@ -1,21 +1,22 @@ - package mage.cards.m; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; /** - * * @author North */ public final class MakeAWish extends CardImpl { @@ -39,12 +40,12 @@ public MakeAWish copy() { class MakeAWishEffect extends OneShotEffect { - public MakeAWishEffect() { + MakeAWishEffect() { super(Outcome.ReturnToHand); this.staticText = "Return two cards at random from your graveyard to your hand"; } - public MakeAWishEffect(final MakeAWishEffect effect) { + private MakeAWishEffect(final MakeAWishEffect effect) { super(effect); } @@ -56,20 +57,13 @@ public MakeAWishEffect copy() { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - Cards cards = player.getGraveyard(); - for (int i = 0; i < 2 && !cards.isEmpty(); i++) { - Card card = cards.getRandom(game); - if (card != null) { - card.moveToZone(Zone.HAND, source, game, true); - cards.remove(card); - game.informPlayers(card.getName() + " returned to the hand of " + player.getLogName()); - } else { - return false; - } - } - return true; + if (player == null || player.getGraveyard().isEmpty()) { + return false; } - return false; + TargetCard target = new TargetCardInYourGraveyard(Math.min(player.getGraveyard().size(), 2), StaticFilters.FILTER_CARD); + target.setNotTarget(true); + target.setRandom(true); + player.chooseTarget(outcome, target, source, game); + return player.moveCards(new CardsImpl(target.getTargets()), Zone.HAND, source, game); } } diff --git a/Mage.Sets/src/mage/cards/m/MakeYourMark.java b/Mage.Sets/src/mage/cards/m/MakeYourMark.java new file mode 100644 index 000000000000..8a4d85a2a4fa --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MakeYourMark.java @@ -0,0 +1,107 @@ +package mage.cards.m; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.LoreholdToken; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MakeYourMark extends CardImpl { + + public MakeYourMark(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R/W}"); + + // Target creature gets +1/+0 until end of turn. When that creature dies this turn, create a 3/2 red and white Spirit creature token. + this.getSpellAbility().addEffect(new BoostTargetEffect(1, 0)); + this.getSpellAbility().addEffect(new MakeYourMarkEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private MakeYourMark(final MakeYourMark card) { + super(card); + } + + @Override + public MakeYourMark copy() { + return new MakeYourMark(this); + } +} + +class MakeYourMarkEffect extends OneShotEffect { + + MakeYourMarkEffect() { + super(Outcome.Benefit); + staticText = "When that creature dies this turn, create a 3/2 red and white Spirit creature token."; + } + + private MakeYourMarkEffect(final MakeYourMarkEffect effect) { + super(effect); + } + + @Override + public MakeYourMarkEffect copy() { + return new MakeYourMarkEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + game.addDelayedTriggeredAbility(new MakeYourMarkDelayedTriggeredAbility(permanent, game), source); + return true; + } +} + +class MakeYourMarkDelayedTriggeredAbility extends DelayedTriggeredAbility { + + private final MageObjectReference mor; + + MakeYourMarkDelayedTriggeredAbility(Permanent permanent, Game game) { + super(new CreateTokenEffect(new LoreholdToken()), Duration.EndOfTurn, false, false); + this.mor = new MageObjectReference(permanent, game); + } + + private MakeYourMarkDelayedTriggeredAbility(final MakeYourMarkDelayedTriggeredAbility ability) { + super(ability); + this.mor = ability.mor; + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return mor.refersTo(((ZoneChangeEvent) event).getTarget(), game); + } + + @Override + public MakeYourMarkDelayedTriggeredAbility copy() { + return new MakeYourMarkDelayedTriggeredAbility(this); + } + + @Override + public String getRule() { + return "When that creature dies this turn, create a 3/2 red and white Spirit creature token."; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/m/MaliciousAffliction.java b/Mage.Sets/src/mage/cards/m/MaliciousAffliction.java index 35527d44582b..2922c55323e4 100644 --- a/Mage.Sets/src/mage/cards/m/MaliciousAffliction.java +++ b/Mage.Sets/src/mage/cards/m/MaliciousAffliction.java @@ -1,30 +1,23 @@ - package mage.cards.m; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.Ability; -import mage.abilities.condition.LockedInCondition; import mage.abilities.condition.common.MorbidCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CastSourceTriggeredAbility; +import mage.abilities.effects.common.CopySourceSpellEffect; import mage.abilities.effects.common.DestroyTargetEffect; 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.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.Game; -import mage.game.stack.Spell; -import mage.game.stack.StackObject; -import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class MaliciousAffliction extends CardImpl { @@ -36,13 +29,14 @@ public final class MaliciousAffliction extends CardImpl { } public MaliciousAffliction(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}{B}"); // Morbid — When you cast Malicious Affliction, if a creature died this turn, you may copy Malicious Affliction and may choose a new target for the copy. Ability ability = new ConditionalInterveningIfTriggeredAbility( new CastSourceTriggeredAbility(new CopySourceSpellEffect(), true), - new LockedInCondition(MorbidCondition.instance), - "Morbid — When you cast {this}, if a creature died this turn, you may copy {this} and may choose a new target for the copy"); + MorbidCondition.instance, "Morbid — When you cast this spell, " + + "if a creature died this turn, you may copy {this} and may choose a new target for the copy" + ); ability.setRuleAtTheTop(true); this.addAbility(ability); @@ -60,42 +54,3 @@ public MaliciousAffliction copy() { return new MaliciousAffliction(this); } } - -class CopySourceSpellEffect extends OneShotEffect { - - static final String rule = "copy {this} and may choose a new target for the copy"; - - public CopySourceSpellEffect() { - super(Outcome.Benefit); - staticText = rule; - } - - public CopySourceSpellEffect(final CopySourceSpellEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Spell spell = game.getStack().getSpell(source.getSourceId()); - if (spell != null) { - StackObject stackObjectCopy = spell.createCopyOnStack(game, source, source.getControllerId(), true); - if (stackObjectCopy instanceof Spell) { - String activateMessage = ((Spell) stackObjectCopy).getActivatedMessage(game); - if (activateMessage.startsWith(" casts ")) { - activateMessage = activateMessage.substring(6); - } - game.informPlayers(controller.getLogName() + " copies " + activateMessage); - } - return true; - } - } - return false; - } - - @Override - public CopySourceSpellEffect copy() { - return new CopySourceSpellEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/m/ManaBreach.java b/Mage.Sets/src/mage/cards/m/ManaBreach.java index 2cba39a5ce8c..9134774fdfeb 100644 --- a/Mage.Sets/src/mage/cards/m/ManaBreach.java +++ b/Mage.Sets/src/mage/cards/m/ManaBreach.java @@ -1,7 +1,5 @@ - package mage.cards.m; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SpellCastAllTriggeredAbility; import mage.abilities.effects.OneShotEffect; @@ -12,25 +10,24 @@ import mage.constants.SetTargetPointer; import mage.constants.Zone; import mage.filter.StaticFilters; -import mage.filter.common.FilterLandPermanent; -import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.common.TargetLandPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author LoneFox - */ public final class ManaBreach extends CardImpl { public ManaBreach(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); // Whenever a player casts a spell, that player returns a land they control to its owner's hand. - this.addAbility(new SpellCastAllTriggeredAbility(new ManaBreachEffect(), StaticFilters.FILTER_SPELL, false, SetTargetPointer.PLAYER)); + this.addAbility(new SpellCastAllTriggeredAbility( + new ManaBreachEffect(), StaticFilters.FILTER_SPELL_A, false, SetTargetPointer.PLAYER + )); } private ManaBreach(final ManaBreach card) { @@ -45,32 +42,32 @@ public ManaBreach copy() { class ManaBreachEffect extends OneShotEffect { - public ManaBreachEffect() { - super(Outcome.Detriment); - staticText="that player returns a land they control to its owner's hand."; - } - - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(targetPointer.getFirst(game, source)); - if(player != null) { - FilterLandPermanent filter = new FilterLandPermanent("a land you control"); - filter.add(new ControllerIdPredicate(player.getId())); - TargetLandPermanent toBounce = new TargetLandPermanent(1, 1, filter, true); - if(player.chooseTarget(Outcome.ReturnToHand, toBounce, source, game)) { - Permanent land = game.getPermanent(toBounce.getTargets().get(0)); - land.moveToZone(Zone.HAND, source, game, false); - } - return true; - } - return false; + ManaBreachEffect() { + super(Outcome.Benefit); + staticText = "that player returns a land they control to its owner's hand"; } - public ManaBreachEffect(final ManaBreachEffect effect) { + private ManaBreachEffect(final ManaBreachEffect effect) { super(effect); } + @Override public ManaBreachEffect copy() { return new ManaBreachEffect(this); } + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(targetPointer.getFirst(game, source)); + if (player == null || game.getBattlefield().count( + StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, + source.getSourceId(), player.getId(), game + ) < 1) { + return false; + } + TargetPermanent target = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND); + target.setNotTarget(true); + player.choose(outcome, target, source.getSourceId(), game); + return player.moveCards(game.getPermanent(target.getFirstTarget()), Zone.HAND, source, game); + } } diff --git a/Mage.Sets/src/mage/cards/m/ManaDrain.java b/Mage.Sets/src/mage/cards/m/ManaDrain.java index 45bbe97f0b9b..b0a6b5e116ff 100644 --- a/Mage.Sets/src/mage/cards/m/ManaDrain.java +++ b/Mage.Sets/src/mage/cards/m/ManaDrain.java @@ -47,7 +47,7 @@ class ManaDrainCounterEffect extends OneShotEffect { public ManaDrainCounterEffect() { super(Outcome.Benefit); - this.staticText = "Counter target spell. At the beginning of your next main phase, add an amount of {C} equal to that spell's converted mana cost"; + this.staticText = "Counter target spell. At the beginning of your next main phase, add an amount of {C} equal to that spell's mana value"; } public ManaDrainCounterEffect(final ManaDrainCounterEffect effect) { @@ -65,7 +65,7 @@ public boolean apply(Game game, Ability source) { if (spell != null) { game.getStack().counter(getTargetPointer().getFirst(game, source), source, game); // mana gets added also if counter is not successful - int cmc = spell.getConvertedManaCost(); + int cmc = spell.getManaValue(); Effect effect = new AddManaToManaPoolTargetControllerEffect(Mana.ColorlessMana(cmc), "your"); effect.setTargetPointer(new FixedTarget(source.getControllerId())); AtTheBeginOfMainPhaseDelayedTriggeredAbility delayedAbility diff --git a/Mage.Sets/src/mage/cards/m/ManaScrew.java b/Mage.Sets/src/mage/cards/m/ManaScrew.java index 6b106148928f..b77ee4d178be 100644 --- a/Mage.Sets/src/mage/cards/m/ManaScrew.java +++ b/Mage.Sets/src/mage/cards/m/ManaScrew.java @@ -66,7 +66,7 @@ public ManaScrewAbility copy() { @Override public String getRule() { - return super.getRule() + " Activate this ability only any time you could cast an instant."; + return super.getRule() + " Activate only as an instant."; } } diff --git a/Mage.Sets/src/mage/cards/m/ManaSeverance.java b/Mage.Sets/src/mage/cards/m/ManaSeverance.java index ad111fbc2a72..86ae4527ef03 100644 --- a/Mage.Sets/src/mage/cards/m/ManaSeverance.java +++ b/Mage.Sets/src/mage/cards/m/ManaSeverance.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.cards.m; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/m/Manaplasm.java b/Mage.Sets/src/mage/cards/m/Manaplasm.java index 552136c85fb9..f83ef347dbd6 100644 --- a/Mage.Sets/src/mage/cards/m/Manaplasm.java +++ b/Mage.Sets/src/mage/cards/m/Manaplasm.java @@ -14,7 +14,6 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.stack.Spell; import mage.target.targetpointer.FixedTarget; @@ -50,7 +49,7 @@ public Manaplasm copy() { class ManaplasmAbility extends TriggeredAbilityImpl { public ManaplasmAbility() { - super(Zone.BATTLEFIELD, new InfoEffect("{this} gets +X/+X until end of turn, where X is that spell's converted mana cost"), false); + super(Zone.BATTLEFIELD, new InfoEffect("{this} gets +X/+X until end of turn, where X is that spell's mana value"), false); } @@ -69,7 +68,7 @@ public boolean checkTrigger(GameEvent event, Game game) { Spell spell = game.getStack().getSpell(event.getTargetId()); if (spell != null && spell.isControlledBy(controllerId)) { this.getEffects().remove(0); - int x = spell.getConvertedManaCost(); + int x = spell.getManaValue(); this.addEffect(new BoostSourceEffect(x,x, Duration.EndOfTurn)); this.getEffects().get(0).setTargetPointer(new FixedTarget(event.getPlayerId())); return true; @@ -79,7 +78,7 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { - return "Whenever you cast a spell, {this} gets +X/+X until end of turn, where X is that spell's converted mana cost"; + return "Whenever you cast a spell, {this} gets +X/+X until end of turn, where X is that spell's mana value"; } @Override diff --git a/Mage.Sets/src/mage/cards/m/ManascapeRefractor.java b/Mage.Sets/src/mage/cards/m/ManascapeRefractor.java index f2b01168acc8..1dd80ca1346f 100644 --- a/Mage.Sets/src/mage/cards/m/ManascapeRefractor.java +++ b/Mage.Sets/src/mage/cards/m/ManascapeRefractor.java @@ -12,7 +12,7 @@ import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterLandPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.ManaPoolItem; diff --git a/Mage.Sets/src/mage/cards/m/ManifestationSage.java b/Mage.Sets/src/mage/cards/m/ManifestationSage.java new file mode 100644 index 000000000000..93a4fd74fe64 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/ManifestationSage.java @@ -0,0 +1,42 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.common.CardsInControllerHandCount; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.QuandrixToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ManifestationSage extends CardImpl { + + public ManifestationSage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G/U}{G/U}{G/U}{G/U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // When Manifestation Sage enters the battlefield, create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it, where X is the number of cards in your hand. + this.addAbility(new EntersBattlefieldTriggeredAbility(QuandrixToken.getEffect( + CardsInControllerHandCount.instance, "Put X +1/+1 counters on it, " + + "where X is the number of cards in your hand" + ))); + } + + private ManifestationSage(final ManifestationSage card) { + super(card); + } + + @Override + public ManifestationSage copy() { + return new ManifestationSage(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/ManifoldKey.java b/Mage.Sets/src/mage/cards/m/ManifoldKey.java index 87cb68873c87..945e196d43d3 100644 --- a/Mage.Sets/src/mage/cards/m/ManifoldKey.java +++ b/Mage.Sets/src/mage/cards/m/ManifoldKey.java @@ -12,7 +12,7 @@ import mage.constants.Duration; import mage.filter.FilterPermanent; import mage.filter.common.FilterArtifactPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/m/ManipulateFate.java b/Mage.Sets/src/mage/cards/m/ManipulateFate.java index 3be51deef356..ead32c27eb79 100644 --- a/Mage.Sets/src/mage/cards/m/ManipulateFate.java +++ b/Mage.Sets/src/mage/cards/m/ManipulateFate.java @@ -2,21 +2,20 @@ import mage.abilities.Ability; import mage.abilities.effects.SearchEffect; -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.filter.FilterCard; +import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; -import java.util.List; import java.util.UUID; /** - * * @author emerald000 */ public final class ManipulateFate extends CardImpl { @@ -26,7 +25,6 @@ public ManipulateFate(UUID ownerId, CardSetInfo setInfo) { // Search your library for three cards, exile them, then shuffle your library. this.getSpellAbility().addEffect(new ManipulateFateEffect()); - } private ManipulateFate(final ManipulateFate card) { @@ -42,12 +40,12 @@ public ManipulateFate copy() { class ManipulateFateEffect extends SearchEffect { ManipulateFateEffect() { - super(new TargetCardInLibrary(3, new FilterCard()), Outcome.Benefit); + super(new TargetCardInLibrary(3, StaticFilters.FILTER_CARD), Outcome.Benefit); staticText = "Search your library for three cards, exile them, " - + "then shuffle your library. Draw a card"; + + "then shuffle.
Draw a card"; } - ManipulateFateEffect(final ManipulateFateEffect effect) { + private ManipulateFateEffect(final ManipulateFateEffect effect) { super(effect); } @@ -59,24 +57,13 @@ public ManipulateFateEffect copy() { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - if (player.searchLibrary(target, source, game)) { - for (UUID targetId : getTargets()) { - Card card = player.getLibrary().getCard(targetId, game); - if (card != null) { - card.moveToExile(null, null, source, game); - } - } - } - player.shuffleLibrary(source, game); - player.drawCards(1, source, game); - return true; + if (player == null) { + return false; } - return false; - } - - public List getTargets() { - return target.getTargets(); + player.searchLibrary(target, source, game); + player.moveCards(new CardsImpl(target.getTargets()), Zone.EXILED, source, game); + player.shuffleLibrary(source, game); + player.drawCards(1, source, game); + return true; } - } diff --git a/Mage.Sets/src/mage/cards/m/MaralenOfTheMornsong.java b/Mage.Sets/src/mage/cards/m/MaralenOfTheMornsong.java index 5153502d4b06..ec03899ac023 100644 --- a/Mage.Sets/src/mage/cards/m/MaralenOfTheMornsong.java +++ b/Mage.Sets/src/mage/cards/m/MaralenOfTheMornsong.java @@ -86,7 +86,7 @@ class MaralenOfTheMornsongEffect2 extends OneShotEffect { public MaralenOfTheMornsongEffect2() { super(Outcome.LoseLife); - staticText = "that player loses 3 life, searches their library for a card, puts it into their hand, then shuffles their library"; + staticText = "that player loses 3 life, searches their library for a card, puts it into their hand, then shuffles"; } public MaralenOfTheMornsongEffect2(final MaralenOfTheMornsongEffect2 effect) { diff --git a/Mage.Sets/src/mage/cards/m/MarathWillOfTheWild.java b/Mage.Sets/src/mage/cards/m/MarathWillOfTheWild.java index 314802684565..c62667f144c1 100644 --- a/Mage.Sets/src/mage/cards/m/MarathWillOfTheWild.java +++ b/Mage.Sets/src/mage/cards/m/MarathWillOfTheWild.java @@ -1,7 +1,6 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.Mode; @@ -22,10 +21,9 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; @@ -35,8 +33,9 @@ import mage.target.common.TargetAnyTarget; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class MarathWillOfTheWild extends CardImpl { @@ -58,7 +57,7 @@ public MarathWillOfTheWild(UUID ownerId, CardSetInfo setInfo) { // {X}, Remove X +1/+1 counters from Marath: Choose one - Put X +1/+1 counters on target creature; effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance(0), ManacostVariableValue.instance); effect.setText("Put X +1/+1 counters on target creature"); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{X}")); + Ability ability = new SimpleActivatedAbility(effect, new ManaCostsImpl("{X}")); ability.addCost(new MarathWillOfTheWildRemoveCountersCost()); ability.addTarget(new TargetCreaturePermanent()); @@ -83,24 +82,6 @@ public MarathWillOfTheWild(UUID ownerId, CardSetInfo setInfo) { this.addAbility(ability); } - @Override - public void adjustCosts(Ability ability, Game game) { - if (ability instanceof SimpleActivatedAbility && ability.getModes().size() == 3) { - Permanent sourcePermanent = game.getPermanent(ability.getSourceId()); - if (sourcePermanent != null) { - int amount = sourcePermanent.getCounters(game).getCount(CounterType.P1P1); - if (amount > 0) { - for (VariableCost cost : ability.getManaCostsToPay().getVariableCosts()) { - if (cost instanceof VariableManaCost) { - ((VariableManaCost) cost).setMaxX(amount); - break; - } - } - } - } - } - } - private MarathWillOfTheWild(final MarathWillOfTheWild card) { super(card); } @@ -113,12 +94,12 @@ public MarathWillOfTheWild copy() { class MarathWillOfTheWildCreateTokenEffect extends OneShotEffect { - public MarathWillOfTheWildCreateTokenEffect() { + MarathWillOfTheWildCreateTokenEffect() { super(Outcome.PutCreatureInPlay); staticText = "create an X/X green Elemental creature token"; } - public MarathWillOfTheWildCreateTokenEffect(final MarathWillOfTheWildCreateTokenEffect effect) { + private MarathWillOfTheWildCreateTokenEffect(final MarathWillOfTheWildCreateTokenEffect effect) { super(effect); } @@ -144,12 +125,12 @@ public boolean apply(Game game, Ability source) { class MarathWillOfTheWildRemoveCountersCost extends CostImpl { - public MarathWillOfTheWildRemoveCountersCost() { + MarathWillOfTheWildRemoveCountersCost() { this.text = "Remove X +1/+1 counters from Marath"; } - public MarathWillOfTheWildRemoveCountersCost(MarathWillOfTheWildRemoveCountersCost cost) { + private MarathWillOfTheWildRemoveCountersCost(MarathWillOfTheWildRemoveCountersCost cost) { super(cost); } diff --git a/Mage.Sets/src/mage/cards/m/MaraudingBoneslasher.java b/Mage.Sets/src/mage/cards/m/MaraudingBoneslasher.java index e98c92e5cdda..53b1f98a0bb7 100644 --- a/Mage.Sets/src/mage/cards/m/MaraudingBoneslasher.java +++ b/Mage.Sets/src/mage/cards/m/MaraudingBoneslasher.java @@ -11,7 +11,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/m/MaraudingRaptor.java b/Mage.Sets/src/mage/cards/m/MaraudingRaptor.java index 7d28ae3ecbde..e73ec6b65051 100644 --- a/Mage.Sets/src/mage/cards/m/MaraudingRaptor.java +++ b/Mage.Sets/src/mage/cards/m/MaraudingRaptor.java @@ -14,7 +14,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterCreatureCard; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/m/MarchFromTheTomb.java b/Mage.Sets/src/mage/cards/m/MarchFromTheTomb.java index 4704ff205ce2..9288987ed6b1 100644 --- a/Mage.Sets/src/mage/cards/m/MarchFromTheTomb.java +++ b/Mage.Sets/src/mage/cards/m/MarchFromTheTomb.java @@ -28,7 +28,7 @@ public MarchFromTheTomb(UUID ownerId, CardSetInfo setInfo) { // Return any number of target Ally creature cards with total converted mana cost of 8 or less from your graveyard to the battlefield. Effect effect = new ReturnFromGraveyardToBattlefieldTargetEffect(); - effect.setText("Return any number of target Ally creature cards with total converted mana cost of 8 or less from your graveyard to the battlefield"); + effect.setText("Return any number of target Ally creature cards with total mana value 8 or less from your graveyard to the battlefield"); this.getSpellAbility().addEffect(effect); FilterCard filter = new FilterCreatureCard(); filter.add(SubType.ALLY.getPredicate()); @@ -61,18 +61,18 @@ public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game ga for (UUID targetId : this.getTargets()) { Card card = game.getCard(targetId); if (card != null) { - cmcLeft -= card.getConvertedManaCost(); + cmcLeft -= card.getManaValue(); } } Set possibleTargets = super.possibleTargets(sourceId, sourceControllerId, game); Set leftPossibleTargets = new HashSet<>(); for (UUID targetId : possibleTargets) { Card card = game.getCard(targetId); - if (card != null && card.getConvertedManaCost() <= cmcLeft) { + if (card != null && card.getManaValue() <= cmcLeft) { leftPossibleTargets.add(targetId); } } - setTargetName("any number of target Ally creature cards with total converted mana cost of 8 or less (" + cmcLeft + " left) from your graveyard"); + setTargetName("any number of target Ally creature cards with total mana value of 8 or less (" + cmcLeft + " left) from your graveyard"); return leftPossibleTargets; } @@ -83,11 +83,11 @@ public boolean canTarget(UUID playerId, UUID objectId, Ability source, Game game for (UUID targetId : this.getTargets()) { Card card = game.getCard(targetId); if (card != null) { - cmcLeft -= card.getConvertedManaCost(); + cmcLeft -= card.getManaValue(); } } Card card = game.getCard(objectId); - return card != null && card.getConvertedManaCost() <= cmcLeft; + return card != null && card.getManaValue() <= cmcLeft; } return false; } diff --git a/Mage.Sets/src/mage/cards/m/MarchOfTheMachines.java b/Mage.Sets/src/mage/cards/m/MarchOfTheMachines.java index 88ca46e57308..c5fbacfabc65 100644 --- a/Mage.Sets/src/mage/cards/m/MarchOfTheMachines.java +++ b/Mage.Sets/src/mage/cards/m/MarchOfTheMachines.java @@ -48,7 +48,7 @@ class MarchOfTheMachinesEffect extends ContinuousEffectImpl { public MarchOfTheMachinesEffect() { super(Duration.WhileOnBattlefield, Outcome.BecomeCreature); - staticText = "Each noncreature artifact is an artifact creature with power and toughness each equal to its converted mana cost"; + staticText = "Each noncreature artifact is an artifact creature with power and toughness each equal to its mana value"; dependendToTypes.add(DependencyType.ArtifactAddingRemoving); } @@ -81,7 +81,7 @@ public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) for (Iterator it = affectedObjectList.iterator(); it.hasNext();) { Permanent permanent = it.next().getPermanent(game); if (permanent != null) { - int manaCost = permanent.getConvertedManaCost(); + int manaCost = permanent.getManaValue(); permanent.getPower().setValue(manaCost); permanent.getToughness().setValue(manaCost); } diff --git a/Mage.Sets/src/mage/cards/m/Marjhan.java b/Mage.Sets/src/mage/cards/m/Marjhan.java index 0c7bb9801dad..b3b46622b2ca 100644 --- a/Mage.Sets/src/mage/cards/m/Marjhan.java +++ b/Mage.Sets/src/mage/cards/m/Marjhan.java @@ -62,8 +62,7 @@ public Marjhan(UUID ownerId, CardSetInfo setInfo) { // {U}{U}: Marjhan gets -1/-0 until end of turn and deals 1 damage to target attacking creature without flying. ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(-1, 0, Duration.EndOfTurn), new ManaCostsImpl("{U}{U}")); - Effect effect = new DamageTargetEffect(1); - ability.addEffect(effect); + ability.addEffect(new DamageTargetEffect(1, "and")); ability.addTarget(new TargetCreaturePermanent(filter)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/m/MarkovCrusader.java b/Mage.Sets/src/mage/cards/m/MarkovCrusader.java index 55449c159298..a577b51faf34 100644 --- a/Mage.Sets/src/mage/cards/m/MarkovCrusader.java +++ b/Mage.Sets/src/mage/cards/m/MarkovCrusader.java @@ -16,7 +16,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/m/MarshCasualties.java b/Mage.Sets/src/mage/cards/m/MarshCasualties.java index 8b13137b0847..0a643054803f 100644 --- a/Mage.Sets/src/mage/cards/m/MarshCasualties.java +++ b/Mage.Sets/src/mage/cards/m/MarshCasualties.java @@ -24,7 +24,7 @@ */ public final class MarshCasualties extends CardImpl { - private static final String ruleText = "Creatures target player controls get -1/-1 until end of turn. if this spell was kicked, those creatures get -2/-2 until end of turn instead"; + private static final String ruleText = "Creatures target player controls get -1/-1 until end of turn. If this spell was kicked, those creatures get -2/-2 until end of turn instead"; public MarshCasualties(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{B}{B}"); diff --git a/Mage.Sets/src/mage/cards/m/MarshFlats.java b/Mage.Sets/src/mage/cards/m/MarshFlats.java index f5120250353a..567c28ab6171 100644 --- a/Mage.Sets/src/mage/cards/m/MarshFlats.java +++ b/Mage.Sets/src/mage/cards/m/MarshFlats.java @@ -20,7 +20,7 @@ public final class MarshFlats extends CardImpl { public MarshFlats(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.LAND},null); this.frameColor = new ObjectColor("WB"); - this.addAbility(new FetchLandActivatedAbility(EnumSet.of(SubType.SWAMP, SubType.PLAINS))); + this.addAbility(new FetchLandActivatedAbility(SubType.PLAINS, SubType.SWAMP)); } private MarshFlats(final MarshFlats card) { diff --git a/Mage.Sets/src/mage/cards/m/MarshlandBloodcaster.java b/Mage.Sets/src/mage/cards/m/MarshlandBloodcaster.java new file mode 100644 index 000000000000..cb196f280b3d --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MarshlandBloodcaster.java @@ -0,0 +1,119 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.common.SourceIsSpellCondition; +import mage.abilities.costs.AlternativeCostSourceAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.DynamicCost; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.players.Player; +import mage.watchers.common.SpellsCastWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MarshlandBloodcaster extends CardImpl { + + public MarshlandBloodcaster(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); + + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(3); + this.toughness = new MageInt(5); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // {1}{B}, {T}: Rather than pay the mana cost of the next spell you cast this turn, you may pay life equal to that spell's mana value. + Ability ability = new SimpleActivatedAbility(new MarshlandBloodcasterEffect(), new ManaCostsImpl<>("{1}{B}")); + ability.addCost(new TapSourceCost()); + this.addAbility(ability, new SpellsCastWatcher()); + } + + private MarshlandBloodcaster(final MarshlandBloodcaster card) { + super(card); + } + + @Override + public MarshlandBloodcaster copy() { + return new MarshlandBloodcaster(this); + } +} + +class MarshlandBloodcasterEffect extends ContinuousEffectImpl { + + private static final FilterCard filter = new FilterCard("a spell"); + private final AlternativeCostSourceAbility alternativeCastingCostAbility = new AlternativeCostSourceAbility( + SourceIsSpellCondition.instance, null, filter, true, MarshlandBloodcasterCost.instance + ); + private int spellsCast = -1; + + public MarshlandBloodcasterEffect() { + super(Duration.EndOfTurn, Layer.RulesEffects, SubLayer.NA, Outcome.Detriment); + staticText = "rather than pay the mana cost of the next spell you cast this turn, " + + "you may pay life equal to that spell's mana value"; + } + + public MarshlandBloodcasterEffect(final MarshlandBloodcasterEffect effect) { + super(effect); + } + + @Override + public MarshlandBloodcasterEffect copy() { + return new MarshlandBloodcasterEffect(this); + } + + @Override + public void init(Ability source, Game game, UUID activePlayerId) { + super.init(source, game, activePlayerId); + alternativeCastingCostAbility.setSourceId(source.getSourceId()); + spellsCast = getSpellsCast(source.getControllerId(), game); + } + + @Override + public boolean apply(Game game, Ability source) { + if (getSpellsCast(source.getControllerId(), game) > spellsCast) { + return false; + } + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + controller.getAlternativeSourceCosts().add(alternativeCastingCostAbility); + return true; + } + + private static int getSpellsCast(UUID playerId, Game game) { + SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class); + return watcher != null ? watcher.getSpellsCastThisTurn(playerId).size() : 0; + } +} + +enum MarshlandBloodcasterCost implements DynamicCost { + instance; + + @Override + public Cost getCost(Ability ability, Game game) { + return new PayLifeCost(ability.getManaCosts().manaValue()); + } + + @Override + public String getText(Ability ability, Game game) { + return "Pay " + ability.getManaCosts().manaValue() + " life rather than " + + ability.getManaCosts().getText() + '?'; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MartonStromgald.java b/Mage.Sets/src/mage/cards/m/MartonStromgald.java index a60f3cbe41b6..3f8d0dd3396f 100644 --- a/Mage.Sets/src/mage/cards/m/MartonStromgald.java +++ b/Mage.Sets/src/mage/cards/m/MartonStromgald.java @@ -15,7 +15,7 @@ import mage.constants.SuperType; import mage.filter.common.FilterAttackingCreature; import mage.filter.common.FilterBlockingCreature; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/m/MartyrsCry.java b/Mage.Sets/src/mage/cards/m/MartyrsCry.java index f9f6c91d7d4c..2d7e1adeed5d 100644 --- a/Mage.Sets/src/mage/cards/m/MartyrsCry.java +++ b/Mage.Sets/src/mage/cards/m/MartyrsCry.java @@ -1,22 +1,30 @@ - package mage.cards.m; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.UUID; +import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Controllable; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Collectors; + /** - * * @author TheElk801 */ public final class MartyrsCry extends CardImpl { @@ -40,12 +48,18 @@ public MartyrsCry copy() { class MartyrsCryEffect extends OneShotEffect { + private static final FilterPermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(new ColorPredicate(ObjectColor.WHITE)); + } + MartyrsCryEffect() { super(Outcome.Exile); this.staticText = "Exile all white creatures. For each creature exiled this way, its controller draws a card."; } - MartyrsCryEffect(final MartyrsCryEffect effect) { + private MartyrsCryEffect(final MartyrsCryEffect effect) { super(effect); } @@ -56,19 +70,29 @@ public MartyrsCryEffect copy() { @Override public boolean apply(Game game, Ability source) { - Map playerCrtCount = new HashMap<>(); - for (Iterator it = game.getBattlefield().getActivePermanents(source.getControllerId(), game).iterator(); it.hasNext();) { - Permanent perm = it.next(); - if (perm != null && perm.isCreature() && perm.getColor(game).isWhite() && perm.moveToExile(null, null, source, game)) { - playerCrtCount.putIfAbsent(perm.getControllerId(), 0); - playerCrtCount.compute(perm.getControllerId(), (p, amount) -> amount + 1); - } + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; } - for (UUID playerId : game.getPlayerList().toList()) { - Player player = game.getPlayer(playerId); - if (player != null) { - player.drawCards(playerCrtCount.getOrDefault(playerId, 0), source, game); + List permanents = game.getBattlefield().getActivePermanents( + filter, source.getControllerId(), source.getSourceId(), game + ); + Map playerMap = permanents + .stream() + .filter(Objects::nonNull) + .map(Controllable::getControllerId) + .collect(Collectors.toMap( + Function.identity(), + uuid -> 1, + Integer::sum + )); + controller.moveCards(new CardsImpl(permanents), Zone.EXILED, source, game); + for (Map.Entry entry : playerMap.entrySet()) { + Player player = game.getPlayer(entry.getKey()); + if (player == null) { + continue; } + player.drawCards(entry.getValue(), source, game); } return true; } diff --git a/Mage.Sets/src/mage/cards/m/MarwynTheNurturer.java b/Mage.Sets/src/mage/cards/m/MarwynTheNurturer.java index 4b0726a333c4..1b1877f011bc 100644 --- a/Mage.Sets/src/mage/cards/m/MarwynTheNurturer.java +++ b/Mage.Sets/src/mage/cards/m/MarwynTheNurturer.java @@ -14,7 +14,7 @@ import mage.constants.SuperType; import mage.counters.CounterType; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * @author JRHerlehy Created on 4/7/18. diff --git a/Mage.Sets/src/mage/cards/m/MascotExhibition.java b/Mage.Sets/src/mage/cards/m/MascotExhibition.java new file mode 100644 index 000000000000..a75f8accba4e --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MascotExhibition.java @@ -0,0 +1,40 @@ +package mage.cards.m; + +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.LoreholdToken; +import mage.game.permanent.token.PrismariToken; +import mage.game.permanent.token.SilverquillToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MascotExhibition extends CardImpl { + + public MascotExhibition(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{7}"); + + this.subtype.add(SubType.LESSON); + + // Create a 2/1 white and black Inkling creature token with flying, a 3/2 red and white Spirit creature token, and a 4/4 blue and red Elemental creature token. + this.getSpellAbility().addEffect(new CreateTokenEffect(new SilverquillToken())); + this.getSpellAbility().addEffect(new CreateTokenEffect(new LoreholdToken()) + .setText(", a 3/2 red and white Spirit creature token")); + this.getSpellAbility().addEffect(new CreateTokenEffect(new PrismariToken()) + .setText(", and a 4/4 blue and red Elemental creature token")); + } + + private MascotExhibition(final MascotExhibition card) { + super(card); + } + + @Override + public MascotExhibition copy() { + return new MascotExhibition(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MascotInterception.java b/Mage.Sets/src/mage/cards/m/MascotInterception.java new file mode 100644 index 000000000000..8ece1fda2d2e --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MascotInterception.java @@ -0,0 +1,65 @@ +package mage.cards.m; + +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.SourceTargetsPermanentCondition; +import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.continuous.GainControlTargetEffect; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MascotInterception extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("a creature token"); + + static { + filter.add(TokenPredicate.instance); + } + + private static final Condition condition = new SourceTargetsPermanentCondition(filter); + + public MascotInterception(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}"); + + // This spell costs {3} less to cast if it targets a creature token. + this.addAbility(new SimpleStaticAbility( + Zone.ALL, new SpellCostReductionSourceEffect(3, condition).setCanWorksOnStackOnly(true) + ).setRuleAtTheTop(true)); + + // Gain control of target creature until end of turn. Untap that creature. It gets +2/+0 and gains haste until end of turn. + this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.EndOfTurn)); + this.getSpellAbility().addEffect(new UntapTargetEffect().setText("Untap that creature")); + this.getSpellAbility().addEffect(new BoostTargetEffect( + 2, 0, Duration.EndOfTurn + ).setText("It gets +2/+0")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + HasteAbility.getInstance(), Duration.EndOfTurn + ).setText("and gains haste until end of turn")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private MascotInterception(final MascotInterception card) { + super(card); + } + + @Override + public MascotInterception copy() { + return new MascotInterception(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MaskOfTheMimic.java b/Mage.Sets/src/mage/cards/m/MaskOfTheMimic.java index ad67a71902bf..33151c068f19 100644 --- a/Mage.Sets/src/mage/cards/m/MaskOfTheMimic.java +++ b/Mage.Sets/src/mage/cards/m/MaskOfTheMimic.java @@ -60,7 +60,7 @@ class MaskOfTheMimicEffect extends OneShotEffect { MaskOfTheMimicEffect() { super(Outcome.Benefit); this.staticText = "Search your library for a card with the same name as target nontoken creature " - + "and put that card onto the battlefield. Then shuffle your library."; + + "and put that card onto the battlefield. Then shuffle."; } MaskOfTheMimicEffect(final MaskOfTheMimicEffect effect) { diff --git a/Mage.Sets/src/mage/cards/m/MasterOfCruelties.java b/Mage.Sets/src/mage/cards/m/MasterOfCruelties.java index cb0dd27840f5..78dcd131033e 100644 --- a/Mage.Sets/src/mage/cards/m/MasterOfCruelties.java +++ b/Mage.Sets/src/mage/cards/m/MasterOfCruelties.java @@ -150,9 +150,8 @@ public MasterOfCrueltiesNoDamageEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { switch (event.getType()) { - case DAMAGE_CREATURE: + case DAMAGE_PERMANENT: case DAMAGE_PLAYER: - case DAMAGE_PLANESWALKER: return true; default: return false; diff --git a/Mage.Sets/src/mage/cards/m/MasterOfPredicaments.java b/Mage.Sets/src/mage/cards/m/MasterOfPredicaments.java index f7fed59f58ee..3e8a73bf8858 100644 --- a/Mage.Sets/src/mage/cards/m/MasterOfPredicaments.java +++ b/Mage.Sets/src/mage/cards/m/MasterOfPredicaments.java @@ -57,7 +57,7 @@ class MasterOfPredicamentsEffect extends OneShotEffect { public MasterOfPredicamentsEffect() { super(Outcome.PlayForFree); this.staticText = "choose a card in your hand. That player guesses whether " - + "the card's converted mana cost is greater than 4. If the player " + + "the card's mana value is greater than 4. If the player " + "guessed wrong, you may cast the card without paying its mana cost"; } @@ -92,15 +92,15 @@ public boolean apply(Game game, Ability source) { return false; } boolean guessWrong; - if (attackedPlayer.chooseUse(Outcome.Detriment, "Is the chosen card's converted " - + "mana cost greater than 4?", source, game)) { + if (attackedPlayer.chooseUse(Outcome.Detriment, "Is the chosen card's " + + "mana value greater than 4?", source, game)) { game.informPlayers(attackedPlayer.getLogName() + " guessed that the chosen " - + "card's converted mana cost is greater than 4"); - guessWrong = cardFromHand.getConvertedManaCost() <= 4; + + "card's mana value is greater than 4"); + guessWrong = cardFromHand.getManaValue() <= 4; } else { game.informPlayers(attackedPlayer.getLogName() + " guessed that the chosen " - + "card's converted mana cost is not greater than 4"); - guessWrong = cardFromHand.getConvertedManaCost() > 4; + + "card's mana value is not greater than 4"); + guessWrong = cardFromHand.getManaValue() > 4; } game.informPlayers(attackedPlayer.getLogName() + " guessed " + (guessWrong ? "wrong" : "right")); if (guessWrong) { diff --git a/Mage.Sets/src/mage/cards/m/MasterOfThePearlTrident.java b/Mage.Sets/src/mage/cards/m/MasterOfThePearlTrident.java index ad92cfb176fe..fa47f16b8286 100644 --- a/Mage.Sets/src/mage/cards/m/MasterOfThePearlTrident.java +++ b/Mage.Sets/src/mage/cards/m/MasterOfThePearlTrident.java @@ -1,8 +1,7 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; +import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; @@ -12,11 +11,11 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; +import java.util.UUID; + /** - * * @author North */ public final class MasterOfThePearlTrident extends CardImpl { @@ -28,15 +27,20 @@ public final class MasterOfThePearlTrident extends CardImpl { } public MasterOfThePearlTrident(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}{U}"); this.subtype.add(SubType.MERFOLK); this.power = new MageInt(2); this.toughness = new MageInt(2); // Other Merfolk creatures you control get +1/+1 and have islandwalk. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filter, true))); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(new IslandwalkAbility(), Duration.WhileOnBattlefield, filter, true))); + Ability ability = new SimpleStaticAbility(new BoostControlledEffect( + 1, 1, Duration.WhileOnBattlefield, filter, true + )); + ability.addEffect(new GainAbilityControlledEffect( + new IslandwalkAbility(), Duration.WhileOnBattlefield, filter, true + ).setText("and have islandwalk")); + this.addAbility(ability); } private MasterOfThePearlTrident(final MasterOfThePearlTrident card) { diff --git a/Mage.Sets/src/mage/cards/m/MasterSymmetrist.java b/Mage.Sets/src/mage/cards/m/MasterSymmetrist.java new file mode 100644 index 000000000000..509f691f765a --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MasterSymmetrist.java @@ -0,0 +1,66 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.common.AttacksAllTriggeredAbility; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.ReachAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MasterSymmetrist extends CardImpl { + + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("creature you control with power equal to its toughness"); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + filter.add(MasterSymmetristPredicate.instance); + } + + public MasterSymmetrist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}"); + + this.subtype.add(SubType.RHINO); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Whenever a creature you control with power equal to its toughness attacks, it gains trample until end of turn. + this.addAbility(new AttacksAllTriggeredAbility(new GainAbilityTargetEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn, + "it gains trample until end of turn" + ), false, filter, SetTargetPointer.PERMANENT, false)); + } + + private MasterSymmetrist(final MasterSymmetrist card) { + super(card); + } + + @Override + public MasterSymmetrist copy() { + return new MasterSymmetrist(this); + } +} + +enum MasterSymmetristPredicate implements Predicate { + instance; + + @Override + public boolean apply(Permanent input, Game game) { + return input.getPower().getValue() == input.getToughness().getValue(); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MastermindsAcquisition.java b/Mage.Sets/src/mage/cards/m/MastermindsAcquisition.java index fe61ee0812b9..da1af878cc50 100644 --- a/Mage.Sets/src/mage/cards/m/MastermindsAcquisition.java +++ b/Mage.Sets/src/mage/cards/m/MastermindsAcquisition.java @@ -1,32 +1,31 @@ - package mage.cards.m; -import java.util.UUID; import mage.abilities.Mode; import mage.abilities.effects.common.WishEffect; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInLibrary; +import java.util.UUID; + /** - * * @author L_J */ public final class MastermindsAcquisition extends CardImpl { public MastermindsAcquisition(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}{B}"); // Choose one - // Search your library for a card and put that card into your hand. Then shuffle your library. this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary())); - + // Choose a card you own from outside the game and put it into your hand. - Mode mode = new Mode(); - mode.addEffect(new WishEffect(new FilterCard(), false)); + Mode mode = new Mode(new WishEffect(StaticFilters.FILTER_CARD_A, false) + .setText("Put a card you own from outside the game into your hand")); this.getSpellAbility().addMode(mode); } diff --git a/Mage.Sets/src/mage/cards/m/MatterReshaper.java b/Mage.Sets/src/mage/cards/m/MatterReshaper.java index 6c31426e185f..2f319f419af7 100644 --- a/Mage.Sets/src/mage/cards/m/MatterReshaper.java +++ b/Mage.Sets/src/mage/cards/m/MatterReshaper.java @@ -16,7 +16,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; @@ -52,7 +52,7 @@ class MatterReshaperEffect extends OneShotEffect { public MatterReshaperEffect() { super(Outcome.Benefit); staticText = "reveal the top card of your library. You may put that card onto the battlefield if it's a permanent card" - + " with converted mana cost 3 or less. Otherwise, put that card into your hand"; + + " with mana value 3 or less. Otherwise, put that card into your hand"; } public MatterReshaperEffect(final MatterReshaperEffect effect) { @@ -68,8 +68,8 @@ public boolean apply(Game game, Ability source) { return false; } controller.revealCards(source, new CardsImpl(card), game); - FilterPermanentCard filter = new FilterPermanentCard("permanent card with converted mana cost 3 or less"); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + FilterPermanentCard filter = new FilterPermanentCard("permanent card with mana value 3 or less"); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); if (filter.match(card, game)) { if (controller.chooseUse(Outcome.PutCardInPlay, "Put " + card.getName() + " onto the battlefield (otherwise put in hand)?", source, game)) { controller.moveCards(card, Zone.BATTLEFIELD, source, game); diff --git a/Mage.Sets/src/mage/cards/m/MausoleumHarpy.java b/Mage.Sets/src/mage/cards/m/MausoleumHarpy.java index 2e14f56524d7..3ca270952ef7 100644 --- a/Mage.Sets/src/mage/cards/m/MausoleumHarpy.java +++ b/Mage.Sets/src/mage/cards/m/MausoleumHarpy.java @@ -15,7 +15,7 @@ import mage.constants.TargetController; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/m/MausoleumSecrets.java b/Mage.Sets/src/mage/cards/m/MausoleumSecrets.java index fc592c55271d..d4f5d9bfb1bb 100644 --- a/Mage.Sets/src/mage/cards/m/MausoleumSecrets.java +++ b/Mage.Sets/src/mage/cards/m/MausoleumSecrets.java @@ -13,7 +13,7 @@ import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; @@ -46,9 +46,9 @@ class MausoleumSecretsEffect extends OneShotEffect { public MausoleumSecretsEffect() { super(Outcome.Benefit); this.staticText = "Undergrowth — Search your library " - + "for a black card with converted mana cost less than " + + "for a black card with mana value less than " + "or equal to the number of creature cards in your graveyard, " - + "reveal it, put it into your hand, then shuffle your library."; + + "reveal it, put it into your hand, then shuffle."; } public MausoleumSecretsEffect(final MausoleumSecretsEffect effect) { @@ -67,9 +67,9 @@ public boolean apply(Game game, Ability source) { return false; } int critterCount = player.getGraveyard().count(StaticFilters.FILTER_CARD_CREATURE, game); - FilterCard filter = new FilterCard("a black card with converted mana cost less than or equal to " + critterCount); + FilterCard filter = new FilterCard("a black card with mana value less than or equal to " + critterCount); filter.add(new ColorPredicate(ObjectColor.BLACK)); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, critterCount + 1)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, critterCount + 1)); return new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true).apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/m/MausoleumTurnkey.java b/Mage.Sets/src/mage/cards/m/MausoleumTurnkey.java index d29bb37e7ec1..7595d7cbfc91 100644 --- a/Mage.Sets/src/mage/cards/m/MausoleumTurnkey.java +++ b/Mage.Sets/src/mage/cards/m/MausoleumTurnkey.java @@ -14,7 +14,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.players.Player; import mage.target.Target; diff --git a/Mage.Sets/src/mage/cards/m/MausoleumWanderer.java b/Mage.Sets/src/mage/cards/m/MausoleumWanderer.java index 528d21b7dfa5..11bca2321edf 100644 --- a/Mage.Sets/src/mage/cards/m/MausoleumWanderer.java +++ b/Mage.Sets/src/mage/cards/m/MausoleumWanderer.java @@ -16,7 +16,7 @@ import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterInstantOrSorcerySpell; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetSpell; /** diff --git a/Mage.Sets/src/mage/cards/m/MavindaStudentsAdvocate.java b/Mage.Sets/src/mage/cards/m/MavindaStudentsAdvocate.java new file mode 100644 index 000000000000..58003f53d169 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MavindaStudentsAdvocate.java @@ -0,0 +1,138 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.MageObject; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.common.LimitedTimesPerTurnActivatedAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.CastCardFromGraveyardThenExileItEffect; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Controllable; +import mage.game.Game; +import mage.target.Target; +import mage.target.common.TargetCardInYourGraveyard; +import mage.util.CardUtil; + +import java.util.Collection; +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MavindaStudentsAdvocate extends CardImpl { + + public MavindaStudentsAdvocate(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.BIRD); + this.subtype.add(SubType.ADVISOR); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // {0}: You may cast target instant or sorcery card from your graveyard this turn. If that spell doesn't target a creature you control, it costs {8} more to cast this way. If that spell would be put into your graveyard, exile it instead. Activate only once each turn. + Ability ability = new LimitedTimesPerTurnActivatedAbility( + Zone.BATTLEFIELD, new MavindaStudentsAdvocateEffect(), new GenericManaCost(0) + ); + ability.addTarget(new TargetCardInYourGraveyard( + StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY_FROM_YOUR_GRAVEYARD + )); + this.addAbility(ability); + } + + private MavindaStudentsAdvocate(final MavindaStudentsAdvocate card) { + super(card); + } + + @Override + public MavindaStudentsAdvocate copy() { + return new MavindaStudentsAdvocate(this); + } +} + +class MavindaStudentsAdvocateEffect extends CastCardFromGraveyardThenExileItEffect { + + MavindaStudentsAdvocateEffect() { + super(); + staticText = "you may cast target instant or sorcery card from your graveyard this turn. " + + "If that spell doesn't target a creature you control, it costs {8} more to cast this way. " + + "If that spell would be put into your graveyard, exile it instead"; + } + + private MavindaStudentsAdvocateEffect(final MavindaStudentsAdvocateEffect effect) { + super(effect); + } + + @Override + public MavindaStudentsAdvocateEffect copy() { + return new MavindaStudentsAdvocateEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (!super.apply(game, source)) { + return false; + } + Card card = game.getCard(this.getTargetPointer().getFirst(game, source)); + if (card == null) { + return false; + } + game.addEffect(new MavindaStudentsAdvocateCostEffect(card, game), source); + return true; + } +} + +class MavindaStudentsAdvocateCostEffect extends CostModificationEffectImpl { + + private final MageObjectReference mor; + + MavindaStudentsAdvocateCostEffect(Card card, Game game) { + super(Duration.EndOfTurn, Outcome.Benefit, CostModificationType.INCREASE_COST); + mor = new MageObjectReference(card, game, 1); + } + + private MavindaStudentsAdvocateCostEffect(MavindaStudentsAdvocateCostEffect effect) { + super(effect); + this.mor = effect.mor; + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + CardUtil.increaseCost(abilityToModify, 8); + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + return abilityToModify instanceof SpellAbility + && abilityToModify.isControlledBy(source.getControllerId()) + && mor.refersTo(CardUtil.getMainCardId(game, abilityToModify.getSourceId()), game) + && abilityToModify + .getTargets() + .stream() + .map(Target::getTargets) + .flatMap(Collection::stream) + .map(game::getPermanent) + .filter(Objects::nonNull) + .filter(MageObject::isCreature) + .map(Controllable::getControllerId) + .noneMatch(source::isControlledBy); + } + + @Override + public MavindaStudentsAdvocateCostEffect copy() { + return new MavindaStudentsAdvocateCostEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MayorOfAvabruck.java b/Mage.Sets/src/mage/cards/m/MayorOfAvabruck.java index 6b846fae2670..d3a85d1084bf 100644 --- a/Mage.Sets/src/mage/cards/m/MayorOfAvabruck.java +++ b/Mage.Sets/src/mage/cards/m/MayorOfAvabruck.java @@ -1,33 +1,24 @@ - package mage.cards.m; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.InvertCondition; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.condition.common.TransformedCondition; -import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; +import java.util.UUID; + /** * @author North, noxx */ public final class MayorOfAvabruck extends CardImpl { - private static final String ruleText = "Other Human creatures you control get +1/+1"; - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Human creatures"); static { @@ -47,13 +38,13 @@ public MayorOfAvabruck(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(1); // Other Human creatures you control get +1/+1. - Effect effect = new ConditionalContinuousEffect(new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filter, true), new InvertCondition(new TransformedCondition()), ruleText); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 1, 1, Duration.WhileOnBattlefield, filter, true + ))); // At the beginning of each upkeep, if no spells were cast last turn, transform Mayor of Avabruck. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private MayorOfAvabruck(final MayorOfAvabruck card) { diff --git a/Mage.Sets/src/mage/cards/m/MelekIzzetParagon.java b/Mage.Sets/src/mage/cards/m/MelekIzzetParagon.java index 07c81b9ea166..40a46a08f3fd 100644 --- a/Mage.Sets/src/mage/cards/m/MelekIzzetParagon.java +++ b/Mage.Sets/src/mage/cards/m/MelekIzzetParagon.java @@ -17,7 +17,6 @@ import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.stack.Spell; import mage.target.targetpointer.FixedTarget; @@ -49,8 +48,8 @@ public MelekIzzetParagon(UUID ownerId, CardSetInfo setInfo) { // Play with the top card of your library revealed. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayWithTheTopCardRevealedEffect())); - // You may cast the top card of your library if it's an instant or sorcery card. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayTheTopCardEffect(filter))); + // You may cast instant and sorcery spells from the top of your library. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayTheTopCardEffect(filter, false))); // Whenever you cast an instant or sorcery spell from your library, copy it. You may choose new targets for the copy. this.addAbility(new MelekIzzetParagonTriggeredAbility()); diff --git a/Mage.Sets/src/mage/cards/m/MeletisCharlatan.java b/Mage.Sets/src/mage/cards/m/MeletisCharlatan.java index 02b798274c0c..087a58de3bc6 100644 --- a/Mage.Sets/src/mage/cards/m/MeletisCharlatan.java +++ b/Mage.Sets/src/mage/cards/m/MeletisCharlatan.java @@ -1,7 +1,6 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -11,25 +10,24 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.stack.Spell; -import mage.game.stack.StackObject; -import mage.players.Player; import mage.target.Target; import mage.target.TargetSpell; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class MeletisCharlatan extends CardImpl { public MeletisCharlatan(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WIZARD); @@ -69,15 +67,7 @@ public MeletisCharlatanCopyTargetSpellEffect(final MeletisCharlatanCopyTargetSpe public boolean apply(Game game, Ability source) { Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); if (spell != null) { - StackObject newStackObject = spell.createCopyOnStack(game, source, spell.getControllerId(), true); - Player player = game.getPlayer(spell.getControllerId()); - if (player != null && newStackObject instanceof Spell) { - String activateMessage = ((Spell) newStackObject).getActivatedMessage(game); - if (activateMessage.startsWith(" casts ")) { - activateMessage = activateMessage.substring(6); - } - game.informPlayers(player.getLogName() + " copies " + activateMessage); - } + spell.createCopyOnStack(game, source, spell.getControllerId(), true); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/m/Meltdown.java b/Mage.Sets/src/mage/cards/m/Meltdown.java index c9faffc6c842..686ecf57d793 100644 --- a/Mage.Sets/src/mage/cards/m/Meltdown.java +++ b/Mage.Sets/src/mage/cards/m/Meltdown.java @@ -39,7 +39,7 @@ class MeltdownEffect extends OneShotEffect { MeltdownEffect() { super(Outcome.DestroyPermanent); - this.staticText = "Destroy each artifact with converted mana cost X or less"; + this.staticText = "Destroy each artifact with mana value X or less"; } MeltdownEffect(final MeltdownEffect effect) { @@ -54,7 +54,7 @@ public MeltdownEffect copy() { @Override public boolean apply(Game game, Ability source) { for (Permanent permanent : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) { - if (permanent != null && permanent.isArtifact() && permanent.getConvertedManaCost() <= source.getManaCostsToPay().getX()) { + if (permanent != null && permanent.isArtifact() && permanent.getManaValue() <= source.getManaCostsToPay().getX()) { permanent.destroy(source, game, false); } } diff --git a/Mage.Sets/src/mage/cards/m/Memoricide.java b/Mage.Sets/src/mage/cards/m/Memoricide.java index 8fb1a62e31d0..9a6c564abf88 100644 --- a/Mage.Sets/src/mage/cards/m/Memoricide.java +++ b/Mage.Sets/src/mage/cards/m/Memoricide.java @@ -64,6 +64,6 @@ public MemoricideEffect copy() { @Override public String getText(Mode mode) { - return "Search target player's graveyard, hand, and library for any number of cards with that name and exile them. Then that player shuffles their library"; + return "Search target player's graveyard, hand, and library for any number of cards with that name and exile them. Then that player shuffles"; } } diff --git a/Mage.Sets/src/mage/cards/m/MemoryJar.java b/Mage.Sets/src/mage/cards/m/MemoryJar.java index c41b0393901a..66876a13891b 100644 --- a/Mage.Sets/src/mage/cards/m/MemoryJar.java +++ b/Mage.Sets/src/mage/cards/m/MemoryJar.java @@ -1,22 +1,25 @@ package mage.cards.m; +import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.TargetController; import mage.constants.Zone; +import mage.filter.FilterCard; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.players.Player; -import java.util.Iterator; +import java.util.Objects; import java.util.UUID; /** @@ -33,7 +36,6 @@ public MemoryJar(UUID ownerId, CardSetInfo setInfo) { Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MemoryJarEffect(), new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); - } private MemoryJar(final MemoryJar card) { @@ -48,32 +50,32 @@ public MemoryJar copy() { class MemoryJarEffect extends OneShotEffect { - public MemoryJarEffect() { + MemoryJarEffect() { super(Outcome.DrawCard); - staticText = "Each player exiles all cards from their hand face down and draws seven cards. At the beginning of the next end step, each player discards their hand and returns to their hand each card they exiled this way."; + staticText = "Each player exiles all cards from their hand face down and draws seven cards. " + + "At the beginning of the next end step, each player discards their hand " + + "and returns to their hand each card they exiled this way."; } - public MemoryJarEffect(final MemoryJarEffect effect) { + private MemoryJarEffect(final MemoryJarEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { + MageObject sourceObject = source.getSourceObject(game); + if (sourceObject == null) { + return false; + } Cards cards = new CardsImpl(); //Exile hand for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { Player player = game.getPlayer(playerId); - if (player != null) { - Cards handCards = new CardsImpl(player.getHand()); - for (UUID cardId : handCards) { - Card card = handCards.get(cardId, game); - if (card != null) { - card.moveToExile(getId(), "Memory Jar", source, game); - card.setFaceDown(true, game); - cards.add(card); - } - } + if (player == null) { + continue; } + cards.addAll(player.getHand()); + player.moveCards(player.getHand(), Zone.EXILED, source, game); } //Draw 7 cards for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { @@ -82,10 +84,10 @@ public boolean apply(Game game, Ability source) { player.drawCards(7, source, game); } } + cards.removeIf(uuid -> game.getState().getZone(uuid) != Zone.EXILED); + cards.getCards(game).stream().filter(Objects::nonNull).forEach(card -> card.setFaceDown(true, game)); //Delayed ability - Effect effect = new MemoryJarDelayedEffect(); - effect.setValue("MemoryJarCards", cards); - game.addDelayedTriggeredAbility(new MemoryJarDelayedTriggeredAbility(effect), source); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new MemoryJarDelayedEffect(cards)), source); return true; } @@ -97,13 +99,23 @@ public MemoryJarEffect copy() { class MemoryJarDelayedEffect extends OneShotEffect { - public MemoryJarDelayedEffect() { + private static final FilterCard filter = new FilterCard(); + + static { + filter.add(TargetController.YOU.getOwnerPredicate()); + } + + private final Cards cards = new CardsImpl(); + + MemoryJarDelayedEffect(Cards cards) { super(Outcome.DrawCard); - staticText = "At the beginning of the next end step, each player discards their hand and returns to their hand each card they exiled this way"; + this.cards.addAll(cards); + staticText = "each player discards their hand and returns to their hand each card they exiled this way"; } - public MemoryJarDelayedEffect(final MemoryJarDelayedEffect effect) { + private MemoryJarDelayedEffect(final MemoryJarDelayedEffect effect) { super(effect); + this.cards.addAll(effect.cards); } @Override @@ -113,51 +125,14 @@ public MemoryJarDelayedEffect copy() { @Override public boolean apply(Game game, Ability source) { - Cards cards = (Cards) this.getValue("MemoryJarCards"); - - if (cards != null) { - //Discard - for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - player.discard(player.getHand().size(), false, false, source, game); - } - } - //Return to hand - for (Iterator it = cards.getCards(game).iterator(); it.hasNext(); ) { - Card card = it.next(); - card.moveToZone(Zone.HAND, source, game, true); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; } - return true; + player.discard(player.getHand(), false, source, game); + player.moveCards(cards.getCards(filter, source.getSourceId(), playerId, game), Zone.HAND, source, game); } - return false; - } - -} - -class MemoryJarDelayedTriggeredAbility extends DelayedTriggeredAbility { - - public MemoryJarDelayedTriggeredAbility(Effect effect) { - super(effect); - } - - public MemoryJarDelayedTriggeredAbility(final MemoryJarDelayedTriggeredAbility ability) { - super(ability); - } - - @Override - public MemoryJarDelayedTriggeredAbility copy() { - return new MemoryJarDelayedTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.END_TURN_STEP_PRE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { return true; } - } diff --git a/Mage.Sets/src/mage/cards/m/MemoryTheft.java b/Mage.Sets/src/mage/cards/m/MemoryTheft.java index 3f6c660bb60a..ae3d3ec6007b 100644 --- a/Mage.Sets/src/mage/cards/m/MemoryTheft.java +++ b/Mage.Sets/src/mage/cards/m/MemoryTheft.java @@ -13,7 +13,7 @@ import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.filter.predicate.mageobject.AdventurePredicate; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; diff --git a/Mage.Sets/src/mage/cards/m/MemorysJourney.java b/Mage.Sets/src/mage/cards/m/MemorysJourney.java index a37ed2e57055..cd3dba5db43f 100644 --- a/Mage.Sets/src/mage/cards/m/MemorysJourney.java +++ b/Mage.Sets/src/mage/cards/m/MemorysJourney.java @@ -1,38 +1,30 @@ - package mage.cards.m; -import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.TargetPlayerShufflesTargetCardsEffect; import mage.abilities.keyword.FlashbackAbility; -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.TimingRule; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.game.Game; -import mage.players.Player; import mage.target.TargetPlayer; -import mage.target.common.TargetCardInGraveyard; +import mage.target.common.TargetCardInTargetPlayersGraveyard; + +import java.util.UUID; /** - * * @author North */ public final class MemorysJourney extends CardImpl { public MemorysJourney(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); // Target player shuffles up to three target cards from their graveyard into their library. - this.getSpellAbility().addEffect(new MemorysJourneyEffect()); + this.getSpellAbility().addEffect(new TargetPlayerShufflesTargetCardsEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); - this.getSpellAbility().addTarget(new MemorysJourneyTarget()); + this.getSpellAbility().addTarget(new TargetCardInTargetPlayersGraveyard(3)); + // Flashback {G} this.addAbility(new FlashbackAbility(new ManaCostsImpl("{G}"), TimingRule.INSTANT)); } @@ -46,57 +38,3 @@ public MemorysJourney copy() { return new MemorysJourney(this); } } - -class MemorysJourneyEffect extends OneShotEffect { - - public MemorysJourneyEffect() { - super(Outcome.Neutral); - this.staticText = "Target player shuffles up to three target cards from their graveyard into their library"; - } - - public MemorysJourneyEffect(final MemorysJourneyEffect effect) { - super(effect); - } - - @Override - public MemorysJourneyEffect copy() { - return new MemorysJourneyEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player targetPlayer = game.getPlayer(source.getFirstTarget()); - if (targetPlayer != null) { - return targetPlayer.shuffleCardsToLibrary(new CardsImpl(source.getTargets().get(1).getTargets()), game, source); - } - return false; - } -} - -class MemorysJourneyTarget extends TargetCardInGraveyard { - - public MemorysJourneyTarget() { - super(0, 3, new FilterCard()); - } - - public MemorysJourneyTarget(final MemorysJourneyTarget target) { - super(target); - } - - @Override - public boolean canTarget(UUID id, Ability source, Game game) { - Card card = game.getCard(id); - if (card != null && game.getState().getZone(card.getId()) == Zone.GRAVEYARD) { - UUID firstTarget = source.getFirstTarget(); - if (firstTarget != null && game.getPlayer(firstTarget).getGraveyard().contains(id)) { - return filter.match(card, game); - } - } - return false; - } - - @Override - public MemorysJourneyTarget copy() { - return new MemorysJourneyTarget(this); - } -} diff --git a/Mage.Sets/src/mage/cards/m/MentalMisstep.java b/Mage.Sets/src/mage/cards/m/MentalMisstep.java index e1a4e1f9ade7..69d051198dbc 100644 --- a/Mage.Sets/src/mage/cards/m/MentalMisstep.java +++ b/Mage.Sets/src/mage/cards/m/MentalMisstep.java @@ -8,7 +8,7 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetSpell; /** @@ -17,10 +17,10 @@ */ public final class MentalMisstep extends CardImpl { - private static final FilterSpell FILTER = new FilterSpell("spell with converted mana cost 1"); + private static final FilterSpell FILTER = new FilterSpell("spell with mana value 1"); static { - FILTER.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, 1)); + FILTER.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, 1)); } public MentalMisstep(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/m/MentorOfTheMeek.java b/Mage.Sets/src/mage/cards/m/MentorOfTheMeek.java index e3eb32521046..9eedbac13a16 100644 --- a/Mage.Sets/src/mage/cards/m/MentorOfTheMeek.java +++ b/Mage.Sets/src/mage/cards/m/MentorOfTheMeek.java @@ -18,7 +18,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * @author Loki diff --git a/Mage.Sets/src/mage/cards/m/MentorsGuidance.java b/Mage.Sets/src/mage/cards/m/MentorsGuidance.java new file mode 100644 index 000000000000..47def4b42a5b --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MentorsGuidance.java @@ -0,0 +1,62 @@ +package mage.cards.m; + +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.CastSourceTriggeredAbility; +import mage.abilities.effects.common.CopySourceSpellEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MentorsGuidance extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(); + + static { + filter.add(Predicates.or( + CardType.PLANESWALKER.getPredicate(), + SubType.CLERIC.getPredicate(), + SubType.DRUID.getPredicate(), + SubType.SHAMAN.getPredicate(), + SubType.WARLOCK.getPredicate(), + SubType.WIZARD.getPredicate() + )); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + + public MentorsGuidance(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}"); + + // When you cast this spell, copy it if you control a planeswalker, Cleric, Druid, Shaman, Warlock, or Wizard. + new CastSourceTriggeredAbility(new ConditionalOneShotEffect( + new CopySourceSpellEffect(), condition, "copy it if you control " + + "a planeswalker, Cleric, Druid, Shaman, Warlock, or Wizard" + )); + + // Scry 1, then draw a card. + this.getSpellAbility().addEffect(new ScryEffect(1, false)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy(", then")); + } + + private MentorsGuidance(final MentorsGuidance card) { + super(card); + } + + @Override + public MentorsGuidance copy() { + return new MentorsGuidance(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MercadianLift.java b/Mage.Sets/src/mage/cards/m/MercadianLift.java index 8b84910a79ef..57cbc2c200f7 100644 --- a/Mage.Sets/src/mage/cards/m/MercadianLift.java +++ b/Mage.Sets/src/mage/cards/m/MercadianLift.java @@ -18,7 +18,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInHand; @@ -58,7 +58,7 @@ class MercadianLiftEffect extends OneShotEffect { public MercadianLiftEffect() { super(Outcome.PutCardInPlay); - staticText = "You may put a creature card with converted mana cost X from your hand onto the battlefield"; + staticText = "You may put a creature card with mana value X from your hand onto the battlefield"; } public MercadianLiftEffect(final MercadianLiftEffect effect) { @@ -82,8 +82,8 @@ public boolean apply(Game game, Ability source) { } System.out.println("The number is " + numberOfCounters); FilterCreatureCard filter = new FilterCreatureCard(); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, numberOfCounters)); - filter.setMessage("creature card with converted mana cost " + numberOfCounters); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, numberOfCounters)); + filter.setMessage("creature card with mana value " + numberOfCounters); TargetCardInHand target = new TargetCardInHand(filter); if (target.canChoose(source.getSourceId(), controller.getId(), game) && controller.chooseUse(Outcome.PutCardInPlay, "Put " + filter.getMessage() + " from your hand onto the battlefield?", source, game) diff --git a/Mage.Sets/src/mage/cards/m/MercilessPredator.java b/Mage.Sets/src/mage/cards/m/MercilessPredator.java index 3176713049be..579f9b79f7b6 100644 --- a/Mage.Sets/src/mage/cards/m/MercilessPredator.java +++ b/Mage.Sets/src/mage/cards/m/MercilessPredator.java @@ -1,19 +1,13 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; -import mage.abilities.keyword.TransformAbility; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** * @author nantuko @@ -21,7 +15,7 @@ public final class MercilessPredator extends CardImpl { public MercilessPredator(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.WEREWOLF); this.color.setRed(true); @@ -33,8 +27,7 @@ public MercilessPredator(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(2); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Merciless Predator. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private MercilessPredator(final MercilessPredator card) { diff --git a/Mage.Sets/src/mage/cards/m/MercurialTransformation.java b/Mage.Sets/src/mage/cards/m/MercurialTransformation.java new file mode 100644 index 000000000000..569fc45200dc --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MercurialTransformation.java @@ -0,0 +1,83 @@ +package mage.cards.m; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect; +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.game.Game; +import mage.game.permanent.token.Token; +import mage.game.permanent.token.custom.CreatureToken; +import mage.players.Player; +import mage.target.common.TargetNonlandPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MercurialTransformation extends CardImpl { + + public MercurialTransformation(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{U}"); + + this.subtype.add(SubType.LESSON); + + // Until end of turn, target nonland permanent loses all abilities and becomes your choice of a blue Frog creature with base power and toughness 1/1 or a blue Octopus creature with base power and toughness 4/4. + this.getSpellAbility().addEffect(new MercurialTransformationEffect()); + this.getSpellAbility().addTarget(new TargetNonlandPermanent()); + } + + private MercurialTransformation(final MercurialTransformation card) { + super(card); + } + + @Override + public MercurialTransformation copy() { + return new MercurialTransformation(this); + } +} + +class MercurialTransformationEffect extends OneShotEffect { + + MercurialTransformationEffect() { + super(Outcome.Benefit); + staticText = "until end of turn, target nonland permanent loses all abilities and " + + "becomes your choice of a blue Frog creature with base power and toughness 1/1 " + + "or a blue Octopus creature with base power and toughness 4/4"; + } + + private MercurialTransformationEffect(final MercurialTransformationEffect effect) { + super(effect); + } + + @Override + public MercurialTransformationEffect copy() { + return new MercurialTransformationEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Token token; + if (player.chooseUse( + outcome, "1/1 Frog or 4/4 Octopus?", + null, "Frog", "Octopus", source, game + )) { + token = new CreatureToken(1, 1).withColor("U").withSubType(SubType.FROG); + } else { + token = new CreatureToken(4, 4).withColor("U").withSubType(SubType.OCTOPUS); + } + game.addEffect(new BecomesCreatureTargetEffect( + token, true, false, Duration.EndOfTurn + ), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MerenOfClanNelToth.java b/Mage.Sets/src/mage/cards/m/MerenOfClanNelToth.java index 9af6fd4441b9..8b0bf5cfe6cb 100644 --- a/Mage.Sets/src/mage/cards/m/MerenOfClanNelToth.java +++ b/Mage.Sets/src/mage/cards/m/MerenOfClanNelToth.java @@ -1,12 +1,9 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.BeginningOfYourEndStepTriggeredAbility; import mage.abilities.common.DiesCreatureTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.counter.AddCountersControllerEffect; import mage.cards.Card; @@ -14,21 +11,22 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.counters.CounterType; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.players.Player; -import mage.target.Target; import mage.target.common.TargetCardInYourGraveyard; +import java.util.UUID; + /** - * * @author fireshoes */ public final class MerenOfClanNelToth extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("another creature you control"); + + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("another creature you control"); static { filter.add(AnotherPredicate.instance); @@ -36,7 +34,7 @@ public final class MerenOfClanNelToth extends CardImpl { } public MerenOfClanNelToth(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{G}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SHAMAN); @@ -44,15 +42,14 @@ public MerenOfClanNelToth(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(4); // Whenever another creature you control dies, you get an experience counter. - Effect effect = new AddCountersControllerEffect(CounterType.EXPERIENCE.createInstance(1), false); - effect.setText("you get an experience counter"); - this.addAbility(new DiesCreatureTriggeredAbility(effect, false, filter)); - + this.addAbility(new DiesCreatureTriggeredAbility(new AddCountersControllerEffect( + CounterType.EXPERIENCE.createInstance(1), false + ).setText("you get an experience counter"), false, filter)); + // At the beginning of your end step, choose target creature card in your graveyard. // If that card's converted mana cost is less than or equal to the number of experience counters you have, return it to the battlefield. Otherwise, put it into your hand. - Target target = new TargetCardInYourGraveyard(new FilterCreatureCard("creature card in your graveyard")); Ability ability = new BeginningOfYourEndStepTriggeredAbility(new MerenOfClanNelTothEffect(), false); - ability.addTarget(target); + ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); this.addAbility(ability); } @@ -68,12 +65,14 @@ public MerenOfClanNelToth copy() { class MerenOfClanNelTothEffect extends OneShotEffect { - public MerenOfClanNelTothEffect() { + MerenOfClanNelTothEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "choose target creature card in your graveyard. If that card's converted mana cost is less than or equal to the number of experience counters you have, return it to the battlefield. Otherwise, put it into your hand"; + this.staticText = "choose target creature card in your graveyard. If that card's mana value " + + "is less than or equal to the number of experience counters you have, " + + "return it to the battlefield. Otherwise, put it into your hand"; } - public MerenOfClanNelTothEffect(final MerenOfClanNelTothEffect effect) { + private MerenOfClanNelTothEffect(final MerenOfClanNelTothEffect effect) { super(effect); } @@ -85,21 +84,11 @@ public MerenOfClanNelTothEffect copy() { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - int amount = player.getCounters().getCount(CounterType.EXPERIENCE); - Card card = game.getCard(targetPointer.getFirst(game, source)); - if (card != null) { - Zone targetZone = Zone.HAND; - String text = " put into hand of "; - if (card.getConvertedManaCost() <= amount) { - targetZone = Zone.BATTLEFIELD; - text = " put onto battlefield for "; - } - card.moveToZone(targetZone, source, game, false); - game.informPlayers("Meren of Clan Nel Toth: " + card.getName() + text + player.getLogName()); - return true; - } + Card card = game.getCard(targetPointer.getFirst(game, source)); + if (player == null || card == null) { + return false; } - return false; + boolean flag = card.getManaValue() <= player.getCounters().getCount(CounterType.EXPERIENCE); + return player.moveCards(card, flag ? Zone.BATTLEFIELD : Zone.HAND, source, game); } } diff --git a/Mage.Sets/src/mage/cards/m/MerrowBonegnawer.java b/Mage.Sets/src/mage/cards/m/MerrowBonegnawer.java index 54b24bc3e53e..1b479e82a81c 100644 --- a/Mage.Sets/src/mage/cards/m/MerrowBonegnawer.java +++ b/Mage.Sets/src/mage/cards/m/MerrowBonegnawer.java @@ -14,7 +14,6 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.FilterCard; import mage.filter.FilterSpell; import mage.filter.predicate.mageobject.ColorPredicate; import mage.target.TargetPlayer; @@ -41,13 +40,14 @@ public MerrowBonegnawer(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(1); // {tap}: Target player exiles a card from their graveyard. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileFromZoneTargetEffect(Zone.GRAVEYARD, null, getIdName(), new FilterCard()), new TapSourceCost()); + Ability ability = new SimpleActivatedAbility( + new ExileFromZoneTargetEffect(Zone.GRAVEYARD, false), new TapSourceCost() + ); ability.addTarget(new TargetPlayer()); this.addAbility(ability); // Whenever you cast a black spell, you may untap Merrow Bonegnawer. - this.addAbility(new SpellCastControllerTriggeredAbility(Zone.BATTLEFIELD, new UntapSourceEffect(), filter, true, false)); - + this.addAbility(new SpellCastControllerTriggeredAbility(new UntapSourceEffect(), filter, true, false)); } private MerrowBonegnawer(final MerrowBonegnawer card) { diff --git a/Mage.Sets/src/mage/cards/m/MesmericFiend.java b/Mage.Sets/src/mage/cards/m/MesmericFiend.java index 3f4a266f1346..1373a9378d18 100644 --- a/Mage.Sets/src/mage/cards/m/MesmericFiend.java +++ b/Mage.Sets/src/mage/cards/m/MesmericFiend.java @@ -122,7 +122,7 @@ public boolean apply(Game game, Ability source) { UUID exileId = (UUID) game.getState().getValue(source.getSourceId().toString() + zoneChangeMinusOne); if (exileId != null) { Cards cards = game.getExile().getExileZone(exileId); - if (!cards.isEmpty()) { + if (cards != null && !cards.isEmpty()) { return controller.moveCards(cards, Zone.HAND, source, game); } } diff --git a/Mage.Sets/src/mage/cards/m/MessengerJays.java b/Mage.Sets/src/mage/cards/m/MessengerJays.java index 92ece05ea574..f75fe8074041 100644 --- a/Mage.Sets/src/mage/cards/m/MessengerJays.java +++ b/Mage.Sets/src/mage/cards/m/MessengerJays.java @@ -1,41 +1,42 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.CouncilsDilemmaVoteEffect; -import mage.abilities.effects.common.DrawDiscardControllerEffect; +import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.choices.TwoChoiceVote; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** - * - * @author JRHerlehy + * @author JRHerlehy, TheElk801 */ public final class MessengerJays extends CardImpl { public MessengerJays(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}"); - + this.subtype.add(SubType.BIRD); this.power = new MageInt(2); this.toughness = new MageInt(1); // Flying this.addAbility(FlyingAbility.getInstance()); + // Council's dilemma — When Messenger Jays enters the battlefield, starting with you, each player votes for feather or quill. Put a +1/+1 counter on Messenger Jays for each feather vote and draw a card for each quill vote. For each card drawn this way, discard a card. - this.addAbility(new EntersBattlefieldTriggeredAbility(new MessengerJaysDilemmaEffect(), false, "Council's dilemma — ")); + this.addAbility(new EntersBattlefieldTriggeredAbility( + new MessengerJaysEffect(), false, "Council's dilemma — " + )); } private MessengerJays(final MessengerJays card) { @@ -48,45 +49,44 @@ public MessengerJays copy() { } } -class MessengerJaysDilemmaEffect extends CouncilsDilemmaVoteEffect { +class MessengerJaysEffect extends OneShotEffect { - public MessengerJaysDilemmaEffect() { + MessengerJaysEffect() { super(Outcome.Benefit); - this.staticText = "starting with you, each player votes for feather or quill. Put a +1/+1 counter on {this} for each feather vote and draw a card for each quill vote. For each card drawn this way, discard a card."; + staticText = "starting with you, each player votes for feather or quill. " + + "Put a +1/+1 counter on {this} for each feather vote " + + "and draw a card for each quill vote. For each card drawn this way, discard a card."; } - public MessengerJaysDilemmaEffect(final MessengerJaysDilemmaEffect effect) { + private MessengerJaysEffect(final MessengerJaysEffect effect) { super(effect); } @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - - //If no controller, exit out here and do not vote. - if (controller == null) return false; - - this.vote("feather", "quill", controller, game, source); - - Permanent permanent = game.getPermanent(source.getSourceId()); - - //Feathers Votes - //If feathers received zero votes or the permanent is no longer on the battlefield, do not attempt to put P1P1 counter on it. - if (voteOneCount > 0 && permanent != null) - permanent.addCounters(CounterType.P1P1.createInstance(voteOneCount), source.getControllerId(), source, game); + public MessengerJaysEffect copy() { + return new MessengerJaysEffect(this); + } - //Quill Votes - //Only let the controller loot the appropriate amount of cards if it was voted for. - if (voteTwoCount > 0) { - Effect lootCardsEffect = new DrawDiscardControllerEffect(voteTwoCount, voteTwoCount); - lootCardsEffect.apply(game, source); + @Override + public boolean apply(Game game, Ability source) { + // Outcome.Benefit - AI will boost all the time (Feather choice) + // TODO: add AI hint logic in the choice method, see Tyrant's Choice as example + TwoChoiceVote vote = new TwoChoiceVote("Feather (+1/+1 counter)", "Quill (draw a card)", Outcome.Benefit); + vote.doVotes(source, game); + + int featherCount = vote.getVoteCount(true); + int quillCount = vote.getVoteCount(false); + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (featherCount > 0 && permanent != null) { + permanent.addCounters(CounterType.P1P1.createInstance(featherCount), source.getControllerId(), source, game); } - return true; - } + Player player = game.getPlayer(source.getControllerId()); + if (quillCount > 0 && player != null) { + int drawn = player.drawCards(quillCount, source, game); + player.discard(drawn, false, false, source, game); + } - @Override - public MessengerJaysDilemmaEffect copy() { - return new MessengerJaysDilemmaEffect(this); + return featherCount + quillCount > 0; } } diff --git a/Mage.Sets/src/mage/cards/m/MetallurgicSummonings.java b/Mage.Sets/src/mage/cards/m/MetallurgicSummonings.java index 582b6fa48b24..c3916cadfe6e 100644 --- a/Mage.Sets/src/mage/cards/m/MetallurgicSummonings.java +++ b/Mage.Sets/src/mage/cards/m/MetallurgicSummonings.java @@ -50,7 +50,7 @@ public MetallurgicSummonings(UUID ownerId, CardSetInfo setInfo) { new MetallurgicSummoningsReturnEffect(), new ManaCostsImpl("{3}{U}{U}"), new PermanentsOnTheBattlefieldCondition(new FilterControlledArtifactPermanent(), ComparisonType.MORE_THAN, 5), "{3}{U}{U}, Exile {this}: Return all instant and sorcery cards from your graveyard to your hand." - + " Activate this ability only if you control six or more artifacts."); + + " Activate only if you control six or more artifacts."); ability.addCost(new ExileSourceCost()); this.addAbility(ability); } @@ -69,7 +69,7 @@ class MetallurgicSummoningsTokenEffect extends OneShotEffect { public MetallurgicSummoningsTokenEffect() { super(Outcome.PutCreatureInPlay); - staticText = "create an X/X colorless Construct artifact creature token, where X is that spell's converted mana cost"; + staticText = "create an X/X colorless Construct artifact creature token, where X is that spell's mana value"; } public MetallurgicSummoningsTokenEffect(MetallurgicSummoningsTokenEffect ability) { @@ -80,7 +80,7 @@ public MetallurgicSummoningsTokenEffect(MetallurgicSummoningsTokenEffect ability public boolean apply(Game game, Ability source) { Spell spell = game.getSpellOrLKIStack(this.getTargetPointer().getFirst(game, source)); if (spell != null) { - int cmc = spell.getConvertedManaCost(); + int cmc = spell.getManaValue(); if (cmc > 0) { return new CreateTokenEffect(new MetallurgicSummoningsConstructToken(cmc)).apply(game, source); } @@ -99,7 +99,7 @@ class MetallurgicSummoningsReturnEffect extends OneShotEffect { MetallurgicSummoningsReturnEffect() { super(Outcome.PutCardInPlay); - this.staticText = "Return all instant and sorcery cards from your graveyard to your hand. Activate this ability only if you control six or more artifacts"; + this.staticText = "Return all instant and sorcery cards from your graveyard to your hand. Activate only if you control six or more artifacts"; } MetallurgicSummoningsReturnEffect(final MetallurgicSummoningsReturnEffect effect) { diff --git a/Mage.Sets/src/mage/cards/m/MetalworkColossus.java b/Mage.Sets/src/mage/cards/m/MetalworkColossus.java index efffcd006874..1415456ba904 100644 --- a/Mage.Sets/src/mage/cards/m/MetalworkColossus.java +++ b/Mage.Sets/src/mage/cards/m/MetalworkColossus.java @@ -64,7 +64,7 @@ class MetalworkColossusCostReductionEffect extends CostModificationEffectImpl { MetalworkColossusCostReductionEffect() { super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "{this} costs {X} less to cast, where X is the total converted mana cost of noncreature artifacts you control"; + staticText = "this spell costs {X} less to cast, where X is the total mana value of noncreature artifacts you control"; } MetalworkColossusCostReductionEffect(final MetalworkColossusCostReductionEffect effect) { @@ -75,7 +75,7 @@ class MetalworkColossusCostReductionEffect extends CostModificationEffectImpl { public boolean apply(Game game, Ability source, Ability abilityToModify) { int totalCMC = 0; for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - totalCMC += permanent.getConvertedManaCost(); + totalCMC += permanent.getManaValue(); } CardUtil.reduceCost(abilityToModify, totalCMC); return true; diff --git a/Mage.Sets/src/mage/cards/m/MetamorphicAlteration.java b/Mage.Sets/src/mage/cards/m/MetamorphicAlteration.java index 5f9deb33f35a..f9a2336b6c94 100644 --- a/Mage.Sets/src/mage/cards/m/MetamorphicAlteration.java +++ b/Mage.Sets/src/mage/cards/m/MetamorphicAlteration.java @@ -100,7 +100,7 @@ public ChooseACreature copy() { class MetamorphicAlterationEffect extends ContinuousEffectImpl { public MetamorphicAlterationEffect() { - super(Duration.WhileOnBattlefield, Layer.CopyEffects_1, SubLayer.NA, Outcome.Copy); + super(Duration.WhileOnBattlefield, Layer.CopyEffects_1, SubLayer.CopyEffects_1a, Outcome.Copy); this.staticText = "Enchanted creature is a copy of the chosen creature."; } diff --git a/Mage.Sets/src/mage/cards/m/Metamorphose.java b/Mage.Sets/src/mage/cards/m/Metamorphose.java index d4f908d11b16..0fd6d8937e16 100644 --- a/Mage.Sets/src/mage/cards/m/Metamorphose.java +++ b/Mage.Sets/src/mage/cards/m/Metamorphose.java @@ -77,7 +77,7 @@ public boolean apply(Game game, Ability source) { Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { Player controller = game.getPlayer(permanent.getControllerId()); - if (controller != null && controller.canRespond() && controller.chooseUse(Outcome.PutCardInPlay, "Do you wish to put an artifact, creature, enchantment, or land card onto the battlefield?", source, game)) { + if (controller != null && controller.canRespond() && controller.chooseUse(Outcome.PutCardInPlay, "Put an artifact, creature, enchantment, or land card onto the battlefield?", source, game)) { TargetCardInHand target = new TargetCardInHand(filter); target.clearChosen(); if (controller.chooseTarget(outcome, target, source, game)) { diff --git a/Mage.Sets/src/mage/cards/m/Metamorphosis.java b/Mage.Sets/src/mage/cards/m/Metamorphosis.java index d3a60140dfca..b834fde58b3f 100644 --- a/Mage.Sets/src/mage/cards/m/Metamorphosis.java +++ b/Mage.Sets/src/mage/cards/m/Metamorphosis.java @@ -47,7 +47,7 @@ class MetamorphosisEffect extends OneShotEffect { public MetamorphosisEffect() { super(Outcome.PutManaInPool); - staticText = "Add X mana of any one color, where X is 1 plus the sacrificed creature's converted mana cost. Spend this mana only to cast creature spells."; + staticText = "Add X mana of any one color, where X is 1 plus the sacrificed creature's mana value. Spend this mana only to cast creature spells."; } public MetamorphosisEffect(final MetamorphosisEffect effect) { @@ -59,7 +59,7 @@ public boolean apply(Game game, Ability source) { int amount = 0; for (Cost cost : source.getCosts()) { if (cost instanceof SacrificeTargetCost && !((SacrificeTargetCost) cost).getPermanents().isEmpty()) { - amount = ((SacrificeTargetCost) cost).getPermanents().get(0).getConvertedManaCost() + 1; + amount = ((SacrificeTargetCost) cost).getPermanents().get(0).getManaValue() + 1; break; } } diff --git a/Mage.Sets/src/mage/cards/m/MetathranAerostat.java b/Mage.Sets/src/mage/cards/m/MetathranAerostat.java index e89b81f25f35..8ad43b8513ef 100644 --- a/Mage.Sets/src/mage/cards/m/MetathranAerostat.java +++ b/Mage.Sets/src/mage/cards/m/MetathranAerostat.java @@ -16,7 +16,7 @@ import mage.constants.ComparisonType; import mage.constants.Outcome; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; @@ -54,7 +54,7 @@ class MetathranAerostatEffect extends OneShotEffect { public MetathranAerostatEffect() { super(Outcome.Benefit); - this.staticText = "You may put a creature card with converted mana cost " + this.staticText = "You may put a creature card with mana value " + "X from your hand onto the battlefield. " + "If you do, return {this} to its owner's hand"; } @@ -75,8 +75,8 @@ public boolean apply(Game game, Ability source) { return false; } int xValue = source.getManaCostsToPay().getX(); - FilterCreatureCard filter = new FilterCreatureCard("a creature with converted mana cost " + xValue); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, xValue)); + FilterCreatureCard filter = new FilterCreatureCard("a creature with mana value " + xValue); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, xValue)); if (new PutCardFromHandOntoBattlefieldEffect(filter).apply(game, source)) { return new ReturnToHandSourceEffect(true).apply(game, source); } diff --git a/Mage.Sets/src/mage/cards/m/MidnightGuard.java b/Mage.Sets/src/mage/cards/m/MidnightGuard.java index 242f9e82524d..9f9382195e0a 100644 --- a/Mage.Sets/src/mage/cards/m/MidnightGuard.java +++ b/Mage.Sets/src/mage/cards/m/MidnightGuard.java @@ -11,7 +11,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/m/MidnightScavengers.java b/Mage.Sets/src/mage/cards/m/MidnightScavengers.java index d0626c7aa0b1..c6ac8ba52f74 100644 --- a/Mage.Sets/src/mage/cards/m/MidnightScavengers.java +++ b/Mage.Sets/src/mage/cards/m/MidnightScavengers.java @@ -15,7 +15,7 @@ import mage.constants.ComparisonType; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.Target; import mage.target.common.TargetCardInYourGraveyard; @@ -25,10 +25,10 @@ */ public final class MidnightScavengers extends CardImpl { - private static final FilterCreatureCard filter = new FilterCreatureCard("creature card with converted mana cost 3 or less from your graveyard"); + private static final FilterCreatureCard filter = new FilterCreatureCard("creature card with mana value 3 or less from your graveyard"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public MidnightScavengers(UUID ownerId, CardSetInfo setInfo) { @@ -45,7 +45,7 @@ public MidnightScavengers(UUID ownerId, CardSetInfo setInfo) { this.addAbility(ability); // (Melds with Graf Rats.) - this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("(Melds with Graf Rats.)"))); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("(Melds with Graf Rats.)"))); } private MidnightScavengers(final MidnightScavengers card) { diff --git a/Mage.Sets/src/mage/cards/m/MikaeusTheLunarch.java b/Mage.Sets/src/mage/cards/m/MikaeusTheLunarch.java index b73bf24d40a8..4f758ef63dd6 100644 --- a/Mage.Sets/src/mage/cards/m/MikaeusTheLunarch.java +++ b/Mage.Sets/src/mage/cards/m/MikaeusTheLunarch.java @@ -20,7 +20,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * @author nantuko diff --git a/Mage.Sets/src/mage/cards/m/MilaCraftyCompanion.java b/Mage.Sets/src/mage/cards/m/MilaCraftyCompanion.java new file mode 100644 index 000000000000..8c91fdf91337 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MilaCraftyCompanion.java @@ -0,0 +1,209 @@ +package mage.cards.m; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetControlledPermanentTriggeredAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.common.delayed.AtTheBeginOfYourNextUpkeepDelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.GetEmblemEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.Card; +import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Controllable; +import mage.game.Game; +import mage.game.command.emblems.LukkaWaywardBonderEmblem; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.targetpointer.FixedTarget; + +import java.util.Objects; +import java.util.UUID; + +import static mage.constants.Outcome.Benefit; + +/** + * @author TheElk801 + */ +public final class MilaCraftyCompanion extends ModalDoubleFacesCard { + + public MilaCraftyCompanion(UUID ownerId, CardSetInfo setInfo) { + super( + ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.FOX}, "{1}{W}{W}", + "Lukka, Wayward Bonder", + new CardType[]{CardType.PLANESWALKER}, new SubType[]{SubType.LUKKA}, "{4}{R}{R}" + ); + + // 1. + // Mila, Crafty Companion + // Legendary Creature - Fox + this.getLeftHalfCard().addSuperType(SuperType.LEGENDARY); + this.getLeftHalfCard().setPT(2, 3); + + // Whenever an opponent attacks one or more planeswalkers you control, put a loyalty counter on each planeswalker you control. + this.getLeftHalfCard().addAbility(new MilaCraftyCompanionTriggeredAbility()); + + // Whenever a permanent you control becomes the target of a spell or ability and opponent controls, you may draw a card. + this.getLeftHalfCard().addAbility(new BecomesTargetControlledPermanentTriggeredAbility( + new DrawCardSourceControllerEffect(1), true + )); + + // 2. + // Lukka, Wayward Bonder + // Legendary Planeswalker - Lukka + this.getRightHalfCard().addSuperType(SuperType.LEGENDARY); + this.getRightHalfCard().addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + + // +1: You may discard a card. If you do, draw a card. If a creature card was discarded this way, draw two cards instead. + this.getRightHalfCard().addAbility(new LoyaltyAbility(new LukkaWaywardBonderDiscardEffect(), 1)); + + // −2: Return target creature card from your graveyard to the battlefield. It gains haste. Exile it at the beginning of your next upkeep. + Ability ability = new LoyaltyAbility(new LukkaWaywardBonderReturnEffect(), -2); + ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); + this.getRightHalfCard().addAbility(ability); + + // −7: You get an emblem with "Whenever a creature enters the battlefield under your control, it deals damage equal to its power to any target." + this.getRightHalfCard().addAbility(new LoyaltyAbility( + new GetEmblemEffect(new LukkaWaywardBonderEmblem()), -7 + )); + } + + private MilaCraftyCompanion(final MilaCraftyCompanion card) { + super(card); + } + + @Override + public MilaCraftyCompanion copy() { + return new MilaCraftyCompanion(this); + } +} + +class MilaCraftyCompanionTriggeredAbility extends TriggeredAbilityImpl { + + MilaCraftyCompanionTriggeredAbility() { + super(Zone.BATTLEFIELD, new AddCountersAllEffect( + CounterType.LOYALTY.createInstance(), StaticFilters.FILTER_CONTROLLED_PERMANENT_PLANESWALKER + ), false); + } + + private MilaCraftyCompanionTriggeredAbility(final MilaCraftyCompanionTriggeredAbility ability) { + super(ability); + } + + @Override + public MilaCraftyCompanionTriggeredAbility copy() { + return new MilaCraftyCompanionTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return game.getCombat() + .getAttackers() + .stream() + .filter(attackerId -> game.getOpponents(getControllerId()).contains(game.getControllerId(attackerId))) + .map(game.getCombat()::getDefenderId) + .map(game::getPermanent) + .filter(Objects::nonNull) + .filter(MageObject::isPlaneswalker) + .map(Controllable::getControllerId) + .anyMatch(getControllerId()::equals); + } + + @Override + public String getRule() { + return "Whenever an opponent attacks one or more planeswalkers you control, " + + "put a loyalty counter on each planeswalker you control."; + } +} + +class LukkaWaywardBonderDiscardEffect extends OneShotEffect { + + LukkaWaywardBonderDiscardEffect() { + super(Benefit); + staticText = "you may discard a card. If you do, draw a card. " + + "If a creature card was discarded this way, draw two cards instead"; + } + + private LukkaWaywardBonderDiscardEffect(final LukkaWaywardBonderDiscardEffect effect) { + super(effect); + } + + @Override + public LukkaWaywardBonderDiscardEffect copy() { + return new LukkaWaywardBonderDiscardEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Card card = player.discard(0, 1, false, source, game).getRandom(game); + if (card == null) { + return false; + } + player.drawCards(card.isCreature() ? 2 : 1, source, game); + return true; + } +} + +class LukkaWaywardBonderReturnEffect extends OneShotEffect { + + LukkaWaywardBonderReturnEffect() { + super(Outcome.Benefit); + staticText = "return target creature card from your graveyard to the battlefield. " + + "It gains haste. Exile it at the beginning of your next upkeep"; + } + + private LukkaWaywardBonderReturnEffect(final LukkaWaywardBonderReturnEffect effect) { + super(effect); + } + + @Override + public LukkaWaywardBonderReturnEffect copy() { + return new LukkaWaywardBonderReturnEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(source.getFirstTarget()); + if (player == null || card == null) { + return false; + } + player.moveCards(card, Zone.BATTLEFIELD, source, game); + Permanent permanent = game.getPermanent(card.getId()); + if (permanent == null) { + return false; + } + game.addEffect(new GainAbilityTargetEffect( + HasteAbility.getInstance(), Duration.EndOfTurn + ).setTargetPointer(new FixedTarget(permanent, game)), source); + game.addDelayedTriggeredAbility(new AtTheBeginOfYourNextUpkeepDelayedTriggeredAbility( + new ExileTargetEffect() + .setText("Exile it at the beginning of your next upkeep.") + .setTargetPointer(new FixedTarget(permanent, game)), + Duration.Custom, true + ), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/m/Mimeofacture.java b/Mage.Sets/src/mage/cards/m/Mimeofacture.java index c2a3db24b60f..75fc2b3bb66a 100644 --- a/Mage.Sets/src/mage/cards/m/Mimeofacture.java +++ b/Mage.Sets/src/mage/cards/m/Mimeofacture.java @@ -53,7 +53,7 @@ class MimeofactureEffect extends OneShotEffect { super(Outcome.PutCardInPlay); this.staticText = "Choose target permanent an opponent controls. " + "Search that player's library for a card with the same name and put it onto the battlefield under your control. " - + "Then that player shuffles their library."; + + "Then that player shuffles."; } MimeofactureEffect(final MimeofactureEffect effect) { diff --git a/Mage.Sets/src/mage/cards/m/MimicVat.java b/Mage.Sets/src/mage/cards/m/MimicVat.java index 982fadad2ddf..acd490f362c8 100644 --- a/Mage.Sets/src/mage/cards/m/MimicVat.java +++ b/Mage.Sets/src/mage/cards/m/MimicVat.java @@ -91,8 +91,7 @@ public boolean checkTrigger(GameEvent event, Game game) { Permanent permanent = zEvent.getTarget(); if (permanent != null - && zEvent.getToZone() == Zone.GRAVEYARD - && zEvent.getFromZone() == Zone.BATTLEFIELD + && zEvent.isDiesEvent() && !(permanent instanceof PermanentToken) && permanent.isCreature()) { diff --git a/Mage.Sets/src/mage/cards/m/Mindblaze.java b/Mage.Sets/src/mage/cards/m/Mindblaze.java index 1d59f8de9491..c332bca81acb 100644 --- a/Mage.Sets/src/mage/cards/m/Mindblaze.java +++ b/Mage.Sets/src/mage/cards/m/Mindblaze.java @@ -53,7 +53,7 @@ class MindblazeEffect extends OneShotEffect { MindblazeEffect() { super(Outcome.Damage); - staticText = "Name a nonland card and choose a number greater than 0. Target player reveals their library. If that library contains exactly the chosen number of the named card, {this} deals 8 damage to that player. Then that player shuffles their library"; + staticText = "Choose a nonland card name and a number greater than 0. Target player reveals their library. If that library contains exactly the chosen number of cards with the chosen name, {this} deals 8 damage to that player. Then that player shuffles"; } MindblazeEffect(final MindblazeEffect effect) { diff --git a/Mage.Sets/src/mage/cards/m/Mindmelter.java b/Mage.Sets/src/mage/cards/m/Mindmelter.java index 64d7cae948f4..a964a6d77150 100644 --- a/Mage.Sets/src/mage/cards/m/Mindmelter.java +++ b/Mage.Sets/src/mage/cards/m/Mindmelter.java @@ -1,12 +1,9 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.ActivateAsSorceryActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.ExileFromZoneTargetEffect; import mage.abilities.keyword.CantBeBlockedSourceAbility; import mage.abilities.keyword.DevoidAbility; @@ -15,17 +12,17 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.FilterCard; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class Mindmelter extends CardImpl { public Mindmelter(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{B}"); this.subtype.add(SubType.ELDRAZI); this.subtype.add(SubType.DRONE); this.power = new MageInt(2); @@ -38,9 +35,9 @@ public Mindmelter(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new CantBeBlockedSourceAbility()); // {3}{C}: Target opponent exiles a card from their hand. Activate this ability only any time you could cast a sorcery. - Effect effect = new ExileFromZoneTargetEffect(Zone.HAND, null, "", new FilterCard()); - effect.setText("Target opponent exiles a card from their hand"); - Ability ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{3}{C}")); + Ability ability = new ActivateAsSorceryActivatedAbility( + Zone.BATTLEFIELD, new ExileFromZoneTargetEffect(Zone.HAND, false), new ManaCostsImpl("{3}{C}") + ); ability.addTarget(new TargetOpponent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/Mindreaver.java b/Mage.Sets/src/mage/cards/m/Mindreaver.java index 9ada5541b40a..24eb1383c661 100644 --- a/Mage.Sets/src/mage/cards/m/Mindreaver.java +++ b/Mage.Sets/src/mage/cards/m/Mindreaver.java @@ -9,24 +9,23 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.keyword.HeroicAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.FilterSpell; -import mage.filter.predicate.Predicate; import mage.game.ExileZone; import mage.game.Game; +import mage.game.stack.Spell; import mage.players.Player; import mage.target.TargetPlayer; import mage.target.TargetSpell; import mage.util.CardUtil; -import java.util.HashSet; -import java.util.Set; +import java.util.Objects; import java.util.UUID; /** @@ -48,11 +47,9 @@ public Mindreaver(UUID ownerId, CardSetInfo setInfo) { this.addAbility(ability); // {U}{U}, Sacrifice Mindreaver: Counter target spell with the same name as a card exiled with Mindreaver. - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CounterTargetEffect(), new ManaCostsImpl("{U}{U}")); - FilterSpell filter = new FilterSpell("spell with the same name as a card exiled with {this}"); - filter.add(new MindreaverNamePredicate(this.getId())); - ability.addTarget(new TargetSpell(filter)); + ability = new SimpleActivatedAbility(new CounterTargetEffect(), new ManaCostsImpl("{U}{U}")); ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new MindreaverTarget()); this.addAbility(ability); } @@ -66,62 +63,63 @@ public Mindreaver copy() { } } -class MindreaverExileEffect extends OneShotEffect { +class MindreaverTarget extends TargetSpell { - public MindreaverExileEffect() { - super(Outcome.Exile); - this.staticText = "exile the top three cards of target opponent's library"; - } + private static final FilterSpell filter + = new FilterSpell("spell with the same name as a card exiled with {this}"); - public MindreaverExileEffect(final MindreaverExileEffect effect) { - super(effect); + MindreaverTarget() { + super(filter); } - @Override - public MindreaverExileEffect copy() { - return new MindreaverExileEffect(this); + private MindreaverTarget(final MindreaverTarget target) { + super(target); } @Override - public boolean apply(Game game, Ability source) { - UUID exileId = CardUtil.getCardExileZoneId(game, source); - MageObject sourceObject = source.getSourceObject(game); - Player opponent = game.getPlayer(this.getTargetPointer().getFirst(game, source)); - if (opponent != null && sourceObject != null) { - for (int i = 0; i < 3; i++) { - Card card = opponent.getLibrary().getFromTop(game); - if (card != null) { - card.moveToExile(exileId, sourceObject.getIdName(), source, game); - } - } - } - return false; + public boolean canTarget(UUID id, Ability source, Game game) { + Spell spell = game.getSpell(id); + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + return super.canTarget(id, source, game) + && spell != null + && exileZone != null + && !exileZone.isEmpty() + && exileZone + .getCards(game) + .stream() + .filter(Objects::nonNull) + .anyMatch(card -> CardUtil.haveSameNames(spell, card)); } } -class MindreaverNamePredicate implements Predicate { +class MindreaverExileEffect extends OneShotEffect { - private final UUID sourceId; + MindreaverExileEffect() { + super(Outcome.Exile); + this.staticText = "exile the top three cards of target player's library"; + } - public MindreaverNamePredicate(UUID sourceId) { - this.sourceId = sourceId; + private MindreaverExileEffect(final MindreaverExileEffect effect) { + super(effect); } @Override - public boolean apply(MageObject input, Game game) { - Set cardNames = new HashSet<>(); - UUID exileId = CardUtil.getCardExileZoneId(game, sourceId); - ExileZone exileZone = game.getExile().getExileZone(exileId); - if (exileZone != null) { - for (Card card : exileZone.getCards(game)) { - cardNames.add(card.getName()); - } - } - return cardNames.stream().anyMatch(needName -> CardUtil.haveSameNames(input, needName, game)); + public MindreaverExileEffect copy() { + return new MindreaverExileEffect(this); } @Override - public String toString() { - return "spell with the same name as a card exiled with {this}"; + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player player = game.getPlayer(source.getFirstTarget()); + MageObject mageObject = source.getSourceObject(game); + if (controller == null || player == null || mageObject == null) { + return false; + } + Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 3)); + return controller.moveCardsToExile( + cards.getCards(game), source, game, true, + CardUtil.getExileZoneId(game, source), mageObject.getIdName() + ); } } diff --git a/Mage.Sets/src/mage/cards/m/Mindshrieker.java b/Mage.Sets/src/mage/cards/m/Mindshrieker.java index b37a91f4b13d..83050d28100d 100644 --- a/Mage.Sets/src/mage/cards/m/Mindshrieker.java +++ b/Mage.Sets/src/mage/cards/m/Mindshrieker.java @@ -58,7 +58,7 @@ class MindshriekerEffect extends OneShotEffect { MindshriekerEffect() { super(Outcome.Detriment); staticText = "Target player mills a card. {this} gets +X/+X until end of turn, " + - "where X is the milled card's converted mana cost"; + "where X is the milled card's mana value"; } private MindshriekerEffect(final MindshriekerEffect effect) { @@ -76,7 +76,7 @@ public boolean apply(Game game, Ability source) { .getCards(game) .stream() .filter(Objects::nonNull) - .mapToInt(MageObject::getConvertedManaCost) + .mapToInt(MageObject::getManaValue) .sum(); if (totalCMC > 0) { game.addEffect(new BoostSourceEffect(totalCMC, totalCMC, Duration.EndOfTurn), source); diff --git a/Mage.Sets/src/mage/cards/m/MindwhipSliver.java b/Mage.Sets/src/mage/cards/m/MindwhipSliver.java index 980a80d9abe5..949d6c04942d 100644 --- a/Mage.Sets/src/mage/cards/m/MindwhipSliver.java +++ b/Mage.Sets/src/mage/cards/m/MindwhipSliver.java @@ -41,7 +41,7 @@ public MindwhipSliver(UUID ownerId, CardSetInfo setInfo) { gainedAbility.addCost(new SacrificeSourceCost()); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(gainedAbility, Duration.WhileOnBattlefield, filter, - "All Slivers have \"{2}, Sacrifice this permanent: Target player discards a card at random. Activate this ability only any time you could cast a sorcery.\""))); + "All Slivers have \"{2}, Sacrifice this permanent: Target player discards a card at random. Activate only as a sorcery.\""))); } private MindwhipSliver(final MindwhipSliver card) { diff --git a/Mage.Sets/src/mage/cards/m/MinionOfLeshrac.java b/Mage.Sets/src/mage/cards/m/MinionOfLeshrac.java index 93ef2ce618db..54e37df84ca5 100644 --- a/Mage.Sets/src/mage/cards/m/MinionOfLeshrac.java +++ b/Mage.Sets/src/mage/cards/m/MinionOfLeshrac.java @@ -16,7 +16,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -89,7 +89,7 @@ public boolean apply(Game game, Ability source) { filterCreature.add(AnotherPredicate.instance); TargetControlledPermanent target = new TargetControlledPermanent(filterCreature); SacrificeTargetCost cost = new SacrificeTargetCost(target); - if (controller.chooseUse(Outcome.AIDontUseIt, "Do you wish to sacrifice another creature to prevent the 5 damage to you?", source, game) + if (controller.chooseUse(Outcome.AIDontUseIt, "Sacrifice another creature to prevent the damage?", source, game) && cost.canPay(source, source, source.getControllerId(), game) && cost.pay(source, game, source, source.getControllerId(), true)) { return true; diff --git a/Mage.Sets/src/mage/cards/m/Mirari.java b/Mage.Sets/src/mage/cards/m/Mirari.java index eb0f346f7411..c67e5a2cc384 100644 --- a/Mage.Sets/src/mage/cards/m/Mirari.java +++ b/Mage.Sets/src/mage/cards/m/Mirari.java @@ -87,8 +87,8 @@ public boolean checkTrigger(GameEvent event, Game game) { private boolean isControlledInstantOrSorcery(Spell spell) { return spell != null - && (spell.isControlledBy(this.getControllerId())) - && (spell.isInstant() || spell.isSorcery()); + && spell.isControlledBy(this.getControllerId()) + && spell.isInstantOrSorcery(); } @Override diff --git a/Mage.Sets/src/mage/cards/m/MirrorUniverse.java b/Mage.Sets/src/mage/cards/m/MirrorUniverse.java index 16ebdd6dff5e..9766fb0b84a2 100644 --- a/Mage.Sets/src/mage/cards/m/MirrorUniverse.java +++ b/Mage.Sets/src/mage/cards/m/MirrorUniverse.java @@ -7,7 +7,7 @@ import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.decorator.ConditionalActivatedAbility; -import mage.abilities.effects.common.ExchangeLifeTargetEffect; +import mage.abilities.effects.common.ExchangeLifeControllerTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -27,7 +27,7 @@ public MirrorUniverse(UUID ownerId, CardSetInfo setInfo) { // {tap}, Sacrifice Mirror Universe: Exchange life totals with target opponent. Activate this ability only during your upkeep. Ability ability = new ConditionalActivatedAbility( Zone.BATTLEFIELD, - new ExchangeLifeTargetEffect(), + new ExchangeLifeControllerTargetEffect(), new TapSourceCost(), new IsStepCondition(PhaseStep.UPKEEP), null); diff --git a/Mage.Sets/src/mage/cards/m/MirrorwingDragon.java b/Mage.Sets/src/mage/cards/m/MirrorwingDragon.java index c2d6d8d886f6..18673de1eae8 100644 --- a/Mage.Sets/src/mage/cards/m/MirrorwingDragon.java +++ b/Mage.Sets/src/mage/cards/m/MirrorwingDragon.java @@ -1,8 +1,7 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.common.CopySpellForEachItCouldTargetEffect; @@ -12,25 +11,29 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.FilterInPlay; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; +import mage.filter.predicate.mageobject.MageObjectReferencePredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.game.stack.Spell; +import mage.game.stack.StackObject; import mage.players.Player; import mage.target.Target; import mage.util.TargetAddress; +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + /** - * * @author LevelX2 */ public final class MirrorwingDragon extends CardImpl { public MirrorwingDragon(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}"); this.subtype.add(SubType.DRAGON); this.power = new MageInt(4); this.toughness = new MageInt(5); @@ -59,7 +62,7 @@ class MirrorwingDragonCopyTriggeredAbility extends TriggeredAbilityImpl { super(Zone.BATTLEFIELD, new MirrorwingDragonCopySpellEffect(), false); } - MirrorwingDragonCopyTriggeredAbility(final MirrorwingDragonCopyTriggeredAbility ability) { + private MirrorwingDragonCopyTriggeredAbility(final MirrorwingDragonCopyTriggeredAbility ability) { super(ability); } @@ -80,26 +83,34 @@ public boolean checkTrigger(GameEvent event, Game game) { } private boolean checkSpell(Spell spell, Game game) { - if (spell != null - && (spell.isInstant() || spell.isSorcery())) { - boolean noTargets = true; - for (TargetAddress addr : TargetAddress.walk(spell)) { - noTargets = false; - Target targetInstance = addr.getTarget(spell); - for (UUID target : targetInstance.getTargets()) { - Permanent permanent = game.getPermanent(target); - if (permanent == null || !permanent.getId().equals(getSourceId())) { - return false; - } - } + if (spell == null || !spell.isInstantOrSorcery()) { + return false; + } + boolean noTargets = true; + for (TargetAddress addr : TargetAddress.walk(spell)) { + if (addr == null) { + continue; } - if (noTargets) { - return false; + noTargets = false; + Target targetInstance = addr.getTarget(spell); + if (targetInstance == null) { + continue; + } + for (UUID target : targetInstance.getTargets()) { + if (target == null) { + continue; + } + Permanent permanent = game.getPermanent(target); + if (permanent == null || !permanent.getId().equals(getSourceId())) { + return false; + } } - getEffects().get(0).setValue("triggeringSpell", spell); - return true; } - return false; + if (noTargets) { + return false; + } + getEffects().setValue("triggeringSpell", spell); + return true; } @Override @@ -110,53 +121,45 @@ public String getRule() { } } -class MirrorwingDragonCopySpellEffect extends CopySpellForEachItCouldTargetEffect { +class MirrorwingDragonCopySpellEffect extends CopySpellForEachItCouldTargetEffect { - public MirrorwingDragonCopySpellEffect() { - this(new FilterControlledCreaturePermanent()); + MirrorwingDragonCopySpellEffect() { + super(); this.staticText = "that player copies that spell for each other creature they control that the spell could target. Each copy targets a different one of those creatures."; } - public MirrorwingDragonCopySpellEffect(MirrorwingDragonCopySpellEffect effect) { + private MirrorwingDragonCopySpellEffect(MirrorwingDragonCopySpellEffect effect) { super(effect); } - private MirrorwingDragonCopySpellEffect(FilterInPlay filter) { - super(filter); - } - @Override protected Player getPlayer(Game game, Ability source) { - Spell spell = getSpell(game, source); - if (spell != null) { - return game.getPlayer(spell.getControllerId()); + Spell spell = getStackObject(game, source); + if (spell == null) { + return null; } - return null; + return game.getPlayer(spell.getControllerId()); } @Override - protected Spell getSpell(Game game, Ability source) { - return (Spell) getValue("triggeringSpell"); + protected List getPossibleTargets(StackObject stackObject, Player player, Ability source, Game game) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + return game.getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_CONTROLLED_CREATURE, + player.getId(), source.getSourceId(), game + ).stream() + .filter(Objects::nonNull) + .filter(p -> !p.equals(permanent)) + .filter(p -> stackObject.canTarget(game, p.getId())) + .map(p -> new MageObjectReference(p, game)) + .map(MageObjectReferencePredicate::new) + .collect(Collectors.toList()); } @Override - protected boolean changeTarget(Target target, Game game, Ability source) { - return true; - } - - @Override - protected void modifyCopy(Spell copy, Game game, Ability source) { - Spell spell = getSpell(game, source); - copy.setControllerId(spell.getControllerId()); - } - - @Override - protected boolean okUUIDToCopyFor(UUID potentialTarget, Game game, Ability source, Spell spell) { - Permanent permanent = game.getPermanent(potentialTarget); - if (permanent == null || !permanent.isControlledBy(spell.getControllerId())) { - return false; - } - return true; + protected Spell getStackObject(Game game, Ability source) { + return (Spell) getValue("triggeringSpell"); } @Override diff --git a/Mage.Sets/src/mage/cards/m/MirrorwoodTreefolk.java b/Mage.Sets/src/mage/cards/m/MirrorwoodTreefolk.java index 0b4e763110ea..906b070e4c58 100644 --- a/Mage.Sets/src/mage/cards/m/MirrorwoodTreefolk.java +++ b/Mage.Sets/src/mage/cards/m/MirrorwoodTreefolk.java @@ -73,7 +73,7 @@ public void init(Ability source, Game game) { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/m/Mirrorworks.java b/Mage.Sets/src/mage/cards/m/Mirrorworks.java index 6aec94982e07..79b440dc0877 100644 --- a/Mage.Sets/src/mage/cards/m/Mirrorworks.java +++ b/Mage.Sets/src/mage/cards/m/Mirrorworks.java @@ -14,7 +14,7 @@ import mage.constants.Zone; import mage.filter.common.FilterArtifactPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; /** diff --git a/Mage.Sets/src/mage/cards/m/Misdirection.java b/Mage.Sets/src/mage/cards/m/Misdirection.java index eebf956e838a..3e56791c8549 100644 --- a/Mage.Sets/src/mage/cards/m/Misdirection.java +++ b/Mage.Sets/src/mage/cards/m/Misdirection.java @@ -14,7 +14,7 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardIdPredicate; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.mageobject.NumberOfTargetsPredicate; +import mage.filter.predicate.other.NumberOfTargetsPredicate; import mage.target.TargetSpell; import mage.target.common.TargetCardInHand; diff --git a/Mage.Sets/src/mage/cards/m/MishraArtificerProdigy.java b/Mage.Sets/src/mage/cards/m/MishraArtificerProdigy.java index cd737ee571e2..8ac2cbe56d5a 100644 --- a/Mage.Sets/src/mage/cards/m/MishraArtificerProdigy.java +++ b/Mage.Sets/src/mage/cards/m/MishraArtificerProdigy.java @@ -87,7 +87,7 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { - return "Whenever you cast an artifact spell, you may search your graveyard, hand, and/or library for a card with the same name as that spell and put it onto the battlefield. If you search your library this way, shuffle it."; + return "Whenever you cast an artifact spell, you may search your graveyard, hand, and/or library for a card with the same name as that spell and put it onto the battlefield. If you search your library this way, shuffle."; } } @@ -97,7 +97,7 @@ class MishraArtificerProdigyEffect extends OneShotEffect { MishraArtificerProdigyEffect() { super(Outcome.PutCardInPlay); - this.staticText = "Search your graveyard, hand, and/or library for a card named " + cardName + " and put it onto the battlefield. If you search your library this way, shuffle it."; + this.staticText = "Search your graveyard, hand, and/or library for a card named " + cardName + " and put it onto the battlefield. If you search your library this way, shuffle."; } MishraArtificerProdigyEffect(final MishraArtificerProdigyEffect effect) { diff --git a/Mage.Sets/src/mage/cards/m/MishrasWarMachine.java b/Mage.Sets/src/mage/cards/m/MishrasWarMachine.java index 9687f11135cb..f4f2dc9dbf99 100644 --- a/Mage.Sets/src/mage/cards/m/MishrasWarMachine.java +++ b/Mage.Sets/src/mage/cards/m/MishrasWarMachine.java @@ -66,7 +66,7 @@ public boolean apply(Game game, Ability source) { if (controller != null && sourcePermanent != null) { DiscardCardCost cost = new DiscardCardCost(); - if (controller.chooseUse(Outcome.Benefit, "Do you wish to discard a card to prevent the 3 damage to you?", source, game) + if (controller.chooseUse(Outcome.Benefit, "Discard a card to prevent the damage?", source, game) && cost.canPay(source, source, source.getControllerId(), game) && cost.pay(source, game, source, source.getControllerId(), true)) { return true; diff --git a/Mage.Sets/src/mage/cards/m/MistfordRiverTurtle.java b/Mage.Sets/src/mage/cards/m/MistfordRiverTurtle.java index c784a8a602d6..10b3a918d355 100644 --- a/Mage.Sets/src/mage/cards/m/MistfordRiverTurtle.java +++ b/Mage.Sets/src/mage/cards/m/MistfordRiverTurtle.java @@ -12,7 +12,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.AttackingPredicate; import mage.target.TargetPermanent; diff --git a/Mage.Sets/src/mage/cards/m/MistformUltimus.java b/Mage.Sets/src/mage/cards/m/MistformUltimus.java index 11090369dd7a..d4e8c44122f0 100644 --- a/Mage.Sets/src/mage/cards/m/MistformUltimus.java +++ b/Mage.Sets/src/mage/cards/m/MistformUltimus.java @@ -1,14 +1,12 @@ package mage.cards.m; import mage.MageInt; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.continuous.IsAllCreatureTypesSourceEffect; +import mage.abilities.keyword.ChangelingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.Zone; import java.util.UUID; @@ -26,7 +24,7 @@ public MistformUltimus(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(3); // Mistform Ultimus is every creature type. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new IsAllCreatureTypesSourceEffect())); + this.addAbility(new ChangelingAbility(false)); } private MistformUltimus(final MistformUltimus card) { diff --git a/Mage.Sets/src/mage/cards/m/MistmeadowSkulk.java b/Mage.Sets/src/mage/cards/m/MistmeadowSkulk.java index efeafcb87cd7..8f3800f23a31 100644 --- a/Mage.Sets/src/mage/cards/m/MistmeadowSkulk.java +++ b/Mage.Sets/src/mage/cards/m/MistmeadowSkulk.java @@ -11,7 +11,7 @@ import mage.constants.SubType; import mage.constants.ComparisonType; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; /** * @@ -19,10 +19,10 @@ */ public final class MistmeadowSkulk extends CardImpl { - private static final FilterCard filter = new FilterCard("converted mana cost 3 or greater"); + private static final FilterCard filter = new FilterCard("mana value 3 or greater"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 2)); + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 2)); } public MistmeadowSkulk(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/m/MistyRainforest.java b/Mage.Sets/src/mage/cards/m/MistyRainforest.java index 18824b1b9e75..21154548aad3 100644 --- a/Mage.Sets/src/mage/cards/m/MistyRainforest.java +++ b/Mage.Sets/src/mage/cards/m/MistyRainforest.java @@ -20,7 +20,7 @@ public final class MistyRainforest extends CardImpl { public MistyRainforest(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.LAND},null); this.frameColor = new ObjectColor("UG"); - this.addAbility(new FetchLandActivatedAbility(EnumSet.of(SubType.FOREST, SubType.ISLAND))); + this.addAbility(new FetchLandActivatedAbility(SubType.FOREST, SubType.ISLAND)); } private MistyRainforest(final MistyRainforest card) { diff --git a/Mage.Sets/src/mage/cards/m/MitoticManipulation.java b/Mage.Sets/src/mage/cards/m/MitoticManipulation.java index 1ae31dcecf02..19e57b7565b5 100644 --- a/Mage.Sets/src/mage/cards/m/MitoticManipulation.java +++ b/Mage.Sets/src/mage/cards/m/MitoticManipulation.java @@ -81,7 +81,7 @@ public boolean apply(Game game, Ability source) { filter.add(Predicates.or(namePredicates)); TargetCard target = new TargetCard(Zone.LIBRARY, filter); if (cardsFromTop.count(filter, source.getSourceId(), source.getControllerId(), game) > 0 - && controller.chooseUse(Outcome.PutCardInPlay, "Do you wish to put a card on the battlefield?", source, game)) { + && controller.chooseUse(Outcome.PutCardInPlay, "Put a card on the battlefield?", source, game)) { if (controller.choose(Outcome.PutCardInPlay, cardsFromTop, target, game)) { Card card = cardsFromTop.get(target.getFirstTarget(), game); if (card != null) { diff --git a/Mage.Sets/src/mage/cards/m/MizzixOfTheIzmagnus.java b/Mage.Sets/src/mage/cards/m/MizzixOfTheIzmagnus.java index 3c0a08c1ae7a..b5bc737a42cf 100644 --- a/Mage.Sets/src/mage/cards/m/MizzixOfTheIzmagnus.java +++ b/Mage.Sets/src/mage/cards/m/MizzixOfTheIzmagnus.java @@ -28,7 +28,7 @@ */ public final class MizzixOfTheIzmagnus extends CardImpl { - private static final FilterInstantOrSorcerySpell filter = new FilterInstantOrSorcerySpell("an instant or sorcery spell with converted mana cost greater than the number of experience counters you have"); + private static final FilterInstantOrSorcerySpell filter = new FilterInstantOrSorcerySpell("an instant or sorcery spell with mana value greater than the number of experience counters you have"); static { filter.add(new MizzixOfTheIzmagnusPredicate()); @@ -68,7 +68,7 @@ public boolean apply(MageObject input, Game game) { if (spell != null) { Player controller = game.getPlayer(spell.getControllerId()); if (controller != null) { - if (spell.getConvertedManaCost() > controller.getCounters().getCount(CounterType.EXPERIENCE)) { + if (spell.getManaValue() > controller.getCounters().getCount(CounterType.EXPERIENCE)) { return true; } } diff --git a/Mage.Sets/src/mage/cards/m/MizzixsMastery.java b/Mage.Sets/src/mage/cards/m/MizzixsMastery.java index f87327b2fbdd..9dd993e15874 100644 --- a/Mage.Sets/src/mage/cards/m/MizzixsMastery.java +++ b/Mage.Sets/src/mage/cards/m/MizzixsMastery.java @@ -34,12 +34,12 @@ public MizzixsMastery(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new MizzixsMasteryEffect()); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard( new FilterInstantOrSorceryCard("card that's an instant or sorcery from your graveyard"))); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); // Overload {5}{R}{R}{R} Ability ability = new OverloadAbility(this, new MizzixsMasteryOverloadEffect(), new ManaCostsImpl("{5}{R}{R}{R}")); - ability.addEffect(ExileSpellEffect.getInstance()); + ability.addEffect(new ExileSpellEffect()); this.addAbility(ability); } @@ -79,7 +79,7 @@ public boolean apply(Game game, Ability source) { if (card != null) { if (controller.moveCards(card, Zone.EXILED, source, game)) { Card cardCopy = game.copyCard(card, source, source.getControllerId()); - if (cardCopy.getSpellAbility().canChooseTarget(game) + if (cardCopy.getSpellAbility().canChooseTarget(game, controller.getId()) && controller.chooseUse(outcome, "Cast copy of " + card.getName() + " without paying its mana cost?", source, game)) { game.getState().setValue("PlayFromNotOwnHandZone" + cardCopy.getId(), Boolean.TRUE); @@ -135,7 +135,7 @@ public boolean apply(Game game, Ability source) { if (controller.chooseTarget(Outcome.PlayForFree, copiedCards, targetCard, source, game)) { Card selectedCard = game.getCard(targetCard.getFirstTarget()); if (selectedCard != null - && selectedCard.getSpellAbility().canChooseTarget(game)) { + && selectedCard.getSpellAbility().canChooseTarget(game, controller.getId())) { game.getState().setValue("PlayFromNotOwnHandZone" + selectedCard.getId(), Boolean.TRUE); controller.cast(controller.chooseAbilityForCast(selectedCard, game, true), game, true, new ApprovingObject(source, game)); diff --git a/Mage.Sets/src/mage/cards/m/MnemonicBetrayal.java b/Mage.Sets/src/mage/cards/m/MnemonicBetrayal.java index ad87e2ded66e..c2333bd1a26e 100644 --- a/Mage.Sets/src/mage/cards/m/MnemonicBetrayal.java +++ b/Mage.Sets/src/mage/cards/m/MnemonicBetrayal.java @@ -32,7 +32,7 @@ public MnemonicBetrayal(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new MnemonicBetrayalExileEffect()); // Exile Mnemonic Betrayal. - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private MnemonicBetrayal(final MnemonicBetrayal card) { diff --git a/Mage.Sets/src/mage/cards/m/MnemonicDeluge.java b/Mage.Sets/src/mage/cards/m/MnemonicDeluge.java index c66bd0d9dc49..cc21aed30a6c 100644 --- a/Mage.Sets/src/mage/cards/m/MnemonicDeluge.java +++ b/Mage.Sets/src/mage/cards/m/MnemonicDeluge.java @@ -26,7 +26,7 @@ public MnemonicDeluge(UUID ownerId, CardSetInfo setInfo) { // Exile target instant or sorcery card from a graveyard. Copy that card three times. You may cast the copies without paying their mana costs. Exile Mnemonic Deluge. this.getSpellAbility().addEffect(new MnemonicDelugeEffect()); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); this.getSpellAbility().addTarget(new TargetCardInGraveyard(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY)); } diff --git a/Mage.Sets/src/mage/cards/m/MobileFort.java b/Mage.Sets/src/mage/cards/m/MobileFort.java index 7a1297e0841c..deab260e391f 100644 --- a/Mage.Sets/src/mage/cards/m/MobileFort.java +++ b/Mage.Sets/src/mage/cards/m/MobileFort.java @@ -34,7 +34,7 @@ public MobileFort(UUID ownerId, CardSetInfo setInfo) { // {3}: Mobile Fort gets +3/-1 until end of turn and can attack this turn as though it didn't have defender. Activate this ability only once each turn. Effect effect = new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.EndOfTurn); - effect.setText("and can attack as though it didn't have defender"); + effect.setText("and can attack this turn as though it didn't have defender"); Ability ability = new LimitedTimesPerTurnActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(3, -1, Duration.EndOfTurn), new ManaCostsImpl("{3}")); ability.addEffect(effect); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/m/MobileGarrison.java b/Mage.Sets/src/mage/cards/m/MobileGarrison.java index 70be4dc56bbb..0d1009a69bf2 100644 --- a/Mage.Sets/src/mage/cards/m/MobileGarrison.java +++ b/Mage.Sets/src/mage/cards/m/MobileGarrison.java @@ -12,7 +12,7 @@ import mage.constants.SubType; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/m/MoggBombers.java b/Mage.Sets/src/mage/cards/m/MoggBombers.java index 6a71b3185f5e..49b996e0a826 100644 --- a/Mage.Sets/src/mage/cards/m/MoggBombers.java +++ b/Mage.Sets/src/mage/cards/m/MoggBombers.java @@ -13,7 +13,7 @@ import mage.constants.CardType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPlayer; /** diff --git a/Mage.Sets/src/mage/cards/m/MoggSquad.java b/Mage.Sets/src/mage/cards/m/MoggSquad.java index 00fe1a712f0d..5b96398b9749 100644 --- a/Mage.Sets/src/mage/cards/m/MoggSquad.java +++ b/Mage.Sets/src/mage/cards/m/MoggSquad.java @@ -16,7 +16,7 @@ import mage.constants.Duration; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/m/Molder.java b/Mage.Sets/src/mage/cards/m/Molder.java index 00cbe6a69316..0302c2e383cb 100644 --- a/Mage.Sets/src/mage/cards/m/Molder.java +++ b/Mage.Sets/src/mage/cards/m/Molder.java @@ -11,7 +11,7 @@ import mage.constants.ComparisonType; import mage.filter.FilterPermanent; import mage.filter.common.FilterArtifactOrEnchantmentPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.target.TargetPermanent; import mage.target.targetadjustment.TargetAdjuster; @@ -27,7 +27,7 @@ public Molder(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{G}"); // Destroy target artifact or enchantment with converted mana cost X. It can't be regenerated. You gain X life. - this.getSpellAbility().addEffect(new DestroyTargetEffect("Destroy target artifact or enchantment with converted mana cost X", true)); + this.getSpellAbility().addEffect(new DestroyTargetEffect("Destroy target artifact or enchantment with mana value X", true)); this.getSpellAbility().addEffect(new GainLifeEffect(ManacostVariableValue.instance)); this.getSpellAbility().setTargetAdjuster(MolderAdjuster.instance); } @@ -49,8 +49,8 @@ enum MolderAdjuster implements TargetAdjuster { public void adjustTargets(Ability ability, Game game) { ability.getTargets().clear(); int xValue = ability.getManaCostsToPay().getX(); - FilterPermanent filter = new FilterArtifactOrEnchantmentPermanent("artifact or enchantment with converted mana cost " + xValue); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, xValue)); + FilterPermanent filter = new FilterArtifactOrEnchantmentPermanent("artifact or enchantment with mana value " + xValue); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, xValue)); ability.addTarget(new TargetPermanent(filter)); } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/m/MolderingKarok.java b/Mage.Sets/src/mage/cards/m/MolderingKarok.java new file mode 100644 index 000000000000..c8c4be3af52b --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MolderingKarok.java @@ -0,0 +1,41 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.TrampleAbility; +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 MolderingKarok extends CardImpl { + + public MolderingKarok(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{G}"); + + this.subtype.add(SubType.ZOMBIE); + this.subtype.add(SubType.CROCODILE); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + } + + private MolderingKarok(final MolderingKarok card) { + super(card); + } + + @Override + public MolderingKarok copy() { + return new MolderingKarok(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MoltenBirth.java b/Mage.Sets/src/mage/cards/m/MoltenBirth.java index f645698e129a..b6499b04e56f 100644 --- a/Mage.Sets/src/mage/cards/m/MoltenBirth.java +++ b/Mage.Sets/src/mage/cards/m/MoltenBirth.java @@ -1,7 +1,9 @@ package mage.cards.m; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -23,8 +25,8 @@ public MoltenBirth(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{R}{R}"); // Create two 1/1 red Elemental creature tokens. Then flip a coin. If you win the flip, return Molten Birth to its owner's hand. + this.getSpellAbility().addEffect(new CreateTokenEffect(new MoltenBirthElementalToken(), 2)); this.getSpellAbility().addEffect(new MoltenBirthEffect()); - } private MoltenBirth(final MoltenBirth card) { @@ -39,12 +41,12 @@ public MoltenBirth copy() { class MoltenBirthEffect extends OneShotEffect { - public MoltenBirthEffect() { + MoltenBirthEffect() { super(Outcome.PutCreatureInPlay); - staticText = "Create two 1/1 red Elemental creature tokens. Then flip a coin. If you win the flip, return {this} to its owner's hand"; + staticText = "Then flip a coin. If you win the flip, return {this} to its owner's hand"; } - public MoltenBirthEffect(final MoltenBirthEffect effect) { + private MoltenBirthEffect(final MoltenBirthEffect effect) { super(effect); } @@ -56,19 +58,10 @@ public MoltenBirthEffect copy() { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - MoltenBirthElementalToken token = new MoltenBirthElementalToken(); - token.putOntoBattlefield(2, game, source, source.getControllerId()); - if (controller.flipCoin(source, game, true)) { - Card molten = game.getCard(source.getSourceId()); - if (molten != null) { - molten.moveToZone(Zone.HAND, source, game, true); - game.informPlayers(controller.getLogName() + " won the flip. " + molten.getLogName() + " is returned to " + controller.getLogName() + "'s hand."); - } - } - return true; - } - return false; + MageObject sourceObject = source.getSourceObject(game); + return controller != null + && controller.flipCoin(source, game, true) + && sourceObject instanceof Card + && controller.moveCards((Card) sourceObject, Zone.HAND, source, game); } - } diff --git a/Mage.Sets/src/mage/cards/m/MomirVigSimicVisionary.java b/Mage.Sets/src/mage/cards/m/MomirVigSimicVisionary.java index f5fadc474dcc..c7bc45c34d60 100644 --- a/Mage.Sets/src/mage/cards/m/MomirVigSimicVisionary.java +++ b/Mage.Sets/src/mage/cards/m/MomirVigSimicVisionary.java @@ -51,7 +51,7 @@ public MomirVigSimicVisionary(UUID ownerId, CardSetInfo setInfo) { // Whenever you cast a green creature spell, you may search your library for a creature card and reveal it. If you do, shuffle your library and put that card on top of it. Effect effect = new SearchLibraryPutOnLibraryEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_CREATURE), true, true); - effect.setText("you may search your library for a creature card and reveal it. If you do, shuffle your library and put that card on top of it"); + effect.setText("search your library for a creature card, reveal it, then shuffle and put that card on top"); this.addAbility(new SpellCastControllerTriggeredAbility(effect, filter, true)); // Whenever you cast a blue creature spell, reveal the top card of your library. If it's a creature card, put that card into your hand. diff --git a/Mage.Sets/src/mage/cards/m/MondronenShaman.java b/Mage.Sets/src/mage/cards/m/MondronenShaman.java index 097caf5595e4..2bedb848b58a 100644 --- a/Mage.Sets/src/mage/cards/m/MondronenShaman.java +++ b/Mage.Sets/src/mage/cards/m/MondronenShaman.java @@ -1,20 +1,14 @@ - package mage.cards.m; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** * @author North @@ -35,10 +29,7 @@ public MondronenShaman(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each upkeep, if no spells were cast last turn, transform Mondronen Shaman. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, - NoSpellsWereCastLastTurnCondition.instance, - TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private MondronenShaman(final MondronenShaman card) { @@ -49,4 +40,4 @@ private MondronenShaman(final MondronenShaman card) { public MondronenShaman copy() { return new MondronenShaman(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/m/MonkeyCage.java b/Mage.Sets/src/mage/cards/m/MonkeyCage.java index 889bf4374e78..f146d6141e66 100644 --- a/Mage.Sets/src/mage/cards/m/MonkeyCage.java +++ b/Mage.Sets/src/mage/cards/m/MonkeyCage.java @@ -48,7 +48,7 @@ class MonkeyCageEffect extends OneShotEffect { public MonkeyCageEffect() { super(Outcome.Benefit); - staticText = "and create X 2/2 green Ape creature tokens, where X is that creature's converted mana cost"; + staticText = "and create X 2/2 green Ape creature tokens, where X is that creature's mana value"; } public MonkeyCageEffect(final MonkeyCageEffect effect) { @@ -63,7 +63,7 @@ public MonkeyCageEffect copy() { public boolean apply(Game game, Ability source) { Permanent creature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (creature != null) { - int cmc = creature.getConvertedManaCost(); + int cmc = creature.getManaValue(); return new CreateTokenEffect(new ApeToken(), cmc).apply(game, source); } return false; diff --git a/Mage.Sets/src/mage/cards/m/MonologueTax.java b/Mage.Sets/src/mage/cards/m/MonologueTax.java new file mode 100644 index 000000000000..2aece48210d9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MonologueTax.java @@ -0,0 +1,73 @@ +package mage.cards.m; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.token.TreasureToken; +import mage.players.Player; +import mage.watchers.common.CastSpellLastTurnWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MonologueTax extends CardImpl { + + public MonologueTax(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); + + // Whenever an opponent casts their second spell each turn, you create a Treasure token. + this.addAbility(new MonologueTaxTriggeredAbility()); + } + + private MonologueTax(final MonologueTax card) { + super(card); + } + + @Override + public MonologueTax copy() { + return new MonologueTax(this); + } +} + +class MonologueTaxTriggeredAbility extends TriggeredAbilityImpl { + + MonologueTaxTriggeredAbility() { + super(Zone.BATTLEFIELD, new CreateTokenEffect(new TreasureToken())); + } + + private MonologueTaxTriggeredAbility(final MonologueTaxTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Player player = game.getPlayer(getControllerId()); + CastSpellLastTurnWatcher watcher = game.getState().getWatcher(CastSpellLastTurnWatcher.class); + return player != null + && watcher != null + && player.hasOpponent(event.getPlayerId(), game) + && watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(event.getPlayerId()) == 2; + } + + @Override + public MonologueTaxTriggeredAbility copy() { + return new MonologueTaxTriggeredAbility(this); + } + + @Override + public String getRule() { + return "Whenever an opponent casts their second spell each turn, you create a Treasure token."; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MonstrousStep.java b/Mage.Sets/src/mage/cards/m/MonstrousStep.java index 3f2469515551..e873e8e3f9db 100644 --- a/Mage.Sets/src/mage/cards/m/MonstrousStep.java +++ b/Mage.Sets/src/mage/cards/m/MonstrousStep.java @@ -11,7 +11,7 @@ import mage.constants.Duration; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; diff --git a/Mage.Sets/src/mage/cards/m/MoonriseIntruder.java b/Mage.Sets/src/mage/cards/m/MoonriseIntruder.java index 33b2096624db..b765b98d6aff 100644 --- a/Mage.Sets/src/mage/cards/m/MoonriseIntruder.java +++ b/Mage.Sets/src/mage/cards/m/MoonriseIntruder.java @@ -1,29 +1,22 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.keyword.MenaceAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** - * * @author fireshoes */ public final class MoonriseIntruder extends CardImpl { public MoonriseIntruder(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.WEREWOLF); this.power = new MageInt(2); this.toughness = new MageInt(2); @@ -37,8 +30,7 @@ public MoonriseIntruder(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new MenaceAbility()); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Moonrise Intruder. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private MoonriseIntruder(final MoonriseIntruder card) { diff --git a/Mage.Sets/src/mage/cards/m/MoonscarredWerewolf.java b/Mage.Sets/src/mage/cards/m/MoonscarredWerewolf.java index b4a4b97a19be..c5ee197498bd 100644 --- a/Mage.Sets/src/mage/cards/m/MoonscarredWerewolf.java +++ b/Mage.Sets/src/mage/cards/m/MoonscarredWerewolf.java @@ -1,33 +1,26 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.Mana; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; -import mage.abilities.keyword.TransformAbility; import mage.abilities.keyword.VigilanceAbility; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; import mage.constants.Zone; +import java.util.UUID; + /** - * * @author North */ public final class MoonscarredWerewolf extends CardImpl { public MoonscarredWerewolf(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.WEREWOLF); this.color.setGreen(true); @@ -39,13 +32,12 @@ public MoonscarredWerewolf(UUID ownerId, CardSetInfo setInfo) { this.transformable = true; this.addAbility(VigilanceAbility.getInstance()); + // {tap}: Add {G}{G}. this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.GreenMana(2), new TapSourceCost())); + // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Moonscarred Werewolf. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, - TwoOrMoreSpellsWereCastLastTurnCondition.instance, - TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private MoonscarredWerewolf(final MoonscarredWerewolf card) { diff --git a/Mage.Sets/src/mage/cards/m/MorbidBloom.java b/Mage.Sets/src/mage/cards/m/MorbidBloom.java index 309d87af2616..be4780fd4ea2 100644 --- a/Mage.Sets/src/mage/cards/m/MorbidBloom.java +++ b/Mage.Sets/src/mage/cards/m/MorbidBloom.java @@ -1,7 +1,5 @@ - package mage.cards.m; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; @@ -9,27 +7,29 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; import mage.game.Game; import mage.game.permanent.token.SaprolingToken; +import mage.players.Player; import mage.target.common.TargetCardInGraveyard; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class MorbidBloom extends CardImpl { - public MorbidBloom(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{B}{G}"); - + private static final FilterCard filter = new FilterCreatureCard("creature card from a graveyard"); - + public MorbidBloom(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}{G}"); // Exile target creature card from a graveyard, then create X 1/1 green Saproling creature tokens, where X is the exiled card's toughness. this.getSpellAbility().addEffect(new MorbidBloomEffect()); - this.getSpellAbility().addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature from a graveyard"))); - + this.getSpellAbility().addTarget(new TargetCardInGraveyard(filter)); } private MorbidBloom(final MorbidBloom card) { @@ -44,12 +44,14 @@ public MorbidBloom copy() { class MorbidBloomEffect extends OneShotEffect { - public MorbidBloomEffect() { + MorbidBloomEffect() { super(Outcome.PutCreatureInPlay); - staticText = "Exile target creature card from a graveyard, then create X 1/1 green Saproling creature tokens, where X is the exiled card's toughness"; + staticText = "Exile target creature card from a graveyard, " + + "then create X 1/1 green Saproling creature tokens, " + + "where X is the exiled card's toughness"; } - public MorbidBloomEffect(final MorbidBloomEffect effect) { + private MorbidBloomEffect(final MorbidBloomEffect effect) { super(effect); } @@ -60,13 +62,16 @@ public MorbidBloomEffect copy() { @Override public boolean apply(Game game, Ability source) { - Card targetCreatureCard = game.getCard(source.getFirstTarget()); - if (targetCreatureCard != null) { - targetCreatureCard.moveToExile(null, null, source, game); - int toughness = targetCreatureCard.getToughness().getValue(); - SaprolingToken token = new SaprolingToken(); - return token.putOntoBattlefield(toughness, game, source, source.getControllerId()); + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(source.getFirstTarget()); + if (player == null || card == null) { + return false; + } + player.moveCards(card, Zone.EXILED, source, game); + int toughness = card.getToughness().getValue(); + if (toughness < 1) { + return true; } - return false; + return new SaprolingToken().putOntoBattlefield(toughness, game, source, player.getId()); } } diff --git a/Mage.Sets/src/mage/cards/m/MorbidCuriosity.java b/Mage.Sets/src/mage/cards/m/MorbidCuriosity.java index 8f4ee39e2788..df0d913a0080 100644 --- a/Mage.Sets/src/mage/cards/m/MorbidCuriosity.java +++ b/Mage.Sets/src/mage/cards/m/MorbidCuriosity.java @@ -33,7 +33,9 @@ public MorbidCuriosity(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter))); // Draw cards equal to the converted mana cost of the sacrificed permanent. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(new SacrificeCostConvertedMana("permanent"))); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect( + new SacrificeCostConvertedMana("permanent") + ).setText("draw cards equal to the mana value of the sacrificed permanent")); } private MorbidCuriosity(final MorbidCuriosity card) { diff --git a/Mage.Sets/src/mage/cards/m/MoritteOfTheFrost.java b/Mage.Sets/src/mage/cards/m/MoritteOfTheFrost.java index 594b71feceaf..0a409ea6ed95 100644 --- a/Mage.Sets/src/mage/cards/m/MoritteOfTheFrost.java +++ b/Mage.Sets/src/mage/cards/m/MoritteOfTheFrost.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.counters.CounterType; -import mage.filter.StaticFilters; +import mage.filter.FilterPermanent; import mage.game.Game; import mage.util.functions.CopyApplier; @@ -24,6 +24,8 @@ */ public final class MoritteOfTheFrost extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent("a permanent you control"); + public MoritteOfTheFrost(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{U}{U}"); @@ -37,11 +39,7 @@ public MoritteOfTheFrost(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new ChangelingAbility()); // You may have Moritte of the Frost enter the battlefield as a copy of a permanent you control, except it's legendary and snow in addition to its other types and, if it's a creature, it enters with two additional +1/+1 counters on it and has changeling. - this.addAbility(new EntersBattlefieldAbility(new CopyPermanentEffect( - StaticFilters.FILTER_CONTROLLED_PERMANENT, new MoritteOfTheFrostCopyApplier() - ).setText("as a copy of a permanent you control, except it's legendary and snow in addition to its other types " + - "and, if it's a creature, it enters with two additional +1/+1 counters on it and has changeling." - ), true)); + this.addAbility(new EntersBattlefieldAbility(new CopyPermanentEffect(filter, new MoritteOfTheFrostCopyApplier()), true)); } private MoritteOfTheFrost(final MoritteOfTheFrost card) { @@ -56,12 +54,18 @@ public MoritteOfTheFrost copy() { class MoritteOfTheFrostCopyApplier extends CopyApplier { + @Override + public String getText() { + return ", except it's legendary and snow in addition to its other types and, if it's a creature, " + + "it enters with two additional +1/+1 counters on it and has changeling"; + } + @Override public boolean apply(Game game, MageObject blueprint, Ability source, UUID copyToObjectId) { blueprint.addSuperType(SuperType.LEGENDARY); blueprint.addSuperType(SuperType.SNOW); - if (!isCopyOfCopy(source, copyToObjectId) && blueprint.isCreature()) { + if (!isCopyOfCopy(source, blueprint, copyToObjectId) && blueprint.isCreature()) { blueprint.getAbilities().add(new ChangelingAbility()); blueprint.getAbilities().add(new EntersBattlefieldAbility( new AddCountersSourceEffect(CounterType.P1P1.createInstance(2), false) diff --git a/Mage.Sets/src/mage/cards/m/MorkrutNecropod.java b/Mage.Sets/src/mage/cards/m/MorkrutNecropod.java index dae2f4fd6751..d255c7140ea9 100644 --- a/Mage.Sets/src/mage/cards/m/MorkrutNecropod.java +++ b/Mage.Sets/src/mage/cards/m/MorkrutNecropod.java @@ -12,7 +12,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/m/MortalitySpear.java b/Mage.Sets/src/mage/cards/m/MortalitySpear.java new file mode 100644 index 000000000000..f937380c05a9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MortalitySpear.java @@ -0,0 +1,45 @@ +package mage.cards.m; + +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.YouGainedLifeCondition; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Zone; +import mage.target.common.TargetNonlandPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MortalitySpear extends CardImpl { + + private static final Condition condition = new YouGainedLifeCondition(ComparisonType.MORE_THAN, 0); + + public MortalitySpear(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}{G}"); + + // This spell costs {2} less to cast if you gained life this turn. + this.addAbility(new SimpleStaticAbility( + Zone.ALL, new SpellCostReductionSourceEffect(2, condition).setCanWorksOnStackOnly(true) + ).setRuleAtTheTop(true)); + + // Destroy target nonland permanent. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetNonlandPermanent()); + } + + private MortalitySpear(final MortalitySpear card) { + super(card); + } + + @Override + public MortalitySpear copy() { + return new MortalitySpear(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MossbridgeTroll.java b/Mage.Sets/src/mage/cards/m/MossbridgeTroll.java index fa3298fa213b..7bd2ea127995 100644 --- a/Mage.Sets/src/mage/cards/m/MossbridgeTroll.java +++ b/Mage.Sets/src/mage/cards/m/MossbridgeTroll.java @@ -14,7 +14,7 @@ import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.events.GameEvent; diff --git a/Mage.Sets/src/mage/cards/m/MountainValley.java b/Mage.Sets/src/mage/cards/m/MountainValley.java index 1e5e817a7537..7fbf269fd6ed 100644 --- a/Mage.Sets/src/mage/cards/m/MountainValley.java +++ b/Mage.Sets/src/mage/cards/m/MountainValley.java @@ -22,7 +22,7 @@ public MountainValley(UUID ownerId, CardSetInfo setInfo) { // Mountain Valley enters the battlefield tapped. this.addAbility(new EntersBattlefieldTappedAbility()); // {tap}, Sacrifice Mountain Valley: Search your library for a Mountain or Forest card and put it onto the battlefield. Then shuffle your library. - this.addAbility(new FetchLandActivatedAbility(false, EnumSet.of(SubType.MOUNTAIN, SubType.FOREST))); + this.addAbility(new FetchLandActivatedAbility(false, SubType.MOUNTAIN, SubType.FOREST)); } diff --git a/Mage.Sets/src/mage/cards/m/MuckDrubb.java b/Mage.Sets/src/mage/cards/m/MuckDrubb.java index 50f7e3fb73ad..adecc34727c7 100644 --- a/Mage.Sets/src/mage/cards/m/MuckDrubb.java +++ b/Mage.Sets/src/mage/cards/m/MuckDrubb.java @@ -18,7 +18,7 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.ObjectPlayer; import mage.filter.predicate.ObjectPlayerPredicate; -import mage.filter.predicate.other.TargetsPermanentPredicate; +import mage.filter.predicate.mageobject.TargetsPermanentPredicate; import mage.game.Game; import mage.game.stack.Spell; import mage.target.Target; diff --git a/Mage.Sets/src/mage/cards/m/MudbrawlerCohort.java b/Mage.Sets/src/mage/cards/m/MudbrawlerCohort.java index 4434a2939d2d..43bbc351f895 100644 --- a/Mage.Sets/src/mage/cards/m/MudbrawlerCohort.java +++ b/Mage.Sets/src/mage/cards/m/MudbrawlerCohort.java @@ -18,7 +18,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/m/Mudhole.java b/Mage.Sets/src/mage/cards/m/Mudhole.java index a26a26c4c77d..2c4eaa49c3a2 100644 --- a/Mage.Sets/src/mage/cards/m/Mudhole.java +++ b/Mage.Sets/src/mage/cards/m/Mudhole.java @@ -1,29 +1,26 @@ - package mage.cards.m; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.filter.FilterCard; -import mage.filter.common.FilterLandCard; +import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author cbt33 */ public final class Mudhole extends CardImpl { public Mudhole(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{R}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); // Target player exiles all land cards from their graveyard. this.getSpellAbility().addTarget(new TargetPlayer()); @@ -42,27 +39,24 @@ public Mudhole copy() { class MudholeEffect extends OneShotEffect { - private static final FilterCard filter = new FilterLandCard(); - - public MudholeEffect() { + MudholeEffect() { super(Outcome.Exile); staticText = "Target player exiles all land cards from their graveyard"; } - public MudholeEffect(final MudholeEffect effect) { + private MudholeEffect(final MudholeEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { - Player targetPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source)); - if (targetPlayer != null) { - for (Card card : targetPlayer.getGraveyard().getCards(filter, game)) { - card.moveToExile(null, "", source, game); - } - return true; + Player player = game.getPlayer(source.getFirstTarget()); + if (player == null) { + return false; } - return false; + return player.moveCards(player.getGraveyard().getCards( + StaticFilters.FILTER_CARD_LAND, game + ), Zone.EXILED, source, game); } @Override diff --git a/Mage.Sets/src/mage/cards/m/MultipleChoice.java b/Mage.Sets/src/mage/cards/m/MultipleChoice.java new file mode 100644 index 000000000000..467e4f6bb238 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MultipleChoice.java @@ -0,0 +1,97 @@ +package mage.cards.m; + +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.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.PrismariToken; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.TargetPlayer; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MultipleChoice extends CardImpl { + + public MultipleChoice(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{U}"); + + // If X is 1, scry 1, then draw a card. + // If X is 2, you may choose a player. They return a creature they control to its owner's hand. + // If X is 3, create a 4/4 blue and red Elemental creature token. + // If X is 4 or more, do all of the above. + this.getSpellAbility().addEffect(new MultipleChoiceEffect()); + } + + private MultipleChoice(final MultipleChoice card) { + super(card); + } + + @Override + public MultipleChoice copy() { + return new MultipleChoice(this); + } +} + +class MultipleChoiceEffect extends OneShotEffect { + + MultipleChoiceEffect() { + super(Outcome.Benefit); + staticText = "If X is 1, scry 1, then draw a card.
" + + "If X is 2, you may choose a player. They return a creature they control to its owner's hand.
" + + "If X is 3, create a 4/4 blue and red Elemental creature token.
" + + "If X is 4 or more, do all of the above."; + } + + private MultipleChoiceEffect(final MultipleChoiceEffect effect) { + super(effect); + } + + @Override + public MultipleChoiceEffect copy() { + return new MultipleChoiceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + int xValue = source.getManaCostsToPay().getX(); + if (xValue == 1 || xValue >= 4) { + controller.scry(1, source, game); + controller.drawCards(1, source, game); + } + if (xValue == 3 || xValue >= 4) { + new PrismariToken().putOntoBattlefield(1, game, source, source.getControllerId()); + } + if (xValue != 2 && xValue < 4) { + return true; + } + TargetPlayer targetPlayer = new TargetPlayer(0, 1, true); + controller.choose(Outcome.Detriment, targetPlayer, source.getSourceId(), game); + Player player = game.getPlayer(targetPlayer.getFirstTarget()); + if (player == null || game.getBattlefield().count( + StaticFilters.FILTER_CONTROLLED_CREATURE, + source.getSourceId(), player.getId(), game + ) <= 0) { + return true; + } + TargetPermanent targetPermanent = new TargetControlledCreaturePermanent(); + targetPermanent.setNotTarget(true); + player.choose(Outcome.ReturnToHand, targetPermanent, source.getSourceId(), game); + Permanent permanent = game.getPermanent(targetPermanent.getFirstTarget()); + return permanent == null || player.moveCards(permanent, Zone.HAND, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MummyParamount.java b/Mage.Sets/src/mage/cards/m/MummyParamount.java index 4800eed61266..c061c996ab94 100644 --- a/Mage.Sets/src/mage/cards/m/MummyParamount.java +++ b/Mage.Sets/src/mage/cards/m/MummyParamount.java @@ -11,7 +11,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/m/MuragandaPetroglyphs.java b/Mage.Sets/src/mage/cards/m/MuragandaPetroglyphs.java index 49ddbfb73f03..230c09afaa0c 100644 --- a/Mage.Sets/src/mage/cards/m/MuragandaPetroglyphs.java +++ b/Mage.Sets/src/mage/cards/m/MuragandaPetroglyphs.java @@ -1,23 +1,14 @@ package mage.cards.m; -import mage.MageObject; -import mage.abilities.Abilities; -import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.BoostAllEffect; -import mage.abilities.keyword.special.JohanVigilanceAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicate; -import mage.game.Game; +import mage.filter.predicate.mageobject.NoAbilityPredicate; -import java.util.Objects; import java.util.UUID; /** @@ -26,19 +17,20 @@ public final class MuragandaPetroglyphs extends CardImpl { private static final FilterCreaturePermanent filterNoAbilities - = new FilterCreaturePermanent("Creatures with no ability"); + = new FilterCreaturePermanent("creatures with no abilities"); static { - filterNoAbilities.add(new NoAbilityPredicate()); + filterNoAbilities.add(NoAbilityPredicate.instance); } public MuragandaPetroglyphs(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}"); - this.expansionSetCode = "FUT"; // Creatures with no abilities get +2/+2. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostAllEffect( - 2, 2, Duration.WhileOnBattlefield, filterNoAbilities, false))); + this.addAbility(new SimpleStaticAbility(new BoostAllEffect( + 2, 2, Duration.WhileOnBattlefield, + filterNoAbilities, false + ))); } private MuragandaPetroglyphs(final MuragandaPetroglyphs card) { @@ -50,56 +42,3 @@ public MuragandaPetroglyphs copy() { return new MuragandaPetroglyphs(this); } } - -class NoAbilityPredicate implements Predicate { - - // Muraganda Petroglyphs gives a bonus only to creatures that have no rules text at all. This includes true vanilla - // creatures (such as Grizzly Bears), face-down creatures, many tokens, and creatures that have lost their abilities - // (due to Ovinize, for example). Any ability of any kind, whether or not the ability functions in the on the - // battlefield zone, including things like “Cycling {2}” means the creature doesn’t get the bonus. - // (2007-05-01) - - @Override - public boolean apply(MageObject input, Game game) { - boolean isFaceDown = false; - Abilities abilities; - if (input instanceof Card) { - abilities = ((Card) input).getAbilities(game); - isFaceDown = ((Card) input).isFaceDown(game); - } else { - abilities = input.getAbilities(); - } - if (isFaceDown) { - // Some Auras and Equipment grant abilities to creatures, meaning the affected creature would no longer - // get the +2/+2 bonus. For example, Flight grants flying to the enchanted creature. Other Auras and - // Equipment do not, meaning the affected creature would continue to get the +2/+2 bonus. For example, - // Dehydration states something now true about the enchanted creature, but doesn’t give it any abilities. - // Auras and Equipment that grant abilities will use the words “gains” or “has,” and they’ll list a keyword - // ability or an ability in quotation marks. - // (2007-05-01) - - for (Ability ability : abilities) { - if (ability.getWorksFaceDown()) { - // inner face down abilities like turn up and becomes creature - continue; - } - if (!Objects.equals(ability.getClass(), SpellAbility.class) && !ability.getClass().equals(JohanVigilanceAbility.class)) { - return false; - } - } - return true; - } - - for (Ability ability : abilities) { - if (!Objects.equals(ability.getClass(), SpellAbility.class) && !ability.getClass().equals(JohanVigilanceAbility.class)) { - return false; - } - } - return true; - } - - @Override - public String toString() { - return "with no abilities"; - } -} diff --git a/Mage.Sets/src/mage/cards/m/MurderOfCrows.java b/Mage.Sets/src/mage/cards/m/MurderOfCrows.java index 74b7086d4cad..0d630d292bc9 100644 --- a/Mage.Sets/src/mage/cards/m/MurderOfCrows.java +++ b/Mage.Sets/src/mage/cards/m/MurderOfCrows.java @@ -62,7 +62,7 @@ public MurderOfCrowsEffect copy() { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player != null && player.chooseUse(Outcome.DrawCard, "Do you wish to draw a card? If you do, discard a card.", source, game)) { + if (player != null && player.chooseUse(Outcome.DrawCard, "Draw a card? If you do, discard a card.", source, game)) { if (player.drawCards(1, source, game) > 0) { player.discard(1, false, false, source, game); } diff --git a/Mage.Sets/src/mage/cards/m/MuseVortex.java b/Mage.Sets/src/mage/cards/m/MuseVortex.java new file mode 100644 index 000000000000..4bfc8c5561d4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MuseVortex.java @@ -0,0 +1,96 @@ +package mage.cards.m; + +import mage.ApprovingObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.common.FilterInstantOrSorceryCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInExile; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MuseVortex extends CardImpl { + + public MuseVortex(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{U}{U}"); + + // Exile the top X cards of your library. You may cast an instant or sorcery spell with mana value X or less from among them without paying its mana cost. Then put the exiled instant and sorcery cards that weren't cast this way into your hand and the rest on the bottom of your library in a random order. + this.getSpellAbility().addEffect(new MuseVortexEffect()); + } + + private MuseVortex(final MuseVortex card) { + super(card); + } + + @Override + public MuseVortex copy() { + return new MuseVortex(this); + } +} + +class MuseVortexEffect extends OneShotEffect { + + MuseVortexEffect() { + super(Outcome.Benefit); + staticText = "exile the top X cards of your library. You may cast an instant or sorcery spell " + + "with mana value X or less from among them without paying its mana cost. " + + "Then put the exiled instant and sorcery cards that weren't cast this way into your hand " + + "and the rest on the bottom of your library in a random order"; + } + + private MuseVortexEffect(final MuseVortexEffect effect) { + super(effect); + } + + @Override + public MuseVortexEffect copy() { + return new MuseVortexEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + int xValue = source.getManaCostsToPay().getX(); + Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, xValue)); + player.moveCards(cards, Zone.EXILED, source, game); + cards.retainZone(Zone.EXILED, game); + // TODO: this needs to be able to cast spells that aren't instant or sorcery cards (adventures/MDFCs) + FilterCard filter = new FilterInstantOrSorceryCard("an instant or sorcery card with mana value " + xValue + " or less"); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); + TargetCard target = new TargetCardInExile(0, 1, filter, null); + target.setNotTarget(true); + player.choose(outcome, cards, target, game); + Card card = cards.get(target.getFirstTarget(), game); + if (card != null) { + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); + player.cast( + player.chooseAbilityForCast(card, game, true), + game, true, new ApprovingObject(source, game) + ); + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); + } + cards.retainZone(Zone.EXILED, game); + player.moveCards(new CardsImpl(cards.getCards( + StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, game + )), Zone.HAND, source, game); + cards.retainZone(Zone.EXILED, game); + player.putCardsOnBottomOfLibrary(card, game, source, false); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/m/Mutilate.java b/Mage.Sets/src/mage/cards/m/Mutilate.java index 03e33ae231ae..c13bcd64cbf3 100644 --- a/Mage.Sets/src/mage/cards/m/Mutilate.java +++ b/Mage.Sets/src/mage/cards/m/Mutilate.java @@ -1,9 +1,6 @@ - package mage.cards.m; -import java.util.UUID; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -14,9 +11,10 @@ import mage.filter.StaticFilters; import mage.filter.common.FilterLandPermanent; +import java.util.UUID; + /** - * * @author North */ public final class Mutilate extends CardImpl { @@ -31,14 +29,15 @@ public final class Mutilate extends CardImpl { } public Mutilate(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}{B}"); // All creatures get -1/-1 until end of turn for each Swamp you control. PermanentsOnBattlefieldCount count = new PermanentsOnBattlefieldCount(filter, -1); - ContinuousEffect effect = new BoostAllEffect(count, count, Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURES, false, null, true); - effect.overrideRuleText(ruleText); - this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addEffect( + new BoostAllEffect(count, count, Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURES, false, null, true) + .setText(ruleText) + ); } private Mutilate(final Mutilate card) { diff --git a/Mage.Sets/src/mage/cards/m/MuxusGoblinGrandee.java b/Mage.Sets/src/mage/cards/m/MuxusGoblinGrandee.java index ca54830561e3..c46314ad760d 100644 --- a/Mage.Sets/src/mage/cards/m/MuxusGoblinGrandee.java +++ b/Mage.Sets/src/mage/cards/m/MuxusGoblinGrandee.java @@ -15,7 +15,7 @@ import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.players.Player; @@ -68,7 +68,7 @@ class MuxusGoblinGrandeeEffect extends OneShotEffect { MuxusGoblinGrandeeEffect() { super(Outcome.Benefit); staticText = "reveal the top six cards of your library. " + - "Put all Goblin creature cards with converted mana cost 5 or less " + + "Put all Goblin creature cards with mana value 5 or less " + "from among them onto the battlefield and the rest on the bottom of your library in a random order."; } @@ -97,7 +97,7 @@ public boolean apply(Game game, Ability source) { .forEach(card -> { if (card.isCreature() && card.hasSubtype(SubType.GOBLIN, game) - && card.getConvertedManaCost() <= 5) { + && card.getManaValue() <= 5) { toBattlfield.add(card); } else { toBottom.add(card); diff --git a/Mage.Sets/src/mage/cards/m/MuzzioVisionaryArchitect.java b/Mage.Sets/src/mage/cards/m/MuzzioVisionaryArchitect.java index f429ece06ab6..20944c90d91c 100644 --- a/Mage.Sets/src/mage/cards/m/MuzzioVisionaryArchitect.java +++ b/Mage.Sets/src/mage/cards/m/MuzzioVisionaryArchitect.java @@ -56,7 +56,7 @@ class MuzzioVisionaryArchitectEffect extends OneShotEffect { public MuzzioVisionaryArchitectEffect() { super(Outcome.Benefit); - this.staticText = "Look at the top X cards of your library, where X is the highest converted mana cost among artifacts you control. You may reveal an artifact card from among them and put it onto the battlefield. Put the rest on the bottom of your library in any order"; + this.staticText = "look at the top X cards of your library, where X is the highest mana value among artifacts you control. You may put an artifact card from among them onto the battlefield. Put the rest on the bottom of your library in any order"; } public MuzzioVisionaryArchitectEffect(final MuzzioVisionaryArchitectEffect effect) { @@ -79,7 +79,7 @@ public boolean apply(Game game, Ability source) { List controlledArtifacts = game.getBattlefield().getAllActivePermanents(new FilterArtifactPermanent(), controller.getId(), game); for (Permanent permanent : controlledArtifacts) { if (permanent.getSpellAbility() != null) { - int cmc = permanent.getSpellAbility().getManaCosts().convertedManaCost(); + int cmc = permanent.getSpellAbility().getManaCosts().manaValue(); if (cmc > highCMC) { highCMC = cmc; } diff --git a/Mage.Sets/src/mage/cards/m/MwonvuliBeastTracker.java b/Mage.Sets/src/mage/cards/m/MwonvuliBeastTracker.java index 40fdbe0d19e8..1d799a1e8bf3 100644 --- a/Mage.Sets/src/mage/cards/m/MwonvuliBeastTracker.java +++ b/Mage.Sets/src/mage/cards/m/MwonvuliBeastTracker.java @@ -24,7 +24,7 @@ */ public final class MwonvuliBeastTracker extends CardImpl { - private static final FilterCard filter = new FilterCard("creature card with deathtouch, hexproof, reach, or trample in your library"); + private static final FilterCard filter = new FilterCard("creature card with deathtouch, hexproof, reach, or trample"); static { filter.add(CardType.CREATURE.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/m/Mycologist.java b/Mage.Sets/src/mage/cards/m/Mycologist.java index be55ad6c9d6d..d44f102733ea 100644 --- a/Mage.Sets/src/mage/cards/m/Mycologist.java +++ b/Mage.Sets/src/mage/cards/m/Mycologist.java @@ -1,4 +1,3 @@ - package mage.cards.m; import java.util.UUID; @@ -42,8 +41,10 @@ public Mycologist(UUID ownerId, CardSetInfo setInfo) { // At the beginning of your upkeep, put a spore counter on Mycologist. this.addAbility(new BeginningOfUpkeepTriggeredAbility(new AddCountersSourceEffect(CounterType.SPORE.createInstance()), TargetController.YOU, false)); + // Remove three spore counters from Mycologist: Create a 1/1 green Saproling creature token. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new SaprolingToken()), new RemoveCountersSourceCost(CounterType.SPORE.createInstance(3)))); + // Sacrifice a Saproling: You gain 2 life. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainLifeEffect(2), new SacrificeTargetCost(new TargetControlledPermanent(1, 1, filter, false)))); diff --git a/Mage.Sets/src/mage/cards/m/MyojinOfCleansingFire.java b/Mage.Sets/src/mage/cards/m/MyojinOfCleansingFire.java index fda52ae51568..89040461c49e 100644 --- a/Mage.Sets/src/mage/cards/m/MyojinOfCleansingFire.java +++ b/Mage.Sets/src/mage/cards/m/MyojinOfCleansingFire.java @@ -24,7 +24,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.watchers.common.CastFromHandWatcher; /** diff --git a/Mage.Sets/src/mage/cards/m/MyrIncubator.java b/Mage.Sets/src/mage/cards/m/MyrIncubator.java index 4ac076ff1a3f..1e6f2b81ef44 100644 --- a/Mage.Sets/src/mage/cards/m/MyrIncubator.java +++ b/Mage.Sets/src/mage/cards/m/MyrIncubator.java @@ -60,7 +60,7 @@ class MyrIncubatorEffect extends SearchEffect { MyrIncubatorEffect() { super(new TargetCardInLibrary(0, Integer.MAX_VALUE, filter), Outcome.Neutral); - staticText = "Search your library for any number of artifact cards, exile them, then put that many 1/1 colorless Myr artifact creature tokens onto the battlefield. Then shuffle your library"; + staticText = "Search your library for any number of artifact cards, exile them, then create that many 1/1 colorless Myr artifact creature tokens. Then shuffle"; } MyrIncubatorEffect(final MyrIncubatorEffect effect) { diff --git a/Mage.Sets/src/mage/cards/m/MyriadLandscape.java b/Mage.Sets/src/mage/cards/m/MyriadLandscape.java index 343c0e381f00..77200e67efde 100644 --- a/Mage.Sets/src/mage/cards/m/MyriadLandscape.java +++ b/Mage.Sets/src/mage/cards/m/MyriadLandscape.java @@ -41,7 +41,7 @@ public MyriadLandscape(UUID ownerId, CardSetInfo setInfo) { // {2}, {tap}, Sacrifice Myriad Landscape: Search your library for up to two basic land cards that share a land type, put them onto the battlefield tapped, then shuffle your library. Effect effect = new SearchLibraryPutInPlayEffect(new TargetCardInLibrarySharingLandType(0, 2), true); - effect.setText("Search your library for up to two basic land cards that share a land type, put them onto the battlefield tapped, then shuffle your library"); + effect.setText("Search your library for up to two basic land cards that share a land type, put them onto the battlefield tapped, then shuffle"); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new GenericManaCost(2)); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); diff --git a/Mage.Sets/src/mage/cards/m/MysticForge.java b/Mage.Sets/src/mage/cards/m/MysticForge.java index 2cfee980edd8..1975c5d34b56 100644 --- a/Mage.Sets/src/mage/cards/m/MysticForge.java +++ b/Mage.Sets/src/mage/cards/m/MysticForge.java @@ -43,7 +43,7 @@ public MysticForge(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect())); // You may cast artifact spells and colorless spells from the top of your library. - this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter))); + this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false))); // {T}, Pay 1 life: Exile the top card of your library. Ability ability = new SimpleActivatedAbility(new MysticForgeExileEffect(), new TapSourceCost()); diff --git a/Mage.Sets/src/mage/cards/m/MysticGenesis.java b/Mage.Sets/src/mage/cards/m/MysticGenesis.java index e16ff17fdbca..4faf3febd224 100644 --- a/Mage.Sets/src/mage/cards/m/MysticGenesis.java +++ b/Mage.Sets/src/mage/cards/m/MysticGenesis.java @@ -43,7 +43,7 @@ class MysticGenesisEffect extends OneShotEffect { public MysticGenesisEffect() { super(Outcome.Detriment); - staticText = "Counter target spell. Create an X/X green Ooze creature token, where X is that spell's converted mana cost"; + staticText = "Counter target spell. Create an X/X green Ooze creature token, where X is that spell's mana value"; } public MysticGenesisEffect(final MysticGenesisEffect effect) { @@ -60,7 +60,7 @@ public boolean apply(Game game, Ability source) { StackObject stackObject = game.getStack().getStackObject(targetPointer.getFirst(game, source)); if (stackObject != null) { game.getStack().counter(source.getFirstTarget(), source, game); - return new CreateTokenEffect(new MysticGenesisOozeToken(stackObject.getConvertedManaCost())).apply(game, source); + return new CreateTokenEffect(new MysticGenesisOozeToken(stackObject.getManaValue())).apply(game, source); } return false; } diff --git a/Mage.Sets/src/mage/cards/m/MysticReflection.java b/Mage.Sets/src/mage/cards/m/MysticReflection.java index 4a3a7254c23e..e127d63eb166 100644 --- a/Mage.Sets/src/mage/cards/m/MysticReflection.java +++ b/Mage.Sets/src/mage/cards/m/MysticReflection.java @@ -1,11 +1,12 @@ package mage.cards.m; -import mage.MageItem; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.keyword.ForetellAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -15,15 +16,16 @@ import mage.game.Game; import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeGroupEvent; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; -import mage.watchers.Watcher; - -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; import java.util.UUID; +import mage.MageItem; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.cards.Card; +import mage.game.events.ZoneChangeGroupEvent; +import mage.target.targetpointer.FixedTarget; +import mage.watchers.Watcher; /** * @author TheElk801 @@ -62,8 +64,8 @@ class MysticReflectionEffect extends OneShotEffect { MysticReflectionEffect() { super(Outcome.Benefit); - staticText = "Choose target nonlegendary creature. The next time one or more creatures or planeswalkers " + - "enter the battlefield this turn, they enter as copies of the chosen creature."; + staticText = "Choose target nonlegendary creature. The next time one or more creatures or planeswalkers " + + "enter the battlefield this turn, they enter as copies of the chosen creature."; } private MysticReflectionEffect(final MysticReflectionEffect effect) { @@ -77,31 +79,38 @@ public MysticReflectionEffect copy() { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getFirstTarget()); + Permanent targetedPermanent = game.getPermanent(source.getFirstTarget()); + // store the permanent that was targeted + game.getState().setValue("MysticReflection" + source.getSourceId().toString(), targetedPermanent); MysticReflectionWatcher watcher = game.getState().getWatcher(MysticReflectionWatcher.class); - if (permanent == null || watcher == null) { - return false; - } - game.addEffect(new MysticReflectionCopyEffect(permanent, watcher.getEnteredThisTurn()), source); + // The sourceId must be sent to the next class, otherwise it gets lost. Thus, the identifier parameter. + // Otherwise, the state of the permanent that was targeted gets lost if it leaves the battlefield, etc. + // The zone is ALL because if the targeted permanent leaves the battlefield, the replacement effect still applies. + SimpleStaticAbility staticAbilityOnCard = new SimpleStaticAbility(Zone.ALL, new MysticReflectionReplacementEffect(watcher.getEnteredThisTurn(), source.getSourceId().toString())); + MysticReflectionGainAbilityEffect gainAbilityEffect = new MysticReflectionGainAbilityEffect(staticAbilityOnCard); + gainAbilityEffect.setTargetPointer(new FixedTarget(targetedPermanent.getMainCard().getId())); + game.addEffect(gainAbilityEffect, source); return true; } } -class MysticReflectionCopyEffect extends ReplacementEffectImpl { +class MysticReflectionReplacementEffect extends ReplacementEffectImpl { - private final Permanent permanent; - private final int enteredThisTurn; + final private int enteredThisTurn; + final private String identifier; - MysticReflectionCopyEffect(Permanent permanent, int enteredThisTurn) { - super(Duration.Custom, Outcome.Copy, false); - this.permanent = permanent; + public MysticReflectionReplacementEffect(int enteredThisTurn, String identifier) { + super(Duration.EndOfTurn, Outcome.Copy, false); this.enteredThisTurn = enteredThisTurn; + this.identifier = identifier; + staticText = "The next time one or more creatures or planeswalkers " + + "enter the battlefield this turn, they enter as copies of {this}"; } - private MysticReflectionCopyEffect(MysticReflectionCopyEffect effect) { + public MysticReflectionReplacementEffect(MysticReflectionReplacementEffect effect) { super(effect); - this.permanent = effect.permanent; this.enteredThisTurn = effect.enteredThisTurn; + this.identifier = effect.identifier; } @Override @@ -111,34 +120,33 @@ public boolean checksEventType(GameEvent event, Game game) { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (permanent == null) { - discard(); - return false; - } MysticReflectionWatcher watcher = game.getState().getWatcher(MysticReflectionWatcher.class); - if (watcher != null && watcher.getEnteredThisTurn() > this.enteredThisTurn) { - discard(); - return false; + if (watcher != null) { + if (watcher.getEnteredThisTurn() > this.enteredThisTurn) { + discard(); + return false; + } } - Permanent perm = ((EntersTheBattlefieldEvent) event).getTarget(); - return perm != null - && (perm.isCreature() || perm.isPlaneswalker()) - && perm.isControlledBy(source.getControllerId()); + Permanent permanentEnteringTheBattlefield = ((EntersTheBattlefieldEvent) event).getTarget(); + Permanent targetedPermanent = (Permanent) game.getState().getValue("MysticReflection" + identifier); + return permanentEnteringTheBattlefield != null + && targetedPermanent != null + && permanentEnteringTheBattlefield.isCreature(); } @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - if (this.permanent != null) { - game.copyPermanent(this.permanent, event.getTargetId(), source, null); + Permanent targetedPermanent = (Permanent) game.getState().getValue("MysticReflection" + identifier); + if (targetedPermanent != null) { + game.copyPermanent(targetedPermanent, event.getTargetId(), source, null); } return false; } @Override - public MysticReflectionCopyEffect copy() { - return new MysticReflectionCopyEffect(this); + public MysticReflectionReplacementEffect copy() { + return new MysticReflectionReplacementEffect(this); } - } class MysticReflectionWatcher extends Watcher { @@ -181,3 +189,39 @@ public int getEnteredThisTurn() { return enteredThisTurn; } } + +class MysticReflectionGainAbilityEffect extends ContinuousEffectImpl { + + final private Ability ability; + + public MysticReflectionGainAbilityEffect(Ability ability) { + super(Duration.EndOfTurn, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + this.ability = ability; + } + + public MysticReflectionGainAbilityEffect(final MysticReflectionGainAbilityEffect effect) { + super(effect); + this.ability = effect.ability; + } + + @Override + public MysticReflectionGainAbilityEffect copy() { + return new MysticReflectionGainAbilityEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent targetedPermanent = (Permanent) game.getState().getValue("MysticReflection" + source.getSourceId().toString()); + // The ability must be put on the card. If it leaves the battlefield, the replacement effect must still fire and copy its "permanent" state. + if (targetedPermanent == null) { + return false; + } + Card card = targetedPermanent.getMainCard(); + if (card != null + && !card.getAbilities().contains(ability)) { + game.getState().addOtherAbility(card, ability); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MysticRemora.java b/Mage.Sets/src/mage/cards/m/MysticRemora.java index 36faba2a75d1..8971c0a5c42f 100644 --- a/Mage.Sets/src/mage/cards/m/MysticRemora.java +++ b/Mage.Sets/src/mage/cards/m/MysticRemora.java @@ -118,8 +118,7 @@ public boolean apply(Game game, Ability source) { if (controller != null && opponent != null && sourceObject != null) { if (controller.chooseUse(Outcome.DrawCard, "Draw a card (" + sourceObject.getLogName() + ')', source, game)) { Cost cost = ManaUtil.createManaCost(4, false); - String message = "Would you like to pay {4} to prevent the opponent to draw a card?"; - if (opponent.chooseUse(Outcome.Benefit, message, source, game) + if (opponent.chooseUse(Outcome.Benefit, "Pay {4}?", source, game) && cost.pay(source, game, source, opponent.getId(), false, null)) { return true; } diff --git a/Mage.Sets/src/mage/cards/m/MysticSanctuary.java b/Mage.Sets/src/mage/cards/m/MysticSanctuary.java index b01e015bd51c..1a161a6a49ab 100644 --- a/Mage.Sets/src/mage/cards/m/MysticSanctuary.java +++ b/Mage.Sets/src/mage/cards/m/MysticSanctuary.java @@ -18,7 +18,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterInstantOrSorceryCard; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCardInYourGraveyard; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/m/MythUnbound.java b/Mage.Sets/src/mage/cards/m/MythUnbound.java index ad93e69a47ea..14404f3f026a 100644 --- a/Mage.Sets/src/mage/cards/m/MythUnbound.java +++ b/Mage.Sets/src/mage/cards/m/MythUnbound.java @@ -11,7 +11,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.CommanderPredicate; +import mage.filter.predicate.mageobject.CommanderPredicate; import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; diff --git a/Mage.Sets/src/mage/cards/m/MythosOfBrokkos.java b/Mage.Sets/src/mage/cards/m/MythosOfBrokkos.java index 9413d293cca2..21a313547c58 100644 --- a/Mage.Sets/src/mage/cards/m/MythosOfBrokkos.java +++ b/Mage.Sets/src/mage/cards/m/MythosOfBrokkos.java @@ -55,8 +55,8 @@ class MythosOfBrokkosEffect extends OneShotEffect { MythosOfBrokkosEffect() { super(Outcome.Benefit); - staticText = "If {U}{B} was spent to cast Mythos of Brokkos, search your library for a card, " + - "put that card into your graveyard, then shuffle your library.
" + + staticText = "If {U}{B} was spent to cast this spell, search your library for a card, " + + "put that card into your graveyard, then shuffle.
" + "Return up to two permanent cards from your graveyard to your hand."; } diff --git a/Mage.Sets/src/mage/cards/n/N1Starfighter.java b/Mage.Sets/src/mage/cards/n/N1Starfighter.java index de31caf071a1..c0765cfb9524 100644 --- a/Mage.Sets/src/mage/cards/n/N1Starfighter.java +++ b/Mage.Sets/src/mage/cards/n/N1Starfighter.java @@ -13,7 +13,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledCreaturePermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/n/NahiriTheHarbinger.java b/Mage.Sets/src/mage/cards/n/NahiriTheHarbinger.java index ad5c432fb100..4e7ed4e0029f 100644 --- a/Mage.Sets/src/mage/cards/n/NahiriTheHarbinger.java +++ b/Mage.Sets/src/mage/cards/n/NahiriTheHarbinger.java @@ -93,7 +93,7 @@ class NahiriTheHarbingerEffect extends SearchEffect { NahiriTheHarbingerEffect() { super(new TargetCardInLibrary(0, 1, filterCard), Outcome.PutCardInPlay); - this.staticText = "Search your library for an artifact or creature card, put it onto the battlefield, then shuffle your library. It gains haste. Return it to your hand at the beginning of the next end step"; + this.staticText = "Search your library for an artifact or creature card, put it onto the battlefield, then shuffle. It gains haste. Return it to your hand at the beginning of the next end step"; } NahiriTheHarbingerEffect(final NahiriTheHarbingerEffect effect) { diff --git a/Mage.Sets/src/mage/cards/n/NahirisWrath.java b/Mage.Sets/src/mage/cards/n/NahirisWrath.java index a464b4f69755..876ffb881b07 100644 --- a/Mage.Sets/src/mage/cards/n/NahirisWrath.java +++ b/Mage.Sets/src/mage/cards/n/NahirisWrath.java @@ -35,7 +35,7 @@ public NahirisWrath(UUID ownerId, CardSetInfo setInfo) { // Nahiri's Wrath deals damage equal to the total converted mana cost of the discarded cards to each of up to X target creatures and/or planeswalkers. Effect effect = new DamageTargetEffect(DiscardCostCardConvertedMana.instance); - effect.setText("{this} deals damage equal to the total converted mana cost of the discarded cards to each of up to X target creatures and/or planeswalkers"); + effect.setText("{this} deals damage equal to the total mana value of the discarded cards to each of up to X target creatures and/or planeswalkers"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().setTargetAdjuster(NahirisWrathAdjuster.instance); } diff --git a/Mage.Sets/src/mage/cards/n/NantukoShaman.java b/Mage.Sets/src/mage/cards/n/NantukoShaman.java index 4952cf9a2d30..13317b0a2d03 100644 --- a/Mage.Sets/src/mage/cards/n/NantukoShaman.java +++ b/Mage.Sets/src/mage/cards/n/NantukoShaman.java @@ -41,7 +41,7 @@ public NantukoShaman(UUID ownerId, CardSetInfo setInfo) { Ability ability = new ConditionalInterveningIfTriggeredAbility( new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1)), new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.EQUAL_TO, 0), - "When {this} enters the battlefield, if you control no tapped lands, draw a card"); + "When {this} enters the battlefield, if you control no tapped lands, draw a card."); this.addAbility(ability); // Suspend 1-{2}{G}{G} diff --git a/Mage.Sets/src/mage/cards/n/NantukoShrine.java b/Mage.Sets/src/mage/cards/n/NantukoShrine.java index 44cba3152eed..799d16d83712 100644 --- a/Mage.Sets/src/mage/cards/n/NantukoShrine.java +++ b/Mage.Sets/src/mage/cards/n/NantukoShrine.java @@ -45,7 +45,7 @@ class NantukoShrineEffect extends OneShotEffect { public NantukoShrineEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "that player puts X 1/1 green Squirrel creature tokens onto the battlefield, where X is the number of cards in all graveyards with the same name as that spell"; + this.staticText = "that player creates X 1/1 green Squirrel creature tokens, where X is the number of cards in all graveyards with the same name as that spell"; } public NantukoShrineEffect(final NantukoShrineEffect effect) { diff --git a/Mage.Sets/src/mage/cards/n/NarsetOfTheAncientWay.java b/Mage.Sets/src/mage/cards/n/NarsetOfTheAncientWay.java index ab7d2d7faf5f..00c0cd9b3704 100644 --- a/Mage.Sets/src/mage/cards/n/NarsetOfTheAncientWay.java +++ b/Mage.Sets/src/mage/cards/n/NarsetOfTheAncientWay.java @@ -116,7 +116,7 @@ class NarsetOfTheAncientWayDrawEffect extends OneShotEffect { NarsetOfTheAncientWayDrawEffect() { super(Outcome.Benefit); staticText = "Draw a card, then you may discard a card. When you discard a nonland card this way, " + - "{this} deals damage equal to that card's converted mana cost to target creature or planeswalker."; + "{this} deals damage equal to that card's mana value to target creature or planeswalker."; } private NarsetOfTheAncientWayDrawEffect(final NarsetOfTheAncientWayDrawEffect effect) { @@ -143,8 +143,8 @@ public boolean apply(Game game, Ability source) { return false; } ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( - new DamageTargetEffect(card.getConvertedManaCost()), false, "{this} deals damage " + - "to target creature or planeswalker equal to the discarded card's converted mana cost" + new DamageTargetEffect(card.getManaValue()), false, "{this} deals damage " + + "to target creature or planeswalker equal to the discarded card's mana value" ); ability.addTarget(new TargetCreatureOrPlaneswalker()); game.fireReflexiveTriggeredAbility(ability, source); diff --git a/Mage.Sets/src/mage/cards/n/NarsetTranscendent.java b/Mage.Sets/src/mage/cards/n/NarsetTranscendent.java index 3109ffae900c..2a1f55ae4d27 100644 --- a/Mage.Sets/src/mage/cards/n/NarsetTranscendent.java +++ b/Mage.Sets/src/mage/cards/n/NarsetTranscendent.java @@ -129,7 +129,7 @@ public boolean checkTrigger(GameEvent event, Game game) { Spell spell = game.getStack().getSpell(event.getTargetId()); if (spell != null && spell.getFromZone() == Zone.HAND) { if (spell.getCard() != null - && spell.getCard().isInstant() || spell.getCard().isSorcery()) { + && spell.getCard().isInstantOrSorcery()) { for (Effect effect : getEffects()) { effect.setTargetPointer(new FixedTarget(spell.getId())); } diff --git a/Mage.Sets/src/mage/cards/n/NaturalBalance.java b/Mage.Sets/src/mage/cards/n/NaturalBalance.java index a3fead804d29..93617912995e 100644 --- a/Mage.Sets/src/mage/cards/n/NaturalBalance.java +++ b/Mage.Sets/src/mage/cards/n/NaturalBalance.java @@ -47,7 +47,7 @@ class NaturalBalanceEffect extends OneShotEffect { public NaturalBalanceEffect() { super(Outcome.PutCardInPlay); - this.staticText = "Each player who controls six or more lands chooses five lands they control and sacrifices the rest. Each player who controls four or fewer lands may search their library for up to X basic land cards and put them onto the battlefield, where X is five minus the number of lands they control. Then each player who searched their library this way shuffles it."; + this.staticText = "Each player who controls six or more lands chooses five lands they control and sacrifices the rest. Each player who controls four or fewer lands may search their library for up to X basic land cards and put them onto the battlefield, where X is five minus the number of lands they control. Then each player who searched their library this way shuffles."; } public NaturalBalanceEffect(final NaturalBalanceEffect effect) { diff --git a/Mage.Sets/src/mage/cards/n/NaturalSelection.java b/Mage.Sets/src/mage/cards/n/NaturalSelection.java index 270c8d525ab6..a203f855cc2d 100644 --- a/Mage.Sets/src/mage/cards/n/NaturalSelection.java +++ b/Mage.Sets/src/mage/cards/n/NaturalSelection.java @@ -43,7 +43,7 @@ class NaturalSelectionEffect extends OneShotEffect { public NaturalSelectionEffect() { super(Outcome.DrawCard); - this.staticText = "look at the top three cards of target player's library, then put them back in any order. You may have that player shuffle their library."; + this.staticText = "look at the top three cards of target player's library, then put them back in any order. You may have that player shuffle"; } public NaturalSelectionEffect(final NaturalSelectionEffect effect) { @@ -65,7 +65,7 @@ public boolean apply(Game game, Ability source) { Cards cards = new CardsImpl(targetPlayer.getLibrary().getTopCards(game, 3)); controller.lookAtCards(source, null, cards, game); controller.putCardsOnTopOfLibrary(cards, game, source, true); - if (controller.chooseUse(Outcome.Neutral, "You may have that player shuffle their library", source, game)) { + if (controller.chooseUse(Outcome.Neutral, "You may have that player shuffle", source, game)) { targetPlayer.shuffleLibrary(source, game); } return true; diff --git a/Mage.Sets/src/mage/cards/n/NaturalState.java b/Mage.Sets/src/mage/cards/n/NaturalState.java index 3c2ebf3d206b..add706469ef7 100644 --- a/Mage.Sets/src/mage/cards/n/NaturalState.java +++ b/Mage.Sets/src/mage/cards/n/NaturalState.java @@ -8,7 +8,7 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.filter.common.FilterArtifactOrEnchantmentPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetPermanent; /** @@ -17,10 +17,10 @@ */ public final class NaturalState extends CardImpl { - private static final FilterArtifactOrEnchantmentPermanent filter = new FilterArtifactOrEnchantmentPermanent("artifact or enchantment with converted mana cost 3 or less"); + private static final FilterArtifactOrEnchantmentPermanent filter = new FilterArtifactOrEnchantmentPermanent("artifact or enchantment with mana value 3 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public NaturalState(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/n/NayaHushblade.java b/Mage.Sets/src/mage/cards/n/NayaHushblade.java index 60a10061b3a7..5a517da0ad89 100644 --- a/Mage.Sets/src/mage/cards/n/NayaHushblade.java +++ b/Mage.Sets/src/mage/cards/n/NayaHushblade.java @@ -18,7 +18,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.MulticoloredPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/n/NayaSoulbeast.java b/Mage.Sets/src/mage/cards/n/NayaSoulbeast.java index 7b94e665edb6..ff034ab47ab3 100644 --- a/Mage.Sets/src/mage/cards/n/NayaSoulbeast.java +++ b/Mage.Sets/src/mage/cards/n/NayaSoulbeast.java @@ -83,7 +83,7 @@ public boolean apply(Game game, Ability source) { if (player != null) { if (player.getLibrary().hasCards()) { Card card = player.getLibrary().getFromTop(game); - cmc += card.getConvertedManaCost(); + cmc += card.getManaValue(); player.revealCards(sourceObject.getName() + " (" + player.getName() + ')', new CardsImpl(card), game); } } @@ -103,7 +103,7 @@ class NayaSoulbeastReplacementEffect extends ReplacementEffectImpl { public NayaSoulbeastReplacementEffect() { super(Duration.OneUse, Outcome.BoostCreature); - staticText = "{this} enters the battlefield with X +1/+1 counters on it, where X is the total converted mana cost of all cards revealed this way"; + staticText = "{this} enters the battlefield with X +1/+1 counters on it, where X is the total mana value of all cards revealed this way"; } public NayaSoulbeastReplacementEffect(final NayaSoulbeastReplacementEffect effect) { diff --git a/Mage.Sets/src/mage/cards/n/NebulonBFrigate.java b/Mage.Sets/src/mage/cards/n/NebulonBFrigate.java index 7e1891fe429f..86ae280231d5 100644 --- a/Mage.Sets/src/mage/cards/n/NebulonBFrigate.java +++ b/Mage.Sets/src/mage/cards/n/NebulonBFrigate.java @@ -14,7 +14,7 @@ import mage.constants.Duration; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/n/NeckBreaker.java b/Mage.Sets/src/mage/cards/n/NeckBreaker.java index d80d94979b54..4f9b4f0b7fbc 100644 --- a/Mage.Sets/src/mage/cards/n/NeckBreaker.java +++ b/Mage.Sets/src/mage/cards/n/NeckBreaker.java @@ -1,37 +1,28 @@ - package mage.cards.n; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.common.TransformedCondition; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.TrampleAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.filter.common.FilterAttackingCreature; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author fireshoes */ public final class NeckBreaker extends CardImpl { public NeckBreaker(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.WEREWOLF); this.power = new MageInt(4); this.toughness = new MageInt(3); @@ -42,15 +33,18 @@ public NeckBreaker(UUID ownerId, CardSetInfo setInfo) { this.transformable = true; // Attacking creatures you control get +1/+0 and have trample. - SimpleStaticAbility boostAbility = new SimpleStaticAbility(Zone.BATTLEFIELD, - new ConditionalContinuousEffect(new BoostControlledEffect(1, 0, Duration.WhileOnBattlefield, new FilterAttackingCreature()), - new TransformedCondition(false), "Attacking creatures you control get +1/+0")); - boostAbility.addEffect(new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.WhileOnBattlefield, new FilterAttackingCreature())); - this.addAbility(boostAbility); + Ability ability = new SimpleStaticAbility(new BoostControlledEffect( + 1, 0, Duration.WhileOnBattlefield, + StaticFilters.FILTER_ATTACKING_CREATURES + )); + ability.addEffect(new GainAbilityControlledEffect( + TrampleAbility.getInstance(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_ATTACKING_CREATURES + ).setText("and have trample")); + this.addAbility(ability); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Neck Breaker. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private NeckBreaker(final NeckBreaker card) { diff --git a/Mage.Sets/src/mage/cards/n/NecroblossomSnarl.java b/Mage.Sets/src/mage/cards/n/NecroblossomSnarl.java new file mode 100644 index 000000000000..48ffb94e0939 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NecroblossomSnarl.java @@ -0,0 +1,56 @@ +package mage.cards.n; + +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.costs.common.RevealTargetFromHandCost; +import mage.abilities.effects.common.TapSourceUnlessPaysEffect; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.target.common.TargetCardInHand; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NecroblossomSnarl extends CardImpl { + + private static final FilterCard filter = new FilterCard("a Swamp or Forest card from your hand"); + + static { + filter.add(Predicates.or( + SubType.SWAMP.getPredicate(), + SubType.FOREST.getPredicate() + )); + } + + public NecroblossomSnarl(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // As Necroblossom Snarl enters the battlefield, you may reveal a Swamp or Forest card from your hand. If you don't, Necroblossom Snarl enters the battlefield tapped. + this.addAbility(new AsEntersBattlefieldAbility( + new TapSourceUnlessPaysEffect( + new RevealTargetFromHandCost(new TargetCardInHand(filter)) + ), "you may reveal a Swamp or Forest card from your hand. " + + "If you don't, {this} enters the battlefield tapped" + )); + + // {T}: Add {B} or {G}. + this.addAbility(new BlackManaAbility()); + this.addAbility(new GreenManaAbility()); + } + + private NecroblossomSnarl(final NecroblossomSnarl card) { + super(card); + } + + @Override + public NecroblossomSnarl copy() { + return new NecroblossomSnarl(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/NecromancersCovenant.java b/Mage.Sets/src/mage/cards/n/NecromancersCovenant.java index 621c43eafcae..d32c72d2a680 100644 --- a/Mage.Sets/src/mage/cards/n/NecromancersCovenant.java +++ b/Mage.Sets/src/mage/cards/n/NecromancersCovenant.java @@ -1,34 +1,36 @@ package mage.cards.n; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.abilities.keyword.LifelinkAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledPermanent; import mage.game.Game; import mage.game.permanent.token.ZombieToken; import mage.players.Player; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class NecromancersCovenant extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("Zombies you control"); - - static { - filter.add(SubType.ZOMBIE.getPredicate()); - } + private static final FilterPermanent filter + = new FilterControlledPermanent(SubType.ZOMBIE, "Zombies you control"); public NecromancersCovenant(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}{B}{B}"); @@ -39,7 +41,9 @@ public NecromancersCovenant(UUID ownerId, CardSetInfo setInfo) { this.addAbility(ability); // Zombies you control have lifelink. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(LifelinkAbility.getInstance(), Duration.WhileOnBattlefield, filter))); + this.addAbility(new SimpleStaticAbility(new GainAbilityAllEffect( + LifelinkAbility.getInstance(), Duration.WhileOnBattlefield, filter + ))); } private NecromancersCovenant(final NecromancersCovenant card) { @@ -54,32 +58,26 @@ public NecromancersCovenant copy() { class NecromancersConvenantEffect extends OneShotEffect { - public NecromancersConvenantEffect() { + NecromancersConvenantEffect() { super(Outcome.PutCreatureInPlay); - staticText = "exile all creature cards from target player's graveyard, then create a 2/2 black Zombie creature token for each card exiled this way"; + staticText = "exile all creature cards from target player's graveyard, " + + "then create a 2/2 black Zombie creature token for each card exiled this way"; } - public NecromancersConvenantEffect(NecromancersConvenantEffect effect) { + private NecromancersConvenantEffect(NecromancersConvenantEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); Player player = game.getPlayer(source.getFirstTarget()); - if (player == null) { + if (controller == null || player == null) { return false; } - int count = 0; - for (Card card : player.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURE, game)) { - if (card.moveToExile(source.getSourceId(), "Necromancer Covenant", source, game)) { - count += 1; - } - } - ZombieToken zombieToken = new ZombieToken(); - if (zombieToken.putOntoBattlefield(count, game, source, source.getControllerId())) { - return true; - } - return false; + Cards cards = new CardsImpl(player.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURE, game)); + int count = cards.size(); + return count > 0 && new ZombieToken().putOntoBattlefield(count, game, source, controller.getId()); } @Override diff --git a/Mage.Sets/src/mage/cards/n/NecromanticSelection.java b/Mage.Sets/src/mage/cards/n/NecromanticSelection.java index c8592e14cae4..5b7e42c5b0a6 100644 --- a/Mage.Sets/src/mage/cards/n/NecromanticSelection.java +++ b/Mage.Sets/src/mage/cards/n/NecromanticSelection.java @@ -39,7 +39,7 @@ public NecromanticSelection(UUID ownerId, CardSetInfo setInfo) { // Destroy all creatures, then return a creature card put into a graveyard this way to the battlefield under your control. It's a black Zombie in addition to its other colors and types. Exile Necromantic Selection. this.getSpellAbility().addEffect(new NecromanticSelectionEffect()); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } diff --git a/Mage.Sets/src/mage/cards/n/Necromentia.java b/Mage.Sets/src/mage/cards/n/Necromentia.java index a1122165a55a..093a2317c702 100644 --- a/Mage.Sets/src/mage/cards/n/Necromentia.java +++ b/Mage.Sets/src/mage/cards/n/Necromentia.java @@ -54,7 +54,7 @@ class NecromentiaEffect extends OneShotEffect { NecromentiaEffect() { super(Outcome.Benefit); - staticText = "Search target opponent's graveyard, hand, and library for any number of cards with that name and exile them. That player shuffles their library, then creates a 2/2 black Zombie creature token for each card exiled from their hand this way"; + staticText = "Search target opponent's graveyard, hand, and library for any number of cards with that name and exile them. That player shuffles, then creates a 2/2 black Zombie creature token for each card exiled from their hand this way"; } private NecromentiaEffect(NecromentiaEffect effect) { diff --git a/Mage.Sets/src/mage/cards/n/Necropanther.java b/Mage.Sets/src/mage/cards/n/Necropanther.java index 4edad9309e67..9f3ce68d7c75 100644 --- a/Mage.Sets/src/mage/cards/n/Necropanther.java +++ b/Mage.Sets/src/mage/cards/n/Necropanther.java @@ -12,7 +12,7 @@ import mage.constants.SubType; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInYourGraveyard; import java.util.UUID; @@ -23,10 +23,10 @@ public final class Necropanther extends CardImpl { private static final FilterCard filter - = new FilterCreatureCard("creature card with converted mana cost 3 or less from your graveyard"); + = new FilterCreatureCard("creature card with mana value 3 or less from your graveyard"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public Necropanther(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/n/Necroplasm.java b/Mage.Sets/src/mage/cards/n/Necroplasm.java index 47f2be798703..e8c870c903e2 100644 --- a/Mage.Sets/src/mage/cards/n/Necroplasm.java +++ b/Mage.Sets/src/mage/cards/n/Necroplasm.java @@ -18,7 +18,7 @@ import mage.constants.TargetController; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -60,7 +60,7 @@ class NecroplasmEffect extends OneShotEffect { NecroplasmEffect() { super(Outcome.DestroyPermanent); - this.staticText = "destroy each creature with converted mana cost equal to the number of +1/+1 counters on {this}."; + this.staticText = "destroy each creature with mana value equal to the number of +1/+1 counters on {this}."; } NecroplasmEffect(final NecroplasmEffect effect) { @@ -79,7 +79,7 @@ public boolean apply(Game game, Ability source) { if (player != null && sourcePermanent != null) { int numCounters = sourcePermanent.getCounters(game).getCount(CounterType.P1P1); FilterCreaturePermanent filter = new FilterCreaturePermanent(); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, numCounters)); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, numCounters)); for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { if(permanent != null) { permanent.destroy(source, game, false); diff --git a/Mage.Sets/src/mage/cards/n/Necropolis.java b/Mage.Sets/src/mage/cards/n/Necropolis.java index bd514c50fa38..f45749a485f1 100644 --- a/Mage.Sets/src/mage/cards/n/Necropolis.java +++ b/Mage.Sets/src/mage/cards/n/Necropolis.java @@ -39,7 +39,7 @@ public Necropolis(UUID ownerId, CardSetInfo setInfo) { // Exile a creature card from your graveyard: Put X +0/+1 counters on Necropolis, where X is the exiled card's converted mana cost. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, - new AddCountersSourceEffect(CounterType.P0P1.createInstance(0), new NecropolisValue(), true).setText("Put X +0/+1 counters on {this}, where X is the exiled card's converted mana cost"), + new AddCountersSourceEffect(CounterType.P0P1.createInstance(0), new NecropolisValue(), true).setText("Put X +0/+1 counters on {this}, where X is the exiled card's mana value"), new ExileFromGraveCost(new TargetCardInYourGraveyard(new FilterCreatureCard("a creature card"))))); } @@ -61,7 +61,7 @@ public int calculate(Game game, Ability sourceAbility, Effect effect) { if (cost.isPaid() && cost instanceof ExileFromGraveCost) { int xValue = 0; for (Card card : ((ExileFromGraveCost) cost).getExiledCards()) { - xValue += card.getConvertedManaCost(); + xValue += card.getManaValue(); } return xValue; } diff --git a/Mage.Sets/src/mage/cards/n/Necrosavant.java b/Mage.Sets/src/mage/cards/n/Necrosavant.java index b164672713c5..29b43a7bad79 100644 --- a/Mage.Sets/src/mage/cards/n/Necrosavant.java +++ b/Mage.Sets/src/mage/cards/n/Necrosavant.java @@ -34,7 +34,7 @@ public Necrosavant(UUID ownerId, CardSetInfo setInfo) { // {3}{B}{B}, Sacrifice a creature: Return Necrosavant from your graveyard to the battlefield. Activate this ability only during your upkeep. Ability ability = new ConditionalActivatedAbility(Zone.GRAVEYARD, - new ReturnSourceFromGraveyardToBattlefieldEffect(), + new ReturnSourceFromGraveyardToBattlefieldEffect(false,false), new ManaCostsImpl("{3}{B}{B}"), new IsStepCondition(PhaseStep.UPKEEP), null diff --git a/Mage.Sets/src/mage/cards/n/NecroticFumes.java b/Mage.Sets/src/mage/cards/n/NecroticFumes.java new file mode 100644 index 000000000000..0443d4796482 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NecroticFumes.java @@ -0,0 +1,42 @@ +package mage.cards.n; + +import mage.abilities.costs.common.ExileTargetCost; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetControlledPermanent; +import mage.target.common.TargetCreatureOrPlaneswalker; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NecroticFumes extends CardImpl { + + public NecroticFumes(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}{B}"); + + this.subtype.add(SubType.LESSON); + + // As an additional cost to cast this spell, exile a creature you control. + this.getSpellAbility().addCost(new ExileTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_A_CREATURE))); + + // Exile target creature or planeswalker. + this.getSpellAbility().addEffect(new ExileTargetEffect()); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); + } + + private NecroticFumes(final NecroticFumes card) { + super(card); + } + + @Override + public NecroticFumes copy() { + return new NecroticFumes(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/NecroticPlague.java b/Mage.Sets/src/mage/cards/n/NecroticPlague.java index 1365481d5aed..1e279b75a3e4 100644 --- a/Mage.Sets/src/mage/cards/n/NecroticPlague.java +++ b/Mage.Sets/src/mage/cards/n/NecroticPlague.java @@ -85,6 +85,7 @@ public void adjustTargets(Ability ability, Game game) { ability.setControllerId(creatureController.getId()); ability.getTargets().clear(); TargetPermanent target = new TargetOpponentsCreaturePermanent(); + target.setTargetController(creatureController.getId()); ability.getTargets().add(target); } } diff --git a/Mage.Sets/src/mage/cards/n/NeedlethornDrake.java b/Mage.Sets/src/mage/cards/n/NeedlethornDrake.java new file mode 100644 index 000000000000..bcf222c3a516 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NeedlethornDrake.java @@ -0,0 +1,40 @@ +package mage.cards.n; + +import mage.MageInt; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.FlyingAbility; +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 NeedlethornDrake extends CardImpl { + + public NeedlethornDrake(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{U}"); + + this.subtype.add(SubType.DRAKE); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + } + + private NeedlethornDrake(final NeedlethornDrake card) { + super(card); + } + + @Override + public NeedlethornDrake copy() { + return new NeedlethornDrake(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/NehebDreadhordeChampion.java b/Mage.Sets/src/mage/cards/n/NehebDreadhordeChampion.java index 69a785b7d0df..ed232f0b8248 100644 --- a/Mage.Sets/src/mage/cards/n/NehebDreadhordeChampion.java +++ b/Mage.Sets/src/mage/cards/n/NehebDreadhordeChampion.java @@ -8,19 +8,11 @@ import mage.abilities.keyword.TrampleAbility; 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.SuperType; -import mage.filter.StaticFilters; +import mage.constants.*; import mage.game.Game; import mage.players.Player; -import mage.target.TargetCard; -import mage.target.common.TargetCardInHand; import java.util.UUID; -import java.util.stream.IntStream; /** * @author TheElk801 @@ -79,18 +71,12 @@ public boolean apply(Game game, Ability source) { if (player == null) { return false; } - TargetCard target = new TargetCardInHand(0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD); - if (!player.choose(outcome, target, source.getSourceId(), game)) { - return false; - } - int counter = player.discard(new CardsImpl(target.getTargets()), false, source, game).size(); - Mana mana = new Mana(); + int counter = player.discard(0, Integer.MAX_VALUE, false, source, game).size(); if (counter < 1) { return true; } - IntStream.range(0, counter).forEach(x -> mana.increaseRed()); player.drawCards(counter, source, game); - player.getManaPool().addMana(mana, game, source, true); + player.getManaPool().addMana(new Mana(ManaType.RED, counter), game, source, true); return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/n/Neoform.java b/Mage.Sets/src/mage/cards/n/Neoform.java index 1d6bac5871e2..65203b10a6df 100644 --- a/Mage.Sets/src/mage/cards/n/Neoform.java +++ b/Mage.Sets/src/mage/cards/n/Neoform.java @@ -13,7 +13,7 @@ import mage.counters.CounterType; import mage.filter.FilterCard; import mage.filter.StaticFilters; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; @@ -57,9 +57,9 @@ class NeoformEffect extends OneShotEffect { NeoformEffect() { super(Outcome.Benefit); - staticText = "Search your library for a creature card with converted mana cost equal to " + - "1 plus the sacrificed creature's converted mana cost, " + - "put that card onto the battlefield with an additional +1/+1 counter on it, then shuffle your library."; + staticText = "Search your library for a creature card with mana value equal to " + + "1 plus the sacrificed creature's mana value, " + + "put that card onto the battlefield with an additional +1/+1 counter on it, then shuffle."; } private NeoformEffect(final NeoformEffect effect) { @@ -82,9 +82,9 @@ public boolean apply(Game game, Ability source) { if (sacrificedPermanent == null || controller == null) { return false; } - int newConvertedCost = sacrificedPermanent.getConvertedManaCost() + 1; - FilterCard filter = new FilterCard("creature card with converted mana cost " + newConvertedCost); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, newConvertedCost)); + int newConvertedCost = sacrificedPermanent.getManaValue() + 1; + FilterCard filter = new FilterCard("creature card with mana value " + newConvertedCost); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, newConvertedCost)); filter.add(CardType.CREATURE.getPredicate()); TargetCardInLibrary target = new TargetCardInLibrary(filter); if (controller.searchLibrary(target, source, game)) { diff --git a/Mage.Sets/src/mage/cards/n/NephaliaSmuggler.java b/Mage.Sets/src/mage/cards/n/NephaliaSmuggler.java index acbc02188aec..c081c100933f 100644 --- a/Mage.Sets/src/mage/cards/n/NephaliaSmuggler.java +++ b/Mage.Sets/src/mage/cards/n/NephaliaSmuggler.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledCreaturePermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/n/NessianHornbeetle.java b/Mage.Sets/src/mage/cards/n/NessianHornbeetle.java index 86f5f174b23c..992447f31f02 100644 --- a/Mage.Sets/src/mage/cards/n/NessianHornbeetle.java +++ b/Mage.Sets/src/mage/cards/n/NessianHornbeetle.java @@ -16,7 +16,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/n/NessianWildsRavager.java b/Mage.Sets/src/mage/cards/n/NessianWildsRavager.java index eea21cb467de..2d3609755e49 100644 --- a/Mage.Sets/src/mage/cards/n/NessianWildsRavager.java +++ b/Mage.Sets/src/mage/cards/n/NessianWildsRavager.java @@ -14,7 +14,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/n/NestedGhoul.java b/Mage.Sets/src/mage/cards/n/NestedGhoul.java index b02044d0a814..bf65048e36be 100644 --- a/Mage.Sets/src/mage/cards/n/NestedGhoul.java +++ b/Mage.Sets/src/mage/cards/n/NestedGhoul.java @@ -59,7 +59,7 @@ public NestedGhoulTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/n/NestingGrounds.java b/Mage.Sets/src/mage/cards/n/NestingGrounds.java index 7ef79ee7d5fb..aad65d99c4e8 100644 --- a/Mage.Sets/src/mage/cards/n/NestingGrounds.java +++ b/Mage.Sets/src/mage/cards/n/NestingGrounds.java @@ -17,7 +17,7 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/n/NetherVoid.java b/Mage.Sets/src/mage/cards/n/NetherVoid.java index 97c14619d2ef..06ac88f56090 100644 --- a/Mage.Sets/src/mage/cards/n/NetherVoid.java +++ b/Mage.Sets/src/mage/cards/n/NetherVoid.java @@ -11,6 +11,7 @@ import mage.constants.SetTargetPointer; import mage.constants.SuperType; import mage.filter.FilterSpell; +import mage.filter.StaticFilters; /** * @@ -23,7 +24,7 @@ public NetherVoid(UUID ownerId, CardSetInfo setInfo) { addSuperType(SuperType.WORLD); // Whenever a player casts a spell, counter it unless that player pays {3}. - this.addAbility(new SpellCastAllTriggeredAbility(new CounterUnlessPaysEffect(new GenericManaCost(3)), new FilterSpell("a spell"), false, SetTargetPointer.SPELL)); + this.addAbility(new SpellCastAllTriggeredAbility(new CounterUnlessPaysEffect(new GenericManaCost(3)), StaticFilters.FILTER_SPELL_A, false, SetTargetPointer.SPELL)); } private NetherVoid(final NetherVoid card) { diff --git a/Mage.Sets/src/mage/cards/n/NettlingImp.java b/Mage.Sets/src/mage/cards/n/NettlingImp.java index d054646eea14..8bd273d6ce5c 100644 --- a/Mage.Sets/src/mage/cards/n/NettlingImp.java +++ b/Mage.Sets/src/mage/cards/n/NettlingImp.java @@ -52,7 +52,7 @@ public NettlingImp(UUID ownerId, CardSetInfo setInfo) { new TapSourceCost(), new NettlingImpTurnCondition(), "{T}: Choose target non-Wall creature the active player has controlled continuously since the beginning of the turn. " + "That creature attacks this turn if able. If it doesn't, destroy it at the beginning of the next end step. " - + "Activate this ability only during an opponent's turn, before attackers are declared."); + + "Activate only during an opponent's turn, before attackers are declared."); ability.addEffect(new NettlingImpDelayedDestroyEffect()); ability.addTarget(new TargetCreaturePermanent(filter)); this.addAbility(ability, new AttackedThisTurnWatcher()); diff --git a/Mage.Sets/src/mage/cards/n/NeverendingTorment.java b/Mage.Sets/src/mage/cards/n/NeverendingTorment.java index b47ca65ece4b..3438c7dbc38b 100644 --- a/Mage.Sets/src/mage/cards/n/NeverendingTorment.java +++ b/Mage.Sets/src/mage/cards/n/NeverendingTorment.java @@ -50,7 +50,7 @@ class NeverendingTormentEffect extends OneShotEffect { public NeverendingTormentEffect() { super(Outcome.Benefit); - staticText = "Search target player's library for X cards, where X is the number of cards in your hand, and exile them. Then that player shuffles their library"; + staticText = "Search target player's library for X cards, where X is the number of cards in your hand, and exile them. Then that player shuffles"; } public NeverendingTormentEffect(final NeverendingTormentEffect effect) { diff --git a/Mage.Sets/src/mage/cards/n/NewFrontiers.java b/Mage.Sets/src/mage/cards/n/NewFrontiers.java index dce943e682cf..042ac488f55f 100644 --- a/Mage.Sets/src/mage/cards/n/NewFrontiers.java +++ b/Mage.Sets/src/mage/cards/n/NewFrontiers.java @@ -43,7 +43,7 @@ class NewFrontiersEffect extends OneShotEffect { public NewFrontiersEffect() { super(Outcome.Detriment); - this.staticText = "Each player may search their library for up to X basic land cards and put them onto the battlefield tapped. Then each player who searched their library this way shuffles it"; + this.staticText = "Each player may search their library for up to X basic land cards and put them onto the battlefield tapped. Then each player who searched their library this way shuffles"; } public NewFrontiersEffect(final NewFrontiersEffect effect) { diff --git a/Mage.Sets/src/mage/cards/n/NiambiEsteemedSpeaker.java b/Mage.Sets/src/mage/cards/n/NiambiEsteemedSpeaker.java index 83a79d8d9e12..415a41a6548c 100644 --- a/Mage.Sets/src/mage/cards/n/NiambiEsteemedSpeaker.java +++ b/Mage.Sets/src/mage/cards/n/NiambiEsteemedSpeaker.java @@ -20,7 +20,7 @@ import mage.filter.FilterCard; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -84,7 +84,7 @@ class NiambiEsteemedSpeakerEffect extends OneShotEffect { NiambiEsteemedSpeakerEffect() { super(Outcome.Benefit); - staticText = "If you do, you gain life equal to that creature's converted mana cost."; + staticText = "If you do, you gain life equal to that creature's mana value."; } private NiambiEsteemedSpeakerEffect(final NiambiEsteemedSpeakerEffect effect) { @@ -103,7 +103,7 @@ public boolean apply(Game game, Ability source) { if (permanent == null || player == null) { return false; } - return permanent.getConvertedManaCost() > 0 - && player.gainLife(permanent.getConvertedManaCost(), game, source) > 0; + return permanent.getManaValue() > 0 + && player.gainLife(permanent.getManaValue(), game, source) > 0; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/n/NicolBolasDragonGod.java b/Mage.Sets/src/mage/cards/n/NicolBolasDragonGod.java index dce9352d5a50..fb6d6a6fd584 100644 --- a/Mage.Sets/src/mage/cards/n/NicolBolasDragonGod.java +++ b/Mage.Sets/src/mage/cards/n/NicolBolasDragonGod.java @@ -15,7 +15,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; import mage.filter.common.FilterPlaneswalkerPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/n/NightDealings.java b/Mage.Sets/src/mage/cards/n/NightDealings.java index f64cf7055d7d..f130b49c6f8b 100644 --- a/Mage.Sets/src/mage/cards/n/NightDealings.java +++ b/Mage.Sets/src/mage/cards/n/NightDealings.java @@ -1,8 +1,5 @@ - package mage.cards.n; -import java.util.Objects; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.SimpleActivatedAbility; @@ -10,20 +7,26 @@ import mage.abilities.costs.common.RemoveVariableCountersSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.ComparisonType; import mage.constants.Outcome; import mage.constants.Zone; import mage.counters.CounterType; +import mage.filter.FilterCard; import mage.filter.common.FilterNonlandCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInLibrary; +import java.util.UUID; + /** * @author Loki */ @@ -36,7 +39,7 @@ public NightDealings(UUID ownerId, CardSetInfo setInfo) { this.addAbility((new NightDealingsTriggeredAbility())); // {2}{B}{B}, Remove X theft counters from Night Dealings: Search your library for a nonland card with converted mana cost X, reveal it, and put it into your hand. Then shuffle your library. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new NightDealingsSearchEffect(), new ManaCostsImpl("{2}{B}{B}")); + Ability ability = new SimpleActivatedAbility(new NightDealingsSearchEffect(), new ManaCostsImpl("{2}{B}{B}")); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.THEFT.createInstance(1))); this.addAbility(ability); } @@ -50,13 +53,13 @@ public NightDealings copy() { return new NightDealings(this); } - private class NightDealingsTriggeredAbility extends TriggeredAbilityImpl { + private static final class NightDealingsTriggeredAbility extends TriggeredAbilityImpl { - public NightDealingsTriggeredAbility() { + private NightDealingsTriggeredAbility() { super(Zone.BATTLEFIELD, new NightDealingsEffect()); } - public NightDealingsTriggeredAbility(final NightDealingsTriggeredAbility ability) { + private NightDealingsTriggeredAbility(final NightDealingsTriggeredAbility ability) { super(ability); } @@ -73,16 +76,12 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { // to another player - if (!Objects.equals(this.getControllerId(), event.getTargetId())) { - // a source you control - UUID sourceControllerId = game.getControllerId(event.getSourceId()); - if (sourceControllerId != null && sourceControllerId.equals(this.getControllerId())) { - // save amount of damage to effect - this.getEffects().get(0).setValue("damageAmount", event.getAmount()); - return true; - } + if (this.isControlledBy(event.getTargetId()) + || !this.isControlledBy(game.getControllerId(event.getSourceId()))) { + return false; } - return false; + this.getEffects().setValue("damageAmount", event.getAmount()); + return true; } @Override @@ -91,14 +90,14 @@ public String getRule() { } } - private static class NightDealingsEffect extends OneShotEffect { + private static final class NightDealingsEffect extends OneShotEffect { - public NightDealingsEffect() { + private NightDealingsEffect() { super(Outcome.Damage); this.staticText = "put that many theft counters on {this}"; } - public NightDealingsEffect(final NightDealingsEffect effect) { + private NightDealingsEffect(final NightDealingsEffect effect) { super(effect); } @@ -109,26 +108,27 @@ public NightDealingsEffect copy() { @Override public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); Integer damageAmount = (Integer) this.getValue("damageAmount"); - if (damageAmount != null) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - permanent.addCounters(CounterType.THEFT.createInstance(damageAmount), source.getControllerId(), source, game); - return true; - } - } - return false; + return permanent != null + && damageAmount != null + && damageAmount > 0 + && permanent.addCounters( + CounterType.THEFT.createInstance(damageAmount), + source.getControllerId(), source, game + ); } } - private static class NightDealingsSearchEffect extends OneShotEffect { + private static final class NightDealingsSearchEffect extends OneShotEffect { - public NightDealingsSearchEffect() { + private NightDealingsSearchEffect() { super(Outcome.DrawCard); - this.staticText = "Search your library for a nonland card with converted mana cost X, reveal it, and put it into your hand. Then shuffle your library"; + this.staticText = "Search your library for a nonland card with mana value X, " + + "reveal it, put it into your hand, then shuffle"; } - public NightDealingsSearchEffect(final NightDealingsSearchEffect effect) { + private NightDealingsSearchEffect(final NightDealingsSearchEffect effect) { super(effect); } @@ -148,33 +148,21 @@ public boolean apply(Game game, Ability source) { for (Cost cost : source.getCosts()) { if (cost instanceof RemoveVariableCountersSourceCost) { cmc = ((RemoveVariableCountersSourceCost) cost).getAmount(); + break; } } - FilterNonlandCard filter = new FilterNonlandCard("nonland card with converted mana cost X = " + cmc); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, cmc)); + FilterCard filter = new FilterNonlandCard("nonland card with mana value " + cmc); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, cmc)); TargetCardInLibrary target = new TargetCardInLibrary(filter); - - if (player.searchLibrary(target, source, game)) { - Card card = player.getLibrary().getCard(target.getFirstTarget(), game); - if (card != null) { - card.moveToZone(Zone.HAND, source, game, false); - - String name = "Reveal"; - Cards cards = new CardsImpl(card); - Card sourceCard = game.getCard(source.getSourceId()); - if (sourceCard != null) { - name = sourceCard.getName(); - } - player.revealCards(name, cards, game); - game.informPlayers(player.getLogName() + " reveals " + card.getName()); - } - player.shuffleLibrary(source, game); - return true; + player.searchLibrary(target, source, game); + Card card = player.getLibrary().getCard(target.getFirstTarget(), game); + if (card != null) { + player.revealCards(source, new CardsImpl(card), game); + player.moveCards(card, Zone.HAND, source, game); } player.shuffleLibrary(source, game); - return false; + return true; } } - } diff --git a/Mage.Sets/src/mage/cards/n/NightTerrors.java b/Mage.Sets/src/mage/cards/n/NightTerrors.java index f4f6a4d06b7b..86e8a3f6c9f0 100644 --- a/Mage.Sets/src/mage/cards/n/NightTerrors.java +++ b/Mage.Sets/src/mage/cards/n/NightTerrors.java @@ -1,7 +1,5 @@ - package mage.cards.n; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; @@ -10,21 +8,21 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.common.FilterNonlandCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author North */ public final class NightTerrors extends CardImpl { public NightTerrors(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}"); // Target player reveals their hand. You choose a nonland card from it. Exile that card. this.getSpellAbility().addTarget(new TargetPlayer()); @@ -43,12 +41,12 @@ public NightTerrors copy() { class NightTerrorsEffect extends OneShotEffect { - public NightTerrorsEffect() { + NightTerrorsEffect() { super(Outcome.Exile); this.staticText = "Target player reveals their hand. You choose a nonland card from it. Exile that card"; } - public NightTerrorsEffect(final NightTerrorsEffect effect) { + private NightTerrorsEffect(final NightTerrorsEffect effect) { super(effect); } @@ -61,19 +59,16 @@ public NightTerrorsEffect copy() { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); Player targetPlayer = game.getPlayer(source.getFirstTarget()); - if (player != null && targetPlayer != null) { - targetPlayer.revealCards("Night Terrors", targetPlayer.getHand(), game); - - TargetCard target = new TargetCard(Zone.HAND, new FilterNonlandCard("nonland card to exile")); - if (player.choose(Outcome.Exile, targetPlayer.getHand(), target, game)) { - Card card = targetPlayer.getHand().get(target.getFirstTarget(), game); - if (card != null) { - card.moveToExile(null, "", source, game); - } - } + if (player == null || targetPlayer == null) { + return false; + } + targetPlayer.revealCards(source, targetPlayer.getHand(), game); + TargetCard target = new TargetCard(Zone.HAND, StaticFilters.FILTER_CARD_NON_LAND); + if (!player.choose(Outcome.Exile, targetPlayer.getHand(), target, game)) { return true; } - return false; + Card card = targetPlayer.getHand().get(target.getFirstTarget(), game); + return card != null && player.moveCards(card, Zone.EXILED, source, game); } } diff --git a/Mage.Sets/src/mage/cards/n/NightfallPredator.java b/Mage.Sets/src/mage/cards/n/NightfallPredator.java index 0231396f8dd6..7b3d840e22cd 100644 --- a/Mage.Sets/src/mage/cards/n/NightfallPredator.java +++ b/Mage.Sets/src/mage/cards/n/NightfallPredator.java @@ -1,38 +1,27 @@ - package mage.cards.n; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.TransformSourceEffect; -import mage.abilities.keyword.TransformAbility; +import mage.abilities.effects.common.FightTargetSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author North */ public final class NightfallPredator extends CardImpl { public NightfallPredator(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.WEREWOLF); this.color.setGreen(true); @@ -44,13 +33,15 @@ public NightfallPredator(UUID ownerId, CardSetInfo setInfo) { this.transformable = true; // {R}, {tap}: Nightfall Predator fights target creature. - Ability activatedAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new NightfallPredatorEffect(), new ManaCostsImpl("{R}")); + Ability activatedAbility = new SimpleActivatedAbility( + new FightTargetSourceEffect().setText("{this} fights target creature"), new ManaCostsImpl("{R}") + ); activatedAbility.addCost(new TapSourceCost()); activatedAbility.addTarget(new TargetCreaturePermanent()); this.addAbility(activatedAbility); + // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Nightfall Predator. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private NightfallPredator(final NightfallPredator card) { @@ -62,33 +53,3 @@ public NightfallPredator copy() { return new NightfallPredator(this); } } - -class NightfallPredatorEffect extends OneShotEffect { - - public NightfallPredatorEffect() { - super(Outcome.Damage); - this.staticText = "{this} fights target creature"; - } - - public NightfallPredatorEffect(final NightfallPredatorEffect effect) { - super(effect); - } - - @Override - public NightfallPredatorEffect copy() { - return new NightfallPredatorEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent creature1 = game.getPermanent(source.getSourceId()); - Permanent creature2 = game.getPermanent(source.getFirstTarget()); - // 20110930 - 701.10 - if (creature1 != null && creature2 != null) { - if (creature1.isCreature() && creature2.isCreature()) { - return creature1.fight(creature2, source, game); - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/n/NightmareIncursion.java b/Mage.Sets/src/mage/cards/n/NightmareIncursion.java index 56a80cb0a681..9152d18758e4 100644 --- a/Mage.Sets/src/mage/cards/n/NightmareIncursion.java +++ b/Mage.Sets/src/mage/cards/n/NightmareIncursion.java @@ -51,7 +51,7 @@ public NightmareIncursionEffect() { super(Outcome.Benefit); this.staticText = "Search target player's library for up to X cards, " + "where X is the number of Swamps you control, and exile them. " + - "Then that player shuffles their library"; + "Then that player shuffles"; } public NightmareIncursionEffect(final NightmareIncursionEffect effect) { diff --git a/Mage.Sets/src/mage/cards/n/NightmareShepherd.java b/Mage.Sets/src/mage/cards/n/NightmareShepherd.java index d895ef245b4a..adbd503fef6e 100644 --- a/Mage.Sets/src/mage/cards/n/NightmareShepherd.java +++ b/Mage.Sets/src/mage/cards/n/NightmareShepherd.java @@ -16,7 +16,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/n/NightmaresThirst.java b/Mage.Sets/src/mage/cards/n/NightmaresThirst.java index 22f54e539c45..f72d9c44d20f 100644 --- a/Mage.Sets/src/mage/cards/n/NightmaresThirst.java +++ b/Mage.Sets/src/mage/cards/n/NightmaresThirst.java @@ -1,11 +1,10 @@ package mage.cards.n; -import java.util.UUID; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.MultipliedValue; +import mage.abilities.dynamicvalue.common.ControllerGotLifeCount; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; -import mage.abilities.dynamicvalue.common.ControllerGotLifeCount; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -13,23 +12,26 @@ import mage.target.common.TargetCreaturePermanent; import mage.watchers.common.PlayerGainedLifeWatcher; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class NightmaresThirst extends CardImpl { + private static final DynamicValue xValue = new MultipliedValue(ControllerGotLifeCount.instance, -1); + public NightmaresThirst(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}"); // You gain 1 life. Target creature gets -X/-X until end of turn, where X is the amount of life you gained this turn. this.getSpellAbility().addEffect(new GainLifeEffect(1)); - DynamicValue xValue = new MultipliedValue(ControllerGotLifeCount.getInstance(), -1); this.getSpellAbility().addEffect(new BoostTargetEffect( xValue, xValue, Duration.EndOfTurn, true ).setText("Target creature gets -X/-X until end of turn, where X is the amount of life you gained this turn.")); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addWatcher(new PlayerGainedLifeWatcher()); + this.getSpellAbility().addHint(ControllerGotLifeCount.getHint()); } private NightmaresThirst(final NightmaresThirst card) { diff --git a/Mage.Sets/src/mage/cards/n/NikaraLairScavenger.java b/Mage.Sets/src/mage/cards/n/NikaraLairScavenger.java index f93bd3e489fa..cbfc63ea250e 100644 --- a/Mage.Sets/src/mage/cards/n/NikaraLairScavenger.java +++ b/Mage.Sets/src/mage/cards/n/NikaraLairScavenger.java @@ -14,7 +14,7 @@ import mage.constants.SuperType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.CounterAnyPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/n/NikoAris.java b/Mage.Sets/src/mage/cards/n/NikoAris.java index d6240a7374f9..8e529fec322f 100644 --- a/Mage.Sets/src/mage/cards/n/NikoAris.java +++ b/Mage.Sets/src/mage/cards/n/NikoAris.java @@ -8,6 +8,7 @@ import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.MultipliedValue; import mage.abilities.dynamicvalue.common.CardsDrawnThisTurnDynamicValue; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.CreateTokenEffect; @@ -16,18 +17,20 @@ import mage.abilities.effects.common.combat.CantBeBlockedTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.token.ShardToken; -import mage.game.stack.Spell; import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; -import mage.watchers.Watcher; import mage.watchers.common.CardsDrawnThisTurnWatcher; +import mage.watchers.common.ManaSpentToCastWatcher; import java.util.Objects; import java.util.UUID; @@ -54,8 +57,8 @@ public NikoAris(UUID ownerId, CardSetInfo setInfo) { // When Niko Aris enters the battlefield, create X Shard tokens. this.addAbility(new EntersBattlefieldTriggeredAbility( - new CreateTokenEffect(new ShardToken(), NikoArisValue.instance) - ), new NikoArisWatcher()); + new CreateTokenEffect(new ShardToken(), ManacostVariableValue.instance) + ), new ManaSpentToCastWatcher()); // +1: Up to one target creature you control can't be blocked this turn. Whenever that creature deals damage this turn, return it to its owner's hand. Ability ability = new LoyaltyAbility(new CantBeBlockedTargetEffect(Duration.EndOfTurn), 1); @@ -84,59 +87,6 @@ public NikoAris copy() { } } -enum NikoArisValue implements DynamicValue { - instance; - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - NikoArisWatcher watcher = game.getState().getWatcher(NikoArisWatcher.class, sourceAbility.getSourceId()); - if (watcher == null) { - return 0; - } - if (game.getState().getValue(sourceAbility.getSourceId().toString() + "xValue") == null) { - return 0; - } - return (Integer) game.getState().getValue(sourceAbility.getSourceId().toString() + "xValue"); - } - - @Override - public DynamicValue copy() { - return instance; - } - - @Override - public String toString() { - return "X"; - } - - @Override - public String getMessage() { - return ""; - } -} - -class NikoArisWatcher extends Watcher { - - NikoArisWatcher() { - super(WatcherScope.CARD); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() != GameEvent.EventType.SPELL_CAST) { - return; - } - Spell spell = game.getSpellOrLKIStack(event.getTargetId()); - if (spell == null || spell.getSourceId() != super.getSourceId()) { - return; - } - game.getState().setValue( - spell.getSourceId().toString() + "xValue", - spell.getSpellAbility().getManaCostsToPay().getX() - ); - } -} - class NikoArisDamageTriggeredAbility extends DelayedTriggeredAbility { NikoArisDamageTriggeredAbility() { @@ -156,8 +106,7 @@ public NikoArisDamageTriggeredAbility copy() { public boolean checkEventType(GameEvent event, Game game) { switch (event.getType()) { case DAMAGED_PLAYER: - case DAMAGED_CREATURE: - case DAMAGED_PLANESWALKER: + case DAMAGED_PERMANENT: return true; } return false; @@ -177,4 +126,4 @@ public boolean checkTrigger(GameEvent event, Game game) { public String getRule() { return "Whenever that creature deals damage this turn, return it to its owner's hand."; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/n/NikoDefiesDestiny.java b/Mage.Sets/src/mage/cards/n/NikoDefiesDestiny.java index 70261b9a8f74..6a56e48c04f5 100644 --- a/Mage.Sets/src/mage/cards/n/NikoDefiesDestiny.java +++ b/Mage.Sets/src/mage/cards/n/NikoDefiesDestiny.java @@ -24,7 +24,7 @@ import mage.constants.SubType; import mage.filter.FilterCard; import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.ExileZone; import mage.game.Game; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/n/NilsDisciplineEnforcer.java b/Mage.Sets/src/mage/cards/n/NilsDisciplineEnforcer.java new file mode 100644 index 000000000000..4496272c9528 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NilsDisciplineEnforcer.java @@ -0,0 +1,119 @@ +package mage.cards.n; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.combat.CantAttackYouUnlessPayManaAllEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +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.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.targetadjustment.TargetAdjuster; +import mage.target.targetpointer.EachTargetPointer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NilsDisciplineEnforcer extends CardImpl { + + public NilsDisciplineEnforcer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // At the beginning of your end step, for each player, put a +1/+1 counter on up to one target creature that player controls. + Ability ability = new BeginningOfEndStepTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()) + .setTargetPointer(new EachTargetPointer()) + .setText("for each player, put a +1/+1 counter on up to one target creature that player controls"), + TargetController.YOU, false + ); + ability.setTargetAdjuster(NilsDisciplineEnforcerAdjuster.instance); + this.addAbility(ability); + + // Each creature with one or more counters on it can't attack you or planeswalkers you control unless its controller pays {X}, where X is the number of counters on that creature. + this.addAbility(new SimpleStaticAbility(new NilsDisciplineEnforcerEffect())); + } + + private NilsDisciplineEnforcer(final NilsDisciplineEnforcer card) { + super(card); + } + + @Override + public NilsDisciplineEnforcer copy() { + return new NilsDisciplineEnforcer(this); + } +} + +enum NilsDisciplineEnforcerAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + ability.getTargets().clear(); + for (UUID playerId : game.getState().getPlayersInRange(ability.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + FilterPermanent filter = new FilterCreaturePermanent("creature controlled by " + player.getName()); + ability.addTarget(new TargetPermanent(0, 1, filter)); + } + } +} + +class NilsDisciplineEnforcerEffect extends CantAttackYouUnlessPayManaAllEffect { + + NilsDisciplineEnforcerEffect() { + super(null, true); + staticText = "Each creature with one or more counters on it can't attack you or planeswalkers you control " + + "unless its controller pays {X}, where X is the number of counters on that creature."; + } + + private NilsDisciplineEnforcerEffect(NilsDisciplineEnforcerEffect effect) { + super(effect); + } + + @Override + public ManaCosts getManaCostToPay(GameEvent event, Ability source, Game game) { + Permanent permanent = game.getPermanent(event.getSourceId()); + if (permanent == null) { + return null; + } + int count = permanent + .getCounters(game) + .keySet() + .stream() + .mapToInt(permanent.getCounters(game)::getCount) + .sum(); + if (count < 1) { + return null; + } + return new ManaCostsImpl("{" + count + '}'); + } + + @Override + public NilsDisciplineEnforcerEffect copy() { + return new NilsDisciplineEnforcerEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/NimDeathmantle.java b/Mage.Sets/src/mage/cards/n/NimDeathmantle.java index fef851708e53..b233244d4c75 100644 --- a/Mage.Sets/src/mage/cards/n/NimDeathmantle.java +++ b/Mage.Sets/src/mage/cards/n/NimDeathmantle.java @@ -94,8 +94,7 @@ public boolean checkTrigger(GameEvent event, Game game) { Permanent permanent = zEvent.getTarget(); if (permanent != null && permanent.isOwnedBy(this.controllerId) - && zEvent.getToZone() == Zone.GRAVEYARD - && zEvent.getFromZone() == Zone.BATTLEFIELD + && zEvent.isDiesEvent() && !(permanent instanceof PermanentToken) && permanent.isCreature()) { diff --git a/Mage.Sets/src/mage/cards/n/NimDevourer.java b/Mage.Sets/src/mage/cards/n/NimDevourer.java index 42c05673aa8a..fafc11ca9664 100644 --- a/Mage.Sets/src/mage/cards/n/NimDevourer.java +++ b/Mage.Sets/src/mage/cards/n/NimDevourer.java @@ -43,7 +43,7 @@ public NimDevourer(UUID ownerId, CardSetInfo setInfo) { // {B}{B}: Return Nim Devourer from your graveyard to the battlefield, then sacrifice a creature. Activate this ability only during your upkeep. Ability ability = new ConditionalActivatedAbility(Zone.GRAVEYARD, - new ReturnSourceFromGraveyardToBattlefieldEffect(), + new ReturnSourceFromGraveyardToBattlefieldEffect(false, false), new ManaCostsImpl("{B}{B}"), new IsStepCondition(PhaseStep.UPKEEP), null); ability.addEffect(new NimDevourerEffect()); @@ -64,7 +64,7 @@ class NimDevourerEffect extends OneShotEffect { public NimDevourerEffect() { super(Outcome.Sacrifice); - this.staticText = "then sacrifice a creature"; + this.staticText = ", then sacrifice a creature"; } public NimDevourerEffect(final NimDevourerEffect effect) { diff --git a/Mage.Sets/src/mage/cards/n/NimbleTrapfinder.java b/Mage.Sets/src/mage/cards/n/NimbleTrapfinder.java index 92ab23debcb9..062272e7a665 100644 --- a/Mage.Sets/src/mage/cards/n/NimbleTrapfinder.java +++ b/Mage.Sets/src/mage/cards/n/NimbleTrapfinder.java @@ -119,7 +119,7 @@ public void watch(GameEvent event, Game game) { return; } Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent == null || !filter.match(permanent, game)) { + if (!filter.match(permanent, game)) { return; } playerMap diff --git a/Mage.Sets/src/mage/cards/n/NinthBridgePatrol.java b/Mage.Sets/src/mage/cards/n/NinthBridgePatrol.java index 2984e9745ba7..2919c9aa8122 100644 --- a/Mage.Sets/src/mage/cards/n/NinthBridgePatrol.java +++ b/Mage.Sets/src/mage/cards/n/NinthBridgePatrol.java @@ -13,7 +13,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/n/NissaOfShadowedBoughs.java b/Mage.Sets/src/mage/cards/n/NissaOfShadowedBoughs.java index c4b7f1443901..1b3a3667d46f 100644 --- a/Mage.Sets/src/mage/cards/n/NissaOfShadowedBoughs.java +++ b/Mage.Sets/src/mage/cards/n/NissaOfShadowedBoughs.java @@ -18,7 +18,7 @@ import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.custom.CreatureToken; @@ -105,7 +105,7 @@ class NissaOfShadowedBoughsCreatureEffect extends OneShotEffect { NissaOfShadowedBoughsCreatureEffect() { super(Outcome.Benefit); - staticText = "You may put a creature card with converted mana cost less than or equal to " + + staticText = "You may put a creature card with mana value less than or equal to " + "the number of lands you control onto the battlefield from your hand or graveyard " + "with two +1/+1 counters on it."; } @@ -129,8 +129,8 @@ public boolean apply(Game game, Ability source) { StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, source.getSourceId(), source.getControllerId(), game ); - FilterCard filter = new FilterCreatureCard("creature card with converted mana cost " + lands + " or less"); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, lands + 1)); + FilterCard filter = new FilterCreatureCard("creature card with mana value " + lands + " or less"); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, lands + 1)); int inHand = player.getHand().count(filter, game); int inGrave = player.getGraveyard().count(filter, game); if (inHand < 1 && inGrave < 1) { diff --git a/Mage.Sets/src/mage/cards/n/NissaStewardOfElements.java b/Mage.Sets/src/mage/cards/n/NissaStewardOfElements.java index eb9f16fb6b8c..60717a9fbcc7 100644 --- a/Mage.Sets/src/mage/cards/n/NissaStewardOfElements.java +++ b/Mage.Sets/src/mage/cards/n/NissaStewardOfElements.java @@ -21,7 +21,7 @@ import mage.filter.common.FilterControlledLandPermanent; import mage.filter.common.FilterPermanentCard; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.token.TokenImpl; import mage.players.Player; @@ -74,7 +74,7 @@ class NissaStewardOfElementsEffect extends OneShotEffect { public NissaStewardOfElementsEffect() { super(Outcome.PutCardInPlay); - this.staticText = "look at the top card of your library. If it's a land card or a creature card with converted mana cost less than or equal " + this.staticText = "look at the top card of your library. If it's a land card or a creature card with mana value less than or equal " + "to the number of loyalty counters on {this}, you may put that card onto the battlefield"; } @@ -97,7 +97,7 @@ public boolean apply(Game game, Ability source) { FilterPermanentCard filter = new FilterPermanentCard(); filter.add(Predicates.or(CardType.CREATURE.getPredicate(), CardType.LAND.getPredicate())); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, count)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, count)); Card card = controller.getLibrary().getFromTop(game); if (card != null) { controller.lookAtCards(source, null, new CardsImpl(card), game); diff --git a/Mage.Sets/src/mage/cards/n/NissaWorldwaker.java b/Mage.Sets/src/mage/cards/n/NissaWorldwaker.java index 63324a38013d..07bd0b2c8baf 100644 --- a/Mage.Sets/src/mage/cards/n/NissaWorldwaker.java +++ b/Mage.Sets/src/mage/cards/n/NissaWorldwaker.java @@ -70,7 +70,7 @@ class NissaWorldwakerSearchEffect extends OneShotEffect { public NissaWorldwakerSearchEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "Search your library for any number of basic land cards, put them onto the battlefield, then shuffle your library. Those lands become 4/4 Elemental creatures with trample. They're still lands"; + this.staticText = "Search your library for any number of basic land cards, put them onto the battlefield, then shuffle. Those lands become 4/4 Elemental creatures with trample. They're still lands"; } public NissaWorldwakerSearchEffect(final NissaWorldwakerSearchEffect effect) { diff --git a/Mage.Sets/src/mage/cards/n/NissasEncouragement.java b/Mage.Sets/src/mage/cards/n/NissasEncouragement.java index cd00e38e4919..dd3a79666376 100644 --- a/Mage.Sets/src/mage/cards/n/NissasEncouragement.java +++ b/Mage.Sets/src/mage/cards/n/NissasEncouragement.java @@ -52,7 +52,7 @@ class NissasEncouragementEffect extends OneShotEffect { public NissasEncouragementEffect() { super(Outcome.DrawCard); - this.staticText = "Search your library and graveyard for a card named Forest, a card named Brambleweft Behemoth, and a card named Nissa, Genesis Mage. Reveal those cards, put them into your hand, then shuffle your library."; + this.staticText = "Search your library and graveyard for a card named Forest, a card named Brambleweft Behemoth, and a card named Nissa, Genesis Mage. Reveal those cards, put them into your hand, then shuffle."; } public NissasEncouragementEffect(final NissasEncouragementEffect effect) { diff --git a/Mage.Sets/src/mage/cards/n/NissasExpedition.java b/Mage.Sets/src/mage/cards/n/NissasExpedition.java index 063b885449e9..7b11c3195f55 100644 --- a/Mage.Sets/src/mage/cards/n/NissasExpedition.java +++ b/Mage.Sets/src/mage/cards/n/NissasExpedition.java @@ -23,7 +23,7 @@ public NissasExpedition(UUID ownerId, CardSetInfo setInfo) { // Convoke this.addAbility(new ConvokeAbility()); // Search your library for up to two basic land cards, put them onto the battlefield tapped, then shuffle your library. - this.getSpellAbility().addEffect(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0,2, StaticFilters.FILTER_CARD_BASIC_LAND), true, true)); + this.getSpellAbility().addEffect(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0,2, StaticFilters.FILTER_CARD_BASIC_LANDS), true, true)); } private NissasExpedition(final NissasExpedition card) { diff --git a/Mage.Sets/src/mage/cards/n/NissasPilgrimage.java b/Mage.Sets/src/mage/cards/n/NissasPilgrimage.java index 634b31e2417b..f56b14ebddc2 100644 --- a/Mage.Sets/src/mage/cards/n/NissasPilgrimage.java +++ b/Mage.Sets/src/mage/cards/n/NissasPilgrimage.java @@ -56,7 +56,7 @@ class NissasPilgrimageEffect extends OneShotEffect { public NissasPilgrimageEffect() { super(Outcome.Benefit); - this.staticText = "Search your library for up to two basic Forest cards, reveal those cards, and put one onto the battlefield tapped and the rest into your hand. Then shuffle your library." + this.staticText = "Search your library for up to two basic Forest cards, reveal those cards, and put one onto the battlefield tapped and the rest into your hand. Then shuffle." + "
Spell Mastery — If there are two or more instant and/or sorcery cards in your graveyard, search your library for up to three basic Forest cards instead of two."; } diff --git a/Mage.Sets/src/mage/cards/n/NissasRenewal.java b/Mage.Sets/src/mage/cards/n/NissasRenewal.java index c3567a6b9360..488db93218e4 100644 --- a/Mage.Sets/src/mage/cards/n/NissasRenewal.java +++ b/Mage.Sets/src/mage/cards/n/NissasRenewal.java @@ -21,7 +21,7 @@ public NissasRenewal(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{5}{G}"); // Search your library for up to three basic land cards, put them onto the battlefield tapped, then shuffle your library. You gain 7 life. - this.getSpellAbility().addEffect(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 3, StaticFilters.FILTER_CARD_BASIC_LAND), true)); + this.getSpellAbility().addEffect(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 3, StaticFilters.FILTER_CARD_BASIC_LANDS), true)); this.getSpellAbility().addEffect(new GainLifeEffect(7)); } diff --git a/Mage.Sets/src/mage/cards/n/NissasTriumph.java b/Mage.Sets/src/mage/cards/n/NissasTriumph.java index 383ffeec2aab..97755fc6298f 100644 --- a/Mage.Sets/src/mage/cards/n/NissasTriumph.java +++ b/Mage.Sets/src/mage/cards/n/NissasTriumph.java @@ -46,7 +46,7 @@ public NissasTriumph(UUID ownerId, CardSetInfo setInfo) { new PermanentsOnTheBattlefieldCondition(filter2), "Search your library for up to two basic Forest cards. If you control a Nissa planeswalker, " + "instead search your library for up to three land cards. " + - "Reveal those cards, put them into your hand, then shuffle your library." + "Reveal those cards, put them into your hand, then shuffle." )); } diff --git a/Mage.Sets/src/mage/cards/n/NivixAerieOfTheFiremind.java b/Mage.Sets/src/mage/cards/n/NivixAerieOfTheFiremind.java index 21d3aca85915..8b1e107973d3 100644 --- a/Mage.Sets/src/mage/cards/n/NivixAerieOfTheFiremind.java +++ b/Mage.Sets/src/mage/cards/n/NivixAerieOfTheFiremind.java @@ -77,7 +77,7 @@ public boolean apply(Game game, Ability source) { Card card = library.getFromTop(game); if (card != null && controller.moveCardsToExile(card, source, game, true, source.getSourceId(), CardUtil.createObjectRealtedWindowTitle(source, game, null)) - && (card.isInstant() || card.isSorcery())) { + && card.isInstantOrSorcery()) { ContinuousEffect effect = new NivixAerieOfTheFiremindCanCastEffect(); effect.setTargetPointer(new FixedTarget(card.getId())); game.addEffect(effect, source); diff --git a/Mage.Sets/src/mage/cards/n/NivmagusElemental.java b/Mage.Sets/src/mage/cards/n/NivmagusElemental.java index 226bf4d82ce9..daf54a843cef 100644 --- a/Mage.Sets/src/mage/cards/n/NivmagusElemental.java +++ b/Mage.Sets/src/mage/cards/n/NivmagusElemental.java @@ -1,38 +1,29 @@ - package mage.cards.n; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.common.ExileFromStackCost; +import mage.abilities.costs.Cost; +import mage.abilities.costs.CostImpl; 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.constants.TargetController; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.CounterType; import mage.filter.FilterSpell; -import mage.filter.predicate.Predicates; +import mage.filter.common.FilterInstantOrSorcerySpell; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.players.Player; import mage.target.TargetSpell; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class NivmagusElemental extends CardImpl { - private static final FilterSpell filter = new FilterSpell("an instant or sorcery spell you control"); - - static { - filter.add(TargetController.YOU.getControllerPredicate()); - filter.add(Predicates.or( - CardType.INSTANT.getPredicate(), - CardType.SORCERY.getPredicate())); - } - public NivmagusElemental(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U/R}"); this.subtype.add(SubType.ELEMENTAL); @@ -41,9 +32,9 @@ public NivmagusElemental(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(2); // Exile an instant or sorcery spell you control: Put two +1/+1 counters on Nivmagus Elemental. (That spell won't resolve.) - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance(2)), new ExileFromStackCost(new TargetSpell(filter))); - this.addAbility(ability); - + this.addAbility(new SimpleActivatedAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance(2)), new NivmagusElementalCost() + )); } private NivmagusElemental(final NivmagusElemental card) { @@ -55,3 +46,56 @@ public NivmagusElemental copy() { return new NivmagusElemental(this); } } + +class NivmagusElementalCost extends CostImpl { + + private static final FilterSpell filter = new FilterInstantOrSorcerySpell("an instant or sorcery spell you control"); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + } + + NivmagusElementalCost() { + super(); + TargetSpell target = new TargetSpell(filter); + target.setNotTarget(true); + this.addTarget(target); + this.text = "Exile an instant or sorcery spell you control"; + } + + private NivmagusElementalCost(NivmagusElementalCost cost) { + super(cost); + } + + @Override + public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { + Player player = game.getPlayer(controllerId); + if (player == null) { + return false; + } + player.chooseTarget(Outcome.Exile, targets.get(0), source, game); + Spell spell = game.getSpell(targets.getFirstTarget()); + if (spell == null) { + return false; + } + String spellName = spell.getName(); + if (spell.isCopy()) { + game.getStack().remove(spell, game); + } else { + player.moveCards(spell, Zone.EXILED, source, game); + } + paid = true; + game.informPlayers(player.getLogName() + " exiles " + spellName + " (as costs)"); + return paid; + } + + @Override + public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { + return targets.canChoose(source.getSourceId(), controllerId, game); + } + + @Override + public NivmagusElementalCost copy() { + return new NivmagusElementalCost(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/NoRestForTheWicked.java b/Mage.Sets/src/mage/cards/n/NoRestForTheWicked.java index cdb4749d7eb5..d3c9163c0a2b 100644 --- a/Mage.Sets/src/mage/cards/n/NoRestForTheWicked.java +++ b/Mage.Sets/src/mage/cards/n/NoRestForTheWicked.java @@ -1,38 +1,40 @@ - package mage.cards.n; -import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; -import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +import mage.abilities.effects.common.ReturnToHandFromGraveyardAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.WatcherScope; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; -import mage.players.Player; -import mage.watchers.Watcher; +import mage.constants.TargetController; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.card.PutIntoGraveFromBattlefieldThisTurnPredicate; +import mage.watchers.common.CardsPutIntoGraveyardWatcher; -import java.util.ArrayList; -import java.util.List; import java.util.UUID; /** - * * @author anonymous */ public final class NoRestForTheWicked extends CardImpl { + private static final FilterCard filter = new FilterCreatureCard(); + + static { + filter.add(PutIntoGraveFromBattlefieldThisTurnPredicate.instance); + } + public NoRestForTheWicked(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}"); // Sacrifice No Rest for the Wicked: Return to your hand all creature cards in your graveyard that were put there from the battlefield this turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new NoRestForTheWickedEffect(), new SacrificeSourceCost()); - Watcher watcher = new NoRestForTheWickedWatcher(); - addAbility(ability, watcher); + this.addAbility(new SimpleActivatedAbility( + new ReturnToHandFromGraveyardAllEffect(filter, TargetController.YOU) + .setText("return to your hand all creature cards in your graveyard " + + "that were put there from the battlefield this turn"), + new SacrificeSourceCost() + ), new CardsPutIntoGraveyardWatcher()); } private NoRestForTheWicked(final NoRestForTheWicked card) { @@ -44,72 +46,3 @@ public NoRestForTheWicked copy() { return new NoRestForTheWicked(this); } } - -class NoRestForTheWickedEffect extends OneShotEffect { - - NoRestForTheWickedEffect() { - super(Outcome.Sacrifice); - staticText = "Return to your hand all creature cards in your graveyard that were put there from the battlefield this turn"; - } - - NoRestForTheWickedEffect(final NoRestForTheWickedEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - NoRestForTheWickedWatcher watcher = game.getState().getWatcher(NoRestForTheWickedWatcher.class); - Player controller = game.getPlayer(source.getControllerId()); - if (watcher != null && controller != null) { - Cards cardsToHand = new CardsImpl(); - for (UUID cardId : watcher.getCards()) { - Card c = game.getCard(cardId); - if (c != null) { - if (game.getState().getZone(cardId) == Zone.GRAVEYARD - && c.isCreature() - && c.isOwnedBy(source.getControllerId())) { - cardsToHand.add(c); - } - } - } - controller.moveCards(cardsToHand, Zone.HAND, source, game); - return true; - } - return false; - } - - @Override - public NoRestForTheWickedEffect copy() { - return new NoRestForTheWickedEffect(this); - - } -} - -class NoRestForTheWickedWatcher extends Watcher { - - public List getCards() { - return cards; - } - - private List cards; - - public NoRestForTheWickedWatcher() { - super(WatcherScope.GAME); - this.cards = new ArrayList<>(); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.ZONE_CHANGE - && ((ZoneChangeEvent) event).isDiesEvent()) { - //400.3 Intercept only the controller's events - cards.add(event.getTargetId()); - } - } - - @Override - public void reset() { - super.reset(); - cards.clear(); - } -} diff --git a/Mage.Sets/src/mage/cards/n/NobleBenefactor.java b/Mage.Sets/src/mage/cards/n/NobleBenefactor.java index ecf3ec74ce58..cfad9182412c 100644 --- a/Mage.Sets/src/mage/cards/n/NobleBenefactor.java +++ b/Mage.Sets/src/mage/cards/n/NobleBenefactor.java @@ -50,7 +50,7 @@ class NobleBenefactorEffect extends OneShotEffect { public NobleBenefactorEffect() { super(Outcome.Benefit); - this.staticText = "each player may search their library for a card and put that card into their hand. Then each player who searched their library this way shuffles it"; + this.staticText = "each player may search their library for a card and put that card into their hand. Then each player who searched their library this way shuffles"; } public NobleBenefactorEffect(final NobleBenefactorEffect effect) { diff --git a/Mage.Sets/src/mage/cards/n/NoblePurpose.java b/Mage.Sets/src/mage/cards/n/NoblePurpose.java index 44a79d138ec0..ff867527a5e3 100644 --- a/Mage.Sets/src/mage/cards/n/NoblePurpose.java +++ b/Mage.Sets/src/mage/cards/n/NoblePurpose.java @@ -55,9 +55,8 @@ public NoblePurposeTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE - || event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT + || event.getType() == GameEvent.EventType.DAMAGED_PLAYER; } @Override diff --git a/Mage.Sets/src/mage/cards/n/NoeticScales.java b/Mage.Sets/src/mage/cards/n/NoeticScales.java index 7030429f6487..a00ab1079773 100644 --- a/Mage.Sets/src/mage/cards/n/NoeticScales.java +++ b/Mage.Sets/src/mage/cards/n/NoeticScales.java @@ -1,23 +1,27 @@ - package mage.cards.n; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.Objects; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class NoeticScales extends CardImpl { @@ -26,7 +30,9 @@ public NoeticScales(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); // At the beginning of each player's upkeep, return to its owner's hand each creature that player controls with power greater than the number of cards in their hand. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new NoeticScalesEffect(), TargetController.ANY, false, true)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new NoeticScalesEffect(), TargetController.ANY, false + )); } private NoeticScales(final NoeticScales card) { @@ -41,12 +47,18 @@ public NoeticScales copy() { class NoeticScalesEffect extends OneShotEffect { - public NoeticScalesEffect() { + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); + + static { + filter.add(NoeticScalesPredicate.instance); + } + + NoeticScalesEffect() { super(Outcome.ReturnToHand); this.staticText = "return to its owner's hand each creature that player controls with power greater than the number of cards in their hand"; } - public NoeticScalesEffect(final NoeticScalesEffect effect) { + private NoeticScalesEffect(final NoeticScalesEffect effect) { super(effect); } @@ -57,19 +69,24 @@ public NoeticScalesEffect copy() { @Override public boolean apply(Game game, Ability source) { - FilterCreaturePermanent filter = new FilterCreaturePermanent(); - Player player = game.getPlayer(targetPointer.getFirst(game, source)); - if (player != null) { - int numberOfCardsInHand = player.getHand().size(); - for (Permanent creature : game.getBattlefield().getAllActivePermanents(filter, player.getId(), game)) { - if (creature.getPower().getValue() > numberOfCardsInHand) { - if (creature.moveToZone(Zone.HAND, source, game, false)) { - game.informPlayers(player.getLogName() + " moves " + creature.getLogName() + " from the battlefield to their hand."); - } - } - } - return true; + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; } - return false; + Cards cards = new CardsImpl(); + game.getBattlefield().getActivePermanents( + filter, game.getActivePlayerId(), source.getSourceId(), game + ).stream().filter(Objects::nonNull).forEach(cards::add); + return player.moveCards(cards, Zone.HAND, source, game); + } +} + +enum NoeticScalesPredicate implements Predicate { + instance; + + @Override + public boolean apply(Permanent input, Game game) { + Player player = game.getPlayer(input.getControllerId()); + return player != null && player.getHand().size() < input.getPower().getValue(); } } diff --git a/Mage.Sets/src/mage/cards/n/Norritt.java b/Mage.Sets/src/mage/cards/n/Norritt.java index 49e8b06a19f6..ae6f04d6f634 100644 --- a/Mage.Sets/src/mage/cards/n/Norritt.java +++ b/Mage.Sets/src/mage/cards/n/Norritt.java @@ -66,7 +66,7 @@ public Norritt(UUID ownerId, CardSetInfo setInfo) { new TapSourceCost(), BeforeAttackersAreDeclaredCondition.instance, "{T}: Choose target non-Wall creature the active player has controlled continuously since the beginning of the turn. " + "That creature attacks this turn if able. If it doesn't, destroy it at the beginning of the next end step. " - + "Activate this ability only before attackers are declared."); + + "Activate only before attackers are declared."); ability2.addEffect(new NorrittDelayedDestroyEffect()); ability2.addTarget(new TargetCreaturePermanent(filterCreature)); this.addAbility(ability2, new AttackedThisTurnWatcher()); diff --git a/Mage.Sets/src/mage/cards/n/NostalgicDreams.java b/Mage.Sets/src/mage/cards/n/NostalgicDreams.java index 71d08bb68781..6d9aeed918e3 100644 --- a/Mage.Sets/src/mage/cards/n/NostalgicDreams.java +++ b/Mage.Sets/src/mage/cards/n/NostalgicDreams.java @@ -35,7 +35,7 @@ public NostalgicDreams(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().setTargetAdjuster(NostalgicDreamsAdjuster.instance); // Exile Nostalgic Dreams. - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private NostalgicDreams(final NostalgicDreams card) { diff --git a/Mage.Sets/src/mage/cards/n/NosyGoblin.java b/Mage.Sets/src/mage/cards/n/NosyGoblin.java index 4393397057d3..e535e7dbd935 100644 --- a/Mage.Sets/src/mage/cards/n/NosyGoblin.java +++ b/Mage.Sets/src/mage/cards/n/NosyGoblin.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.other.FaceDownPredicate; +import mage.filter.predicate.card.FaceDownPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/n/NotOfThisWorld.java b/Mage.Sets/src/mage/cards/n/NotOfThisWorld.java index edc5b0ed9699..6dd0c14a325b 100644 --- a/Mage.Sets/src/mage/cards/n/NotOfThisWorld.java +++ b/Mage.Sets/src/mage/cards/n/NotOfThisWorld.java @@ -13,7 +13,7 @@ import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; -import mage.filter.predicate.other.TargetsPermanentPredicate; +import mage.filter.predicate.mageobject.TargetsPermanentPredicate; import mage.game.Game; import mage.game.stack.StackObject; import mage.target.Target; diff --git a/Mage.Sets/src/mage/cards/n/NourishingShoal.java b/Mage.Sets/src/mage/cards/n/NourishingShoal.java index 9304ac772674..4e0885a86898 100644 --- a/Mage.Sets/src/mage/cards/n/NourishingShoal.java +++ b/Mage.Sets/src/mage/cards/n/NourishingShoal.java @@ -30,7 +30,7 @@ public NourishingShoal(UUID ownerId, CardSetInfo setInfo) { // You may exile a green card with converted mana cost X from your hand rather than pay Nourishing Shoal's mana cost. - FilterOwnedCard filter = new FilterOwnedCard("a green card with converted mana cost X from your hand"); + FilterOwnedCard filter = new FilterOwnedCard("a green card with mana value X from your hand"); filter.add(new ColorPredicate(ObjectColor.GREEN)); filter.add(Predicates.not(new CardIdPredicate(this.getId()))); // the exile cost can never be paid with the card itself this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(filter), true))); diff --git a/Mage.Sets/src/mage/cards/n/NovablastWurm.java b/Mage.Sets/src/mage/cards/n/NovablastWurm.java index 93ac7aa4485c..bf5c0426c502 100644 --- a/Mage.Sets/src/mage/cards/n/NovablastWurm.java +++ b/Mage.Sets/src/mage/cards/n/NovablastWurm.java @@ -11,7 +11,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/n/NoviceDissector.java b/Mage.Sets/src/mage/cards/n/NoviceDissector.java new file mode 100644 index 000000000000..24b7b2efc88a --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NoviceDissector.java @@ -0,0 +1,52 @@ +package mage.cards.n; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.target.common.TargetControlledPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NoviceDissector extends CardImpl { + + public NoviceDissector(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.TROLL); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // {1}, Sacrifice another creature: Put a +1/+1 counter on target creature. Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), new GenericManaCost(1) + ); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent( + StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE + ))); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private NoviceDissector(final NoviceDissector card) { + super(card); + } + + @Override + public NoviceDissector copy() { + return new NoviceDissector(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/NovijenHeartOfProgress.java b/Mage.Sets/src/mage/cards/n/NovijenHeartOfProgress.java index 24a0a4471efe..c3f1810b0ec8 100644 --- a/Mage.Sets/src/mage/cards/n/NovijenHeartOfProgress.java +++ b/Mage.Sets/src/mage/cards/n/NovijenHeartOfProgress.java @@ -52,7 +52,7 @@ class NovijenHeartOfProgressEffect extends OneShotEffect { public NovijenHeartOfProgressEffect() { super(Outcome.BoostCreature); - staticText = "put a +1/+1 counter on each creature that came into play this turn"; + staticText = "put a +1/+1 counter on each creature that entered the battlefield this turn"; } public NovijenHeartOfProgressEffect(final NovijenHeartOfProgressEffect effect) { diff --git a/Mage.Sets/src/mage/cards/n/NoxiousDragon.java b/Mage.Sets/src/mage/cards/n/NoxiousDragon.java index 32b4fe907b4d..457d9dcdb891 100644 --- a/Mage.Sets/src/mage/cards/n/NoxiousDragon.java +++ b/Mage.Sets/src/mage/cards/n/NoxiousDragon.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.constants.ComparisonType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCreaturePermanent; /** @@ -22,10 +22,10 @@ */ public final class NoxiousDragon extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with converted mana cost 3 or less"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with mana value 3 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public NoxiousDragon(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/n/NoxiousGearhulk.java b/Mage.Sets/src/mage/cards/n/NoxiousGearhulk.java index e97431a41618..8e7ef16071ac 100644 --- a/Mage.Sets/src/mage/cards/n/NoxiousGearhulk.java +++ b/Mage.Sets/src/mage/cards/n/NoxiousGearhulk.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.constants.Outcome; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/n/NyleasIntervention.java b/Mage.Sets/src/mage/cards/n/NyleasIntervention.java index 1fbe570745d3..ce5df91be867 100644 --- a/Mage.Sets/src/mage/cards/n/NyleasIntervention.java +++ b/Mage.Sets/src/mage/cards/n/NyleasIntervention.java @@ -62,7 +62,7 @@ class NyleasInterventionEffect extends OneShotEffect { NyleasInterventionEffect() { super(Outcome.Benefit); staticText = "search your library for up to X land cards, " + - "reveal them, put them into your hand, then shuffle your library"; + "reveal them, put them into your hand, then shuffle"; } private NyleasInterventionEffect(final NyleasInterventionEffect effect) { diff --git a/Mage.Sets/src/mage/cards/n/NyxHerald.java b/Mage.Sets/src/mage/cards/n/NyxHerald.java index 1508a3433343..e4ce874fcd28 100644 --- a/Mage.Sets/src/mage/cards/n/NyxHerald.java +++ b/Mage.Sets/src/mage/cards/n/NyxHerald.java @@ -14,7 +14,7 @@ import mage.constants.TargetController; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.mageobject.EnchantmentOrEnchantedPredicate; +import mage.filter.predicate.permanent.EnchantmentOrEnchantedPredicate; import mage.target.TargetPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/o/OathOfGhouls.java b/Mage.Sets/src/mage/cards/o/OathOfGhouls.java index 55a51f64242a..e692587c55a0 100644 --- a/Mage.Sets/src/mage/cards/o/OathOfGhouls.java +++ b/Mage.Sets/src/mage/cards/o/OathOfGhouls.java @@ -18,7 +18,7 @@ import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.ObjectSourcePlayer; import mage.filter.predicate.ObjectSourcePlayerPredicate; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.players.Player; import mage.target.Target; diff --git a/Mage.Sets/src/mage/cards/o/OathOfLieges.java b/Mage.Sets/src/mage/cards/o/OathOfLieges.java index acb552803189..643d754d1340 100644 --- a/Mage.Sets/src/mage/cards/o/OathOfLieges.java +++ b/Mage.Sets/src/mage/cards/o/OathOfLieges.java @@ -72,7 +72,7 @@ class OathOfLiegesEffect extends OneShotEffect { public OathOfLiegesEffect() { super(Outcome.Benefit); this.staticText = "that player chooses target player who controls more lands than they do and is their opponent. " - + "The first player may search their library for a basic land card, put that card onto the battlefield, then shuffle their library"; + + "The first player may search their library for a basic land card, put that card onto the battlefield, then shuffle"; } public OathOfLiegesEffect(final OathOfLiegesEffect effect) { @@ -88,7 +88,7 @@ public OathOfLiegesEffect copy() { public boolean apply(Game game, Ability source) { Player activePlayer = game.getPlayer(game.getActivePlayerId()); if (activePlayer != null) { - if (activePlayer.chooseUse(outcome, "Search your library for a basic land card, put that card onto the battlefield, then shuffle your library?", source, game)) { + if (activePlayer.chooseUse(outcome, "Search your library for a basic land card, put that card onto the battlefield, then shuffle?", source, game)) { Effect effect = new SearchLibraryPutInPlayTargetPlayerEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), false, false, Outcome.PutLandInPlay, true); effect.setTargetPointer(new FixedTarget(game.getActivePlayerId())); return effect.apply(game, source); diff --git a/Mage.Sets/src/mage/cards/o/OathOfLimDul.java b/Mage.Sets/src/mage/cards/o/OathOfLimDul.java index a343fed1d854..89699582bcf5 100644 --- a/Mage.Sets/src/mage/cards/o/OathOfLimDul.java +++ b/Mage.Sets/src/mage/cards/o/OathOfLimDul.java @@ -16,10 +16,9 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInHand; diff --git a/Mage.Sets/src/mage/cards/o/OathOfTeferi.java b/Mage.Sets/src/mage/cards/o/OathOfTeferi.java index b6ace0a7fd98..3919a67b1eed 100644 --- a/Mage.Sets/src/mage/cards/o/OathOfTeferi.java +++ b/Mage.Sets/src/mage/cards/o/OathOfTeferi.java @@ -12,7 +12,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/o/OathswornVampire.java b/Mage.Sets/src/mage/cards/o/OathswornVampire.java index 65a4b95ef53e..a8c856369405 100644 --- a/Mage.Sets/src/mage/cards/o/OathswornVampire.java +++ b/Mage.Sets/src/mage/cards/o/OathswornVampire.java @@ -1,29 +1,21 @@ - package mage.cards.o; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.Condition; -import mage.abilities.condition.common.YouGainedLifeCondition; +import mage.abilities.dynamicvalue.common.ControllerGotLifeCount; import mage.abilities.effects.AsThoughEffectImpl; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AsThoughEffectType; -import mage.constants.CardType; -import mage.constants.ComparisonType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.watchers.common.PlayerGainedLifeWatcher; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class OathswornVampire extends CardImpl { @@ -40,7 +32,9 @@ public OathswornVampire(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new EntersBattlefieldTappedAbility()); // You may cast Oathsworn Vampire from your graveyard if you gained life this turn. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new OathswornVampirePlayEffect()), new PlayerGainedLifeWatcher()); + this.addAbility(new SimpleStaticAbility( + Zone.ALL, new OathswornVampirePlayEffect() + ).addHint(ControllerGotLifeCount.getHint()), new PlayerGainedLifeWatcher()); } private OathswornVampire(final OathswornVampire card) { @@ -55,17 +49,13 @@ public OathswornVampire copy() { class OathswornVampirePlayEffect extends AsThoughEffectImpl { - private Condition condition; - - public OathswornVampirePlayEffect() { + OathswornVampirePlayEffect() { super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfGame, Outcome.Benefit); staticText = "You may cast {this} from your graveyard if you gained life this turn"; - condition = new YouGainedLifeCondition(ComparisonType.MORE_THAN, 0); } - public OathswornVampirePlayEffect(final OathswornVampirePlayEffect effect) { + private OathswornVampirePlayEffect(final OathswornVampirePlayEffect effect) { super(effect); - this.condition = effect.condition; } @Override @@ -80,13 +70,15 @@ public OathswornVampirePlayEffect copy() { @Override public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { - if (sourceId.equals(source.getSourceId()) && source.isControlledBy(affectedControllerId)) { - Card card = game.getCard(source.getSourceId()); - if (card != null && game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD) { - return condition.apply(game, source); - } + PlayerGainedLifeWatcher watcher = game.getState().getWatcher(PlayerGainedLifeWatcher.class); + if (watcher == null + || watcher.getLifeGained(source.getControllerId()) < 1 + || !sourceId.equals(source.getSourceId()) + || !source.isControlledBy(affectedControllerId)) { + return false; } - return false; + Card card = game.getCard(source.getSourceId()); + return card != null && game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD; } } diff --git a/Mage.Sets/src/mage/cards/o/OblivionRing.java b/Mage.Sets/src/mage/cards/o/OblivionRing.java index 30bdd31adbf7..ce970156068f 100644 --- a/Mage.Sets/src/mage/cards/o/OblivionRing.java +++ b/Mage.Sets/src/mage/cards/o/OblivionRing.java @@ -12,8 +12,7 @@ import mage.constants.CardType; import mage.constants.Zone; import mage.filter.common.FilterNonlandPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; -import mage.target.Target; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/o/OblivionSower.java b/Mage.Sets/src/mage/cards/o/OblivionSower.java index f0d47f4b8a13..0a09125e602c 100644 --- a/Mage.Sets/src/mage/cards/o/OblivionSower.java +++ b/Mage.Sets/src/mage/cards/o/OblivionSower.java @@ -18,8 +18,8 @@ import mage.filter.FilterCard; import mage.filter.common.FilterLandCard; import mage.filter.predicate.Predicates; -import mage.filter.predicate.other.FaceDownPredicate; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.FaceDownPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; diff --git a/Mage.Sets/src/mage/cards/o/OboshThePreypiercer.java b/Mage.Sets/src/mage/cards/o/OboshThePreypiercer.java index 8383689f013d..593d445aa4cb 100644 --- a/Mage.Sets/src/mage/cards/o/OboshThePreypiercer.java +++ b/Mage.Sets/src/mage/cards/o/OboshThePreypiercer.java @@ -54,7 +54,7 @@ enum OboshThePreypiercerCompanionCondition implements CompanionCondition { @Override public String getRule() { - return "Your starting deck contains only cards with odd converted mana costs and land cards."; + return "Your starting deck contains only cards with odd mana values and land cards."; } @Override @@ -62,7 +62,7 @@ public boolean isLegal(Set deck, int startingSize) { return deck .stream() .filter(card -> !card.isLand()) - .mapToInt(MageObject::getConvertedManaCost) + .mapToInt(MageObject::getManaValue) .map(i -> i % 2) .allMatch(i -> i == 1); } @@ -72,7 +72,7 @@ class OboshThePreypiercerEffect extends ReplacementEffectImpl { OboshThePreypiercerEffect() { super(Duration.WhileOnBattlefield, Outcome.Damage); - staticText = "If a source you control with an odd converted mana cost would deal damage " + + staticText = "If a source you control with an odd mana value would deal damage " + "to a permanent or player, it deals double that damage to that permanent or player instead."; } @@ -88,15 +88,14 @@ public OboshThePreypiercerEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType().equals(GameEvent.EventType.DAMAGE_PLAYER) - || event.getType().equals(GameEvent.EventType.DAMAGE_CREATURE) - || event.getType().equals(GameEvent.EventType.DAMAGE_PLANESWALKER); + || event.getType().equals(GameEvent.EventType.DAMAGED_PERMANENT); } @Override public boolean applies(GameEvent event, Ability source, Game game) { MageObject sourceObject = game.getObject(event.getSourceId()); return sourceObject != null - && sourceObject.getConvertedManaCost() % 2 == 1 + && sourceObject.getManaValue() % 2 == 1 && game.getControllerId(event.getSourceId()).equals(source.getControllerId()); } diff --git a/Mage.Sets/src/mage/cards/o/ObscuringAether.java b/Mage.Sets/src/mage/cards/o/ObscuringAether.java index ff216b52fc68..48cb358a8ec1 100644 --- a/Mage.Sets/src/mage/cards/o/ObscuringAether.java +++ b/Mage.Sets/src/mage/cards/o/ObscuringAether.java @@ -12,7 +12,7 @@ import mage.constants.Duration; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.other.FaceDownCastablePredicate; +import mage.filter.predicate.card.FaceDownCastablePredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/o/ObsessiveStitcher.java b/Mage.Sets/src/mage/cards/o/ObsessiveStitcher.java index 27a4904513f2..53b024c014d4 100644 --- a/Mage.Sets/src/mage/cards/o/ObsessiveStitcher.java +++ b/Mage.Sets/src/mage/cards/o/ObsessiveStitcher.java @@ -35,7 +35,7 @@ public ObsessiveStitcher(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawDiscardControllerEffect(), new TapSourceCost())); // {2}{U}{B}, {T}, Sacrifice Obsessive Stitcher: Return target creature card from your graveyard to the battlefield. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnFromGraveyardToBattlefieldTargetEffect(false, false), new ManaCostsImpl("{2}{U}{B}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnFromGraveyardToBattlefieldTargetEffect(), new ManaCostsImpl("{2}{U}{B}")); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); diff --git a/Mage.Sets/src/mage/cards/o/ObstinateFamiliar.java b/Mage.Sets/src/mage/cards/o/ObstinateFamiliar.java index 18a4d0e15b83..a22a881438c0 100644 --- a/Mage.Sets/src/mage/cards/o/ObstinateFamiliar.java +++ b/Mage.Sets/src/mage/cards/o/ObstinateFamiliar.java @@ -69,7 +69,7 @@ public boolean apply(Game game, Ability source) { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player you = game.getPlayer(source.getControllerId()); - if (you != null && you.chooseUse(Outcome.AIDontUseIt, "Would you like to skip drawing a card?", source, game)){ + if (you != null && you.chooseUse(Outcome.AIDontUseIt, "Skip this draw?", source, game)){ return true; } return false; diff --git a/Mage.Sets/src/mage/cards/o/OctaviaLivingThesis.java b/Mage.Sets/src/mage/cards/o/OctaviaLivingThesis.java new file mode 100644 index 000000000000..d18a3e8b43e2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OctaviaLivingThesis.java @@ -0,0 +1,69 @@ +package mage.cards.o; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.CardsInControllerGraveyardCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.common.continuous.SetPowerToughnessTargetEffect; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.WardAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.common.FilterInstantOrSorceryCard; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OctaviaLivingThesis extends CardImpl { + + private static final FilterCard filter = new FilterInstantOrSorceryCard("instant and/or sorcery cards"); + private static final Condition condition = new CardsInControllerGraveyardCondition(8, filter); + private static final Hint hint = new ValueHint( + "Instants and sorceries in your graveyard", new CardsInControllerGraveyardCount(filter) + ); + + public OctaviaLivingThesis(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{8}{U}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.OCTOPUS); + this.power = new MageInt(8); + this.toughness = new MageInt(8); + + // This spell costs {8} less to cast if you have eight or more instant and/or sorcery cards in your graveyard. + this.addAbility(new SimpleStaticAbility( + Zone.ALL, new SpellCostReductionSourceEffect(8, condition).setCanWorksOnStackOnly(true) + ).setRuleAtTheTop(true).addHint(hint)); + + // Ward {8} + this.addAbility(new WardAbility(new ManaCostsImpl<>("{8}"))); + + // Magecraft — Whenever you cast an instant or sorcery spell, target creature has base power and toughness 8/8 until end of turn. + Ability ability = new MagecraftAbility( + new SetPowerToughnessTargetEffect(8, 8, Duration.EndOfTurn) + ); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private OctaviaLivingThesis(final OctaviaLivingThesis card) { + super(card); + } + + @Override + public OctaviaLivingThesis copy() { + return new OctaviaLivingThesis(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/OggyarBattleSeer.java b/Mage.Sets/src/mage/cards/o/OggyarBattleSeer.java new file mode 100644 index 000000000000..1beee9d8abed --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OggyarBattleSeer.java @@ -0,0 +1,43 @@ +package mage.cards.o; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.keyword.HasteAbility; +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 OggyarBattleSeer extends CardImpl { + + public OggyarBattleSeer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{R}"); + + this.subtype.add(SubType.OGRE); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // {T}: Scry 1. + this.addAbility(new SimpleActivatedAbility(new ScryEffect(1), new TapSourceCost())); + } + + private OggyarBattleSeer(final OggyarBattleSeer card) { + super(card); + } + + @Override + public OggyarBattleSeer copy() { + return new OggyarBattleSeer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/OgreBattledriver.java b/Mage.Sets/src/mage/cards/o/OgreBattledriver.java index 49265ebae31f..d0ac16a8ead0 100644 --- a/Mage.Sets/src/mage/cards/o/OgreBattledriver.java +++ b/Mage.Sets/src/mage/cards/o/OgreBattledriver.java @@ -17,7 +17,7 @@ import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/o/OgreErrant.java b/Mage.Sets/src/mage/cards/o/OgreErrant.java index 683342b6b438..a48c0222e82f 100644 --- a/Mage.Sets/src/mage/cards/o/OgreErrant.java +++ b/Mage.Sets/src/mage/cards/o/OgreErrant.java @@ -11,7 +11,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.AttackingPredicate; import mage.target.TargetPermanent; diff --git a/Mage.Sets/src/mage/cards/o/OgreSlumlord.java b/Mage.Sets/src/mage/cards/o/OgreSlumlord.java index be893e3576db..3b9fe8553ee2 100644 --- a/Mage.Sets/src/mage/cards/o/OgreSlumlord.java +++ b/Mage.Sets/src/mage/cards/o/OgreSlumlord.java @@ -15,7 +15,7 @@ import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.permanent.token.RatToken; diff --git a/Mage.Sets/src/mage/cards/o/OjutaiExemplars.java b/Mage.Sets/src/mage/cards/o/OjutaiExemplars.java index 8ea50c9a902b..a59c40824b24 100644 --- a/Mage.Sets/src/mage/cards/o/OjutaiExemplars.java +++ b/Mage.Sets/src/mage/cards/o/OjutaiExemplars.java @@ -1,12 +1,9 @@ - package mage.cards.o; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.common.SpellCastControllerTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.TapTargetEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; @@ -15,29 +12,20 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterSpell; -import mage.filter.predicate.Predicates; +import mage.constants.*; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class OjutaiExemplars extends CardImpl { - private static final FilterSpell filter = new FilterSpell("a noncreature spell"); - - static { - filter.add(Predicates.not(CardType.CREATURE.getPredicate())); - } - public OjutaiExemplars(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}"); this.subtype.add(SubType.HUMAN); @@ -46,23 +34,22 @@ public OjutaiExemplars(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(4); // Whenever you cast a noncreature spell, choose one - Tap target creature; - Ability ability = new SpellCastControllerTriggeredAbility(new TapTargetEffect(), filter, false); + Ability ability = new SpellCastControllerTriggeredAbility( + new TapTargetEffect(), StaticFilters.FILTER_SPELL_A_NON_CREATURE, false + ); ability.addTarget(new TargetCreaturePermanent()); // Ojutai Exemplars gain first strike and lifelink until end of turn; - Mode mode = new Mode(); - Effect effect = new GainAbilitySourceEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn); - effect.setText("{this} gains first strike"); - mode.addEffect(effect); - Effect effect2 = new GainAbilitySourceEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn); - effect2.setText("and lifelink until end of turn"); - mode.addEffect(effect2); + Mode mode = new Mode(new GainAbilitySourceEffect( + FirstStrikeAbility.getInstance(), Duration.EndOfTurn + ).setText("{this} gains first strike")); + mode.addEffect(new GainAbilitySourceEffect( + LifelinkAbility.getInstance(), Duration.EndOfTurn + ).setText("and lifelink until end of turn")); ability.addMode(mode); // or Exile Ojutai Exemplars, then return it to the battlefield tapped under its owner's control. - mode = new Mode(); - mode.addEffect(new OjutaiExemplarsEffect()); - ability.addMode(mode); + ability.addMode(new Mode(new OjutaiExemplarsEffect())); this.addAbility(ability); } @@ -79,12 +66,12 @@ public OjutaiExemplars copy() { class OjutaiExemplarsEffect extends OneShotEffect { - public OjutaiExemplarsEffect() { + OjutaiExemplarsEffect() { super(Outcome.Neutral); this.staticText = "Exile {this}, then return it to the battlefield tapped under its owner's control"; } - public OjutaiExemplarsEffect(final OjutaiExemplarsEffect effect) { + private OjutaiExemplarsEffect(final OjutaiExemplarsEffect effect) { super(effect); } @@ -95,15 +82,14 @@ public OjutaiExemplarsEffect copy() { @Override public boolean apply(Game game, Ability source) { - Permanent ojutaiExemplars = game.getPermanent(source.getSourceId()); - if (ojutaiExemplars != null) { - if (ojutaiExemplars.moveToExile(source.getSourceId(), "Ojutai Exemplars", source, game)) { - Card card = game.getExile().getCard(source.getSourceId(), game); - if (card != null) { - return card.moveToZone(Zone.BATTLEFIELD, source, game, true); - } - } + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (player == null || permanent == null) { + return false; } - return false; + Card card = permanent.getMainCard(); + player.moveCards(permanent, Zone.EXILED, source, game); + player.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, true, null); + return true; } } diff --git a/Mage.Sets/src/mage/cards/o/OjutaisCommand.java b/Mage.Sets/src/mage/cards/o/OjutaisCommand.java index ab112a0081f5..e31d447dbc8f 100644 --- a/Mage.Sets/src/mage/cards/o/OjutaisCommand.java +++ b/Mage.Sets/src/mage/cards/o/OjutaisCommand.java @@ -14,7 +14,7 @@ import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetSpell; import mage.target.common.TargetCardInYourGraveyard; @@ -24,10 +24,10 @@ */ public final class OjutaisCommand extends CardImpl { - private static final FilterCard filter = new FilterCreatureCard("creature card with converted mana cost 2 or less from your graveyard"); + private static final FilterCard filter = new FilterCreatureCard("creature card with mana value 2 or less from your graveyard"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); } public OjutaisCommand(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/o/OkaunEyeOfChaos.java b/Mage.Sets/src/mage/cards/o/OkaunEyeOfChaos.java index b4c513c0969a..48dd0f65c392 100644 --- a/Mage.Sets/src/mage/cards/o/OkaunEyeOfChaos.java +++ b/Mage.Sets/src/mage/cards/o/OkaunEyeOfChaos.java @@ -22,7 +22,6 @@ public final class OkaunEyeOfChaos extends CardImpl { private static final DynamicValue sourcePower = new SourcePermanentPowerCount(); - private static final DynamicValue sourceToughness = new SourcePermanentToughnessValue(); public OkaunEyeOfChaos(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}"); @@ -43,7 +42,7 @@ public OkaunEyeOfChaos(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new WinsCoinFlipTriggeredAbility( new BoostSourceEffect( sourcePower, - sourceToughness, + SourcePermanentToughnessValue.getInstance(), Duration.EndOfTurn, true ).setText("double {this}'s power and toughness until end of turn") diff --git a/Mage.Sets/src/mage/cards/o/OldGrowthDryads.java b/Mage.Sets/src/mage/cards/o/OldGrowthDryads.java index 5507ed58921e..5fd968c11033 100644 --- a/Mage.Sets/src/mage/cards/o/OldGrowthDryads.java +++ b/Mage.Sets/src/mage/cards/o/OldGrowthDryads.java @@ -52,7 +52,7 @@ class OldGrowthDryadsEffect extends OneShotEffect { OldGrowthDryadsEffect() { super(Outcome.Detriment); - this.staticText = "each opponent may search their library for a basic land card, put it onto the battlefield tapped, then shuffle their library"; + this.staticText = "each opponent may search their library for a basic land card, put it onto the battlefield tapped, then shuffle"; } OldGrowthDryadsEffect(final OldGrowthDryadsEffect effect) { diff --git a/Mage.Sets/src/mage/cards/o/OldGrowthTroll.java b/Mage.Sets/src/mage/cards/o/OldGrowthTroll.java index c8c87815424b..b7d9bc0e3eeb 100644 --- a/Mage.Sets/src/mage/cards/o/OldGrowthTroll.java +++ b/Mage.Sets/src/mage/cards/o/OldGrowthTroll.java @@ -3,6 +3,7 @@ import mage.MageInt; import mage.Mana; import mage.abilities.Ability; +import mage.abilities.SpellAbility; import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; @@ -169,6 +170,10 @@ public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) troll.addSubType(game, SubType.AURA); break; case AbilityAddingRemovingEffects_6: + // Spell Ability can be null with clone effects (ex. Moritte) + if (troll.getSpellAbility() == null) { + troll.addAbility(new SpellAbility(null, null), source.getSourceId(), game); + } troll.getSpellAbility().getTargets().clear(); troll.getSpellAbility().getEffects().clear(); TargetPermanent auraTarget = new TargetPermanent(filter); diff --git a/Mage.Sets/src/mage/cards/o/OliviaVoldaren.java b/Mage.Sets/src/mage/cards/o/OliviaVoldaren.java index 0c8231ab56e7..10417115381b 100644 --- a/Mage.Sets/src/mage/cards/o/OliviaVoldaren.java +++ b/Mage.Sets/src/mage/cards/o/OliviaVoldaren.java @@ -20,7 +20,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.CardIdPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/o/OmnispellAdept.java b/Mage.Sets/src/mage/cards/o/OmnispellAdept.java index 36f89272515b..d012b3739a71 100644 --- a/Mage.Sets/src/mage/cards/o/OmnispellAdept.java +++ b/Mage.Sets/src/mage/cards/o/OmnispellAdept.java @@ -97,7 +97,7 @@ public boolean apply(Game game, Ability source) { } realFilter.add(Predicates.not(new CardIdPredicate(cardToCast.getId()))); // remove card from choose dialog (infinite fix) - if (!cardToCast.getSpellAbility().canChooseTarget(game)) { + if (!cardToCast.getSpellAbility().canChooseTarget(game, controller.getId())) { continue; } diff --git a/Mage.Sets/src/mage/cards/o/OnceAndFuture.java b/Mage.Sets/src/mage/cards/o/OnceAndFuture.java index f1fd695ac437..792d329a4aa0 100644 --- a/Mage.Sets/src/mage/cards/o/OnceAndFuture.java +++ b/Mage.Sets/src/mage/cards/o/OnceAndFuture.java @@ -85,17 +85,17 @@ public boolean apply(Game game, Ability source) { } if (card2 == null) { player.putInHand(card1, game); - return ExileSpellEffect.getInstance().apply(game, source); + return new ExileSpellEffect().apply(game, source); } if (AdamantCondition.GREEN.apply(game, source)) { Cards cards = new CardsImpl(); cards.add(card1); cards.add(card2); player.moveCards(cards, Zone.HAND, source, game); - return ExileSpellEffect.getInstance().apply(game, source); + return new ExileSpellEffect().apply(game, source); } player.putInHand(card1, game); player.putCardsOnTopOfLibrary(new CardsImpl(card2), game, source, false); - return ExileSpellEffect.getInstance().apply(game, source); + return new ExileSpellEffect().apply(game, source); } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/o/OnceMoreWithFeeling.java b/Mage.Sets/src/mage/cards/o/OnceMoreWithFeeling.java index e3cdd7d4bd74..41d27143858b 100644 --- a/Mage.Sets/src/mage/cards/o/OnceMoreWithFeeling.java +++ b/Mage.Sets/src/mage/cards/o/OnceMoreWithFeeling.java @@ -34,7 +34,7 @@ public OnceMoreWithFeeling(UUID ownerId, CardSetInfo setInfo) { effect.setText(", then draws seven cards"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addEffect(new SetPlayerLifeAllEffect(10)); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); // DCI ruling — A deck can have only one card named Once More with Feeling. // (according to rule 112.6m, this shouldn't do anything) diff --git a/Mage.Sets/src/mage/cards/o/OnceUponATime.java b/Mage.Sets/src/mage/cards/o/OnceUponATime.java index 39be09ca8c66..52ef52fb3b85 100644 --- a/Mage.Sets/src/mage/cards/o/OnceUponATime.java +++ b/Mage.Sets/src/mage/cards/o/OnceUponATime.java @@ -84,13 +84,18 @@ class OnceUponATimeWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { - if (GameEvent.EventType.SPELL_CAST == event.getType()) { - castSpells.add(event.getPlayerId()); + switch (event.getType()) { + case SPELL_CAST: + castSpells.add(event.getPlayerId()); + return; + case BEGINNING_PHASE_PRE: + if (game.getTurnNum() == 1) { + castSpells.clear(); + } } } - public boolean getSpellsCastThisGame(UUID playerId) { + boolean getSpellsCastThisGame(UUID playerId) { return !castSpells.contains(playerId); } - } diff --git a/Mage.Sets/src/mage/cards/o/OneOfThePack.java b/Mage.Sets/src/mage/cards/o/OneOfThePack.java index 9eb99a843d0c..9a47b281d509 100644 --- a/Mage.Sets/src/mage/cards/o/OneOfThePack.java +++ b/Mage.Sets/src/mage/cards/o/OneOfThePack.java @@ -1,28 +1,21 @@ - package mage.cards.o; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; -import mage.abilities.keyword.TransformAbility; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class OneOfThePack extends CardImpl { public OneOfThePack(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.WEREWOLF); this.power = new MageInt(5); this.toughness = new MageInt(6); @@ -32,8 +25,7 @@ public OneOfThePack(UUID ownerId, CardSetInfo setInfo) { this.transformable = true; // At the beginning of each upkeep, if a player cast two or more spells last turn, transform One of the Pack. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private OneOfThePack(final OneOfThePack card) { diff --git a/Mage.Sets/src/mage/cards/o/OneWithNature.java b/Mage.Sets/src/mage/cards/o/OneWithNature.java index 439e14eb13c3..652ff85e272f 100644 --- a/Mage.Sets/src/mage/cards/o/OneWithNature.java +++ b/Mage.Sets/src/mage/cards/o/OneWithNature.java @@ -39,7 +39,7 @@ public OneWithNature(UUID ownerId, CardSetInfo setInfo) { // Whenever enchanted creature deals combat damage to a player, you may search your library for a basic land card, put that card onto the battlefield tapped, then shuffle your library. ability = new DealsDamageToAPlayerAttachedTriggeredAbility( new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true, Outcome.PutLandInPlay) - .setText("you may search your library for a basic land card, put that card onto the battlefield tapped, then shuffle your library."), + .setText("you may search your library for a basic land card, put that card onto the battlefield tapped, then shuffle."), "enchanted creature", true, false, true, TargetController.ANY); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/o/OneWithTheMachine.java b/Mage.Sets/src/mage/cards/o/OneWithTheMachine.java index 13f6004261e7..ab33ed550387 100644 --- a/Mage.Sets/src/mage/cards/o/OneWithTheMachine.java +++ b/Mage.Sets/src/mage/cards/o/OneWithTheMachine.java @@ -1,7 +1,7 @@ package mage.cards.o; import java.util.UUID; -import mage.abilities.dynamicvalue.common.HighestConvertedManaCostValue; +import mage.abilities.dynamicvalue.common.HighestManaValueCount; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -19,8 +19,8 @@ public OneWithTheMachine(UUID ownerId, CardSetInfo setInfo) { // Draw cards equal to the highest converted mana cost among artifacts you control. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect( - new HighestConvertedManaCostValue(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT) - ).setText("Draw cards equal to the highest converted mana cost among artifacts you control")); + new HighestManaValueCount(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT) + ).setText("Draw cards equal to the highest mana value among artifacts you control")); } private OneWithTheMachine(final OneWithTheMachine card) { diff --git a/Mage.Sets/src/mage/cards/o/OpalAvenger.java b/Mage.Sets/src/mage/cards/o/OpalAvenger.java index 1552029b914d..6484a5527b9c 100644 --- a/Mage.Sets/src/mage/cards/o/OpalAvenger.java +++ b/Mage.Sets/src/mage/cards/o/OpalAvenger.java @@ -75,10 +75,10 @@ public boolean canTrigger(Game game) { } @Override - public void trigger(Game game, UUID controllerId) { + public void trigger(Game game, UUID controllerId, GameEvent triggeringEvent) { //20100716 - 603.8 game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.TRUE); - super.trigger(game, controllerId); + super.trigger(game, controllerId, triggeringEvent); } @Override diff --git a/Mage.Sets/src/mage/cards/o/OpalEyeKondasYojimbo.java b/Mage.Sets/src/mage/cards/o/OpalEyeKondasYojimbo.java index 62807a8286ad..012724a3cc69 100644 --- a/Mage.Sets/src/mage/cards/o/OpalEyeKondasYojimbo.java +++ b/Mage.Sets/src/mage/cards/o/OpalEyeKondasYojimbo.java @@ -83,8 +83,7 @@ public void init(Ability source, Game game) { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE || - event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER || + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT || event.getType() == GameEvent.EventType.DAMAGE_PLAYER; } diff --git a/Mage.Sets/src/mage/cards/o/Opalescence.java b/Mage.Sets/src/mage/cards/o/Opalescence.java index 17bd99dfef37..1d897919a300 100644 --- a/Mage.Sets/src/mage/cards/o/Opalescence.java +++ b/Mage.Sets/src/mage/cards/o/Opalescence.java @@ -9,7 +9,7 @@ import mage.constants.*; import mage.filter.common.FilterEnchantmentPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; @@ -51,7 +51,7 @@ static class OpalescenceEffect extends ContinuousEffectImpl { public OpalescenceEffect() { super(Duration.WhileOnBattlefield, Outcome.BecomeCreature); staticText = "Each other non-Aura enchantment is a creature in addition to its other " - + "types and has base power and base toughness each equal to its converted mana cost"; + + "types and has base power and base toughness each equal to its mana value"; this.dependendToTypes.add(DependencyType.EnchantmentAddingRemoving); // Enchanted Evening this.dependendToTypes.add(DependencyType.AuraAddingRemoving); // Cloudform @@ -83,7 +83,7 @@ public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) case PTChangingEffects_7: if (sublayer == SubLayer.SetPT_7b) { - int manaCost = permanent.getConvertedManaCost(); + int manaCost = permanent.getManaValue(); permanent.getPower().setValue(manaCost); permanent.getToughness().setValue(manaCost); } diff --git a/Mage.Sets/src/mage/cards/o/OpalineSliver.java b/Mage.Sets/src/mage/cards/o/OpalineSliver.java index fc776c9c24c5..ae353054e009 100644 --- a/Mage.Sets/src/mage/cards/o/OpalineSliver.java +++ b/Mage.Sets/src/mage/cards/o/OpalineSliver.java @@ -15,6 +15,7 @@ import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; @@ -56,8 +57,6 @@ public OpalineSliver copy() { class OpalineSliverTriggeredAbility extends TriggeredAbilityImpl { - private static final FilterSpell spellCard = new FilterSpell("a spell"); - public OpalineSliverTriggeredAbility() { super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), false); } @@ -85,7 +84,7 @@ public boolean checkTrigger(GameEvent event, Game game) { } else { return event.getTargetId().equals(this.getSourceId()) && game.getOpponents(this.controllerId).contains(event.getPlayerId()) - && spellCard.match(spell, getSourceId(), getControllerId(), game); + && StaticFilters.FILTER_SPELL_A.match(spell, getSourceId(), getControllerId(), game); } } diff --git a/Mage.Sets/src/mage/cards/o/OppositionAgent.java b/Mage.Sets/src/mage/cards/o/OppositionAgent.java index 16f8f14accd4..a6c7466669c0 100644 --- a/Mage.Sets/src/mage/cards/o/OppositionAgent.java +++ b/Mage.Sets/src/mage/cards/o/OppositionAgent.java @@ -101,7 +101,7 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { // You may play those cards for as long as they remain exiled, and you may spend mana as though it were mana of any color to cast them for (Card card : cardsToExile) { - CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.Custom); + CardUtil.makeCardPlayable(game, source, card, Duration.Custom, true); } // return false all the time diff --git a/Mage.Sets/src/mage/cards/o/Opt.java b/Mage.Sets/src/mage/cards/o/Opt.java index c1915a77b497..4d6c546724e4 100644 --- a/Mage.Sets/src/mage/cards/o/Opt.java +++ b/Mage.Sets/src/mage/cards/o/Opt.java @@ -21,7 +21,7 @@ public Opt(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new ScryEffect(1)); // Draw a card. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); } private Opt(final Opt card) { diff --git a/Mage.Sets/src/mage/cards/o/OracleOfBones.java b/Mage.Sets/src/mage/cards/o/OracleOfBones.java index d6877e43115e..e101d727b324 100644 --- a/Mage.Sets/src/mage/cards/o/OracleOfBones.java +++ b/Mage.Sets/src/mage/cards/o/OracleOfBones.java @@ -95,7 +95,7 @@ public boolean apply(Game game, Ability source) { if (controller.chooseTarget(outcome, target, source, game)) { cardToCast = game.getCard(target.getFirstTarget()); if (cardToCast != null - && cardToCast.getSpellAbility().canChooseTarget(game)) { + && cardToCast.getSpellAbility().canChooseTarget(game, controller.getId())) { cancel = true; } } else { diff --git a/Mage.Sets/src/mage/cards/o/OracleOfMulDaya.java b/Mage.Sets/src/mage/cards/o/OracleOfMulDaya.java index dcebbc1b0612..3e077c7696a2 100644 --- a/Mage.Sets/src/mage/cards/o/OracleOfMulDaya.java +++ b/Mage.Sets/src/mage/cards/o/OracleOfMulDaya.java @@ -20,7 +20,7 @@ */ public final class OracleOfMulDaya extends CardImpl { - private static final FilterCard filter = new FilterLandCard("play land cards"); + private static final FilterCard filter = new FilterLandCard("play lands"); public OracleOfMulDaya(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); @@ -38,8 +38,8 @@ public OracleOfMulDaya(UUID ownerId, CardSetInfo setInfo) { // Play with the top card of your library revealed. this.addAbility(new SimpleStaticAbility(new PlayWithTheTopCardRevealedEffect())); - // You may play the top card of your library if it's a land card. - this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter))); + // You may play lands from the top of your library. + this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false))); } private OracleOfMulDaya(final OracleOfMulDaya card) { diff --git a/Mage.Sets/src/mage/cards/o/OraclesAttendants.java b/Mage.Sets/src/mage/cards/o/OraclesAttendants.java index 186e2653c90a..b0dca23a9e7c 100644 --- a/Mage.Sets/src/mage/cards/o/OraclesAttendants.java +++ b/Mage.Sets/src/mage/cards/o/OraclesAttendants.java @@ -15,7 +15,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamageCreatureEvent; +import mage.game.events.DamageEvent; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; @@ -80,7 +80,7 @@ public void init(Ability source, Game game) { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Permanent permanent = game.getPermanent(source.getSourceId()); - DamageCreatureEvent damageEvent = (DamageCreatureEvent) event; + DamageEvent damageEvent = (DamageEvent) event; if (permanent != null) { permanent.damage(damageEvent.getAmount(), damageEvent.getSourceId(), source, game, damageEvent.isCombatDamage(), damageEvent.isPreventable()); return true; @@ -90,7 +90,7 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE; + return event.getType() == EventType.DAMAGE_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/o/OraclesVault.java b/Mage.Sets/src/mage/cards/o/OraclesVault.java index ceb1b330201f..5802c4b58472 100644 --- a/Mage.Sets/src/mage/cards/o/OraclesVault.java +++ b/Mage.Sets/src/mage/cards/o/OraclesVault.java @@ -42,7 +42,7 @@ public OraclesVault(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new ConditionalActivatedAbility(Zone.BATTLEFIELD, new OraclesVaultFreeEffect(), new TapSourceCost(), new SourceHasCounterCondition(CounterType.BRICK, 3, Integer.MAX_VALUE), "{T}: Exile the top card of your library. Until end of turn, you may play that card without paying its mana cost. " - + "Activate this ability only if there are three or more brick counters on {this}")); + + "Activate only if there are three or more brick counters on {this}")); } private OraclesVault(final OraclesVault card) { diff --git a/Mage.Sets/src/mage/cards/o/OrahSkyclaveHierophant.java b/Mage.Sets/src/mage/cards/o/OrahSkyclaveHierophant.java index 84b81c348315..7d4f8c3931cd 100644 --- a/Mage.Sets/src/mage/cards/o/OrahSkyclaveHierophant.java +++ b/Mage.Sets/src/mage/cards/o/OrahSkyclaveHierophant.java @@ -8,7 +8,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; @@ -73,11 +73,11 @@ public boolean checkTrigger(GameEvent event, Game game) { return false; } FilterCard filterCard = new FilterCard( - "Cleric card with converted mana cost less than " + (zEvent.getTarget().getConvertedManaCost()) + "Cleric card with mana value less than " + (zEvent.getTarget().getManaValue()) ); filterCard.add(SubType.CLERIC.getPredicate()); - filterCard.add(new ConvertedManaCostPredicate( - ComparisonType.FEWER_THAN, zEvent.getTarget().getConvertedManaCost() + filterCard.add(new ManaValuePredicate( + ComparisonType.FEWER_THAN, zEvent.getTarget().getManaValue() )); this.getTargets().clear(); this.addTarget(new TargetCardInYourGraveyard(filterCard)); @@ -92,6 +92,6 @@ public OrahSkyclaveHierophantTriggeredAbility copy() { @Override public String getRule() { return "Whenever {this} or another Cleric you control dies, return target Cleric card " + - "with lesser converted mana cost from your graveyard to the battlefield."; + "with lesser mana value from your graveyard to the battlefield."; } } diff --git a/Mage.Sets/src/mage/cards/o/OratorOfOjutai.java b/Mage.Sets/src/mage/cards/o/OratorOfOjutai.java index 4e511f34ce01..a2bf9f231498 100644 --- a/Mage.Sets/src/mage/cards/o/OratorOfOjutai.java +++ b/Mage.Sets/src/mage/cards/o/OratorOfOjutai.java @@ -1,40 +1,26 @@ - package mage.cards.o; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.common.RevealTargetFromHandCost; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.InfoEffect; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.common.RevealedOrControlledDragonCondition; +import mage.abilities.costs.common.RevealDragonFromHandCost; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.DefenderAbility; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.FilterCard; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.common.TargetCardInHand; +import mage.constants.CardType; +import mage.constants.SubType; import mage.watchers.common.DragonOnTheBattlefieldWhileSpellWasCastWatcher; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class OratorOfOjutai extends CardImpl { - private static final FilterCard filter = new FilterCard("a Dragon card from your hand (you don't have to)"); - - static { - filter.add(SubType.DRAGON.getPredicate()); - } - public OratorOfOjutai(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); this.subtype.add(SubType.BIRD); @@ -47,22 +33,14 @@ public OratorOfOjutai(UUID ownerId, CardSetInfo setInfo) { this.addAbility(FlyingAbility.getInstance()); // As an additional cost to cast Orator of Ojutai, you may reveal a Dragon card from your hand. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new InfoEffect("as an additional cost to cast this spell, you may reveal a Dragon card from your hand"))); + this.getSpellAbility().addCost(new RevealDragonFromHandCost()); // When Orator of Ojutai enters the battlefield, if you revealed a Dragon card or controlled a Dragon as you cast Orator of Ojutai, draw a card. - this.addAbility(new OratorOfOjutaiTriggeredAbility(new OratorOfOjutaiEffect()), new DragonOnTheBattlefieldWhileSpellWasCastWatcher()); - } - - @Override - public void adjustCosts(Ability ability, Game game) { - if (ability.getAbilityType() == AbilityType.SPELL) { - Player controller = game.getPlayer(ability.getControllerId()); - if (controller != null) { - if (controller.getHand().count(filter, game) > 0) { - ability.addCost(new RevealTargetFromHandCost(new TargetCardInHand(0, 1, filter))); - } - } - } + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1)), + RevealedOrControlledDragonCondition.instance, "When {this} enters the battlefield, " + + "if you revealed a Dragon card or controlled a Dragon as you cast this spell, draw a card." + ), new DragonOnTheBattlefieldWhileSpellWasCastWatcher()); } private OratorOfOjutai(final OratorOfOjutai card) { @@ -74,73 +52,3 @@ public OratorOfOjutai copy() { return new OratorOfOjutai(this); } } - -class OratorOfOjutaiTriggeredAbility extends TriggeredAbilityImpl { - - public OratorOfOjutaiTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect, false); - } - - public OratorOfOjutaiTriggeredAbility(final OratorOfOjutaiTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - //Intervening if must be checked - Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(getSourceId()); - DragonOnTheBattlefieldWhileSpellWasCastWatcher watcher = game.getState().getWatcher(DragonOnTheBattlefieldWhileSpellWasCastWatcher.class); - return event.getTargetId().equals(getSourceId()) - && watcher != null - && watcher.castWithConditionTrue(sourcePermanent.getSpellAbility().getId()); - } - - @Override - public String getRule() { - return "When {this} enters the battlefield, " + super.getRule(); - } - - @Override - public OratorOfOjutaiTriggeredAbility copy() { - return new OratorOfOjutaiTriggeredAbility(this); - } -} - -class OratorOfOjutaiEffect extends OneShotEffect { - - public OratorOfOjutaiEffect() { - super(Outcome.Benefit); - this.staticText = "If you revealed a Dragon card or controlled a Dragon as you cast {this}, draw a card"; - } - - public OratorOfOjutaiEffect(final OratorOfOjutaiEffect effect) { - super(effect); - } - - @Override - public OratorOfOjutaiEffect copy() { - return new OratorOfOjutaiEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - //Intervening if is checked again on resolution - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (sourcePermanent != null) { - DragonOnTheBattlefieldWhileSpellWasCastWatcher watcher = game.getState().getWatcher(DragonOnTheBattlefieldWhileSpellWasCastWatcher.class); - if (watcher != null && watcher.castWithConditionTrue(sourcePermanent.getSpellAbility().getId())) { - controller.drawCards(1, source, game); - return true; - } - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/o/OrcGeneral.java b/Mage.Sets/src/mage/cards/o/OrcGeneral.java index 7a2c83e0ccf1..84c5db9317fd 100644 --- a/Mage.Sets/src/mage/cards/o/OrcGeneral.java +++ b/Mage.Sets/src/mage/cards/o/OrcGeneral.java @@ -17,7 +17,7 @@ import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledPermanent; /** diff --git a/Mage.Sets/src/mage/cards/o/OrcSureshot.java b/Mage.Sets/src/mage/cards/o/OrcSureshot.java index a35f289aec91..6e8e7de87fd6 100644 --- a/Mage.Sets/src/mage/cards/o/OrcSureshot.java +++ b/Mage.Sets/src/mage/cards/o/OrcSureshot.java @@ -15,7 +15,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/o/OrchardElemental.java b/Mage.Sets/src/mage/cards/o/OrchardElemental.java index ff7573f3ca09..11006f316f58 100644 --- a/Mage.Sets/src/mage/cards/o/OrchardElemental.java +++ b/Mage.Sets/src/mage/cards/o/OrchardElemental.java @@ -1,38 +1,38 @@ - package mage.cards.o; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.CouncilsDilemmaVoteEffect; -import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.choices.TwoChoiceVote; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** - * - * @author JRHerlehy + * @author JRHerlehy, TheElk801 */ public final class OrchardElemental extends CardImpl { public OrchardElemental(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{G}"); - + this.subtype.add(SubType.ELEMENTAL); this.power = new MageInt(2); this.toughness = new MageInt(2); // Council's dilemma &mdash When Orchard Elemental enters the battlefield, starting with you, each player votes for sprout or harvest. Put two +1/+1 counters on Orchard Elemental for each sprout vote. You gain 3 life for each harvest vote. - this.addAbility(new EntersBattlefieldTriggeredAbility(new OrchardElementalDilemmaEffect(), false, "Council's dilemma — ")); + this.addAbility(new EntersBattlefieldTriggeredAbility( + new OrchardElementalEffect(), false, "Council's dilemma — " + )); } private OrchardElemental(final OrchardElemental card) { @@ -45,43 +45,42 @@ public OrchardElemental copy() { } } -class OrchardElementalDilemmaEffect extends CouncilsDilemmaVoteEffect { +class OrchardElementalEffect extends OneShotEffect { - public OrchardElementalDilemmaEffect() { + OrchardElementalEffect() { super(Outcome.Benefit); - this.staticText = "starting with you, each player votes for sprout or harvest. Put two +1/+1 counters on Orchard Elemental for each sprout vote. You gain 3 life for each harvest vote"; + staticText = "starting with you, each player votes for sprout or harvest. " + + "Put two +1/+1 counters on {this} for each sprout vote. You gain 3 life for each harvest vote"; } - public OrchardElementalDilemmaEffect(final OrchardElementalDilemmaEffect effect) { + private OrchardElementalEffect(final OrchardElementalEffect effect) { super(effect); } @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - - if (controller == null) return false; - - this.vote("sprout", "harvest", controller, game, source); - - Permanent permanent = game.getPermanent(source.getSourceId()); - - //Sprout Votes - //If sprout received zero votes or the permanent is no longer on the battlefield, do not attempt to put P1P1 counter on it. - if (voteOneCount > 0 && permanent != null) - permanent.addCounters(CounterType.P1P1.createInstance(voteOneCount * 2), source.getControllerId(), source, game); + public OrchardElementalEffect copy() { + return new OrchardElementalEffect(this); + } - //Harvest Votes - if (voteTwoCount > 0) { - Effect gainLifeEffect = new GainLifeEffect(voteTwoCount * 3); - gainLifeEffect.apply(game, source); + @Override + public boolean apply(Game game, Ability source) { + // Outcome.Benefit - AI will boost all the time (Sprout choice) + // TODO: add AI hint logic in the choice method, see Tyrant's Choice as example + TwoChoiceVote vote = new TwoChoiceVote("Sprout (two +1/+1 counters)", "Harvest (3 life)", Outcome.Benefit); + vote.doVotes(source, game); + + int sproutCount = vote.getVoteCount(true); + int harvestCount = vote.getVoteCount(false); + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (sproutCount > 0 && permanent != null) { + permanent.addCounters(CounterType.P1P1.createInstance(2 * sproutCount), source.getControllerId(), source, game); } - return true; - } + Player player = game.getPlayer(source.getControllerId()); + if (harvestCount > 0 && player != null) { + player.gainLife(3 * harvestCount, game, source); + } - @Override - public OrchardElementalDilemmaEffect copy() { - return new OrchardElementalDilemmaEffect(this); + return sproutCount + harvestCount > 0; } } diff --git a/Mage.Sets/src/mage/cards/o/OrchardWarden.java b/Mage.Sets/src/mage/cards/o/OrchardWarden.java index e5be96fc0a74..6db2d014303c 100644 --- a/Mage.Sets/src/mage/cards/o/OrchardWarden.java +++ b/Mage.Sets/src/mage/cards/o/OrchardWarden.java @@ -10,7 +10,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/o/OrdealOfNylea.java b/Mage.Sets/src/mage/cards/o/OrdealOfNylea.java index 910b4d6d20b0..387a35867336 100644 --- a/Mage.Sets/src/mage/cards/o/OrdealOfNylea.java +++ b/Mage.Sets/src/mage/cards/o/OrdealOfNylea.java @@ -49,7 +49,7 @@ public OrdealOfNylea(UUID ownerId, CardSetInfo setInfo) { this.addAbility(ability); // When you sacrifice Ordeal of Nylea, search your library for up to two basic land cards, put them onto the battlefield tapped, then shuffle your library. ability = new SacrificeSourceTriggeredAbility( - new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0,2, StaticFilters.FILTER_CARD_BASIC_LAND),true, true),false); + new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0,2, StaticFilters.FILTER_CARD_BASIC_LANDS),true, true),false); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/o/OrderOfWhiteclay.java b/Mage.Sets/src/mage/cards/o/OrderOfWhiteclay.java index efd488380379..7331784c1583 100644 --- a/Mage.Sets/src/mage/cards/o/OrderOfWhiteclay.java +++ b/Mage.Sets/src/mage/cards/o/OrderOfWhiteclay.java @@ -15,7 +15,7 @@ import mage.constants.ComparisonType; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.Target; import mage.target.common.TargetCardInYourGraveyard; @@ -26,10 +26,10 @@ */ public final class OrderOfWhiteclay extends CardImpl { - private static final FilterCreatureCard filter = new FilterCreatureCard("creature card with converted mana cost 3 or less from your graveyard"); + private static final FilterCreatureCard filter = new FilterCreatureCard("creature card with mana value 3 or less from your graveyard"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public OrderOfWhiteclay(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/o/OreskosExplorer.java b/Mage.Sets/src/mage/cards/o/OreskosExplorer.java index f57d83046b76..47ef467fae1c 100644 --- a/Mage.Sets/src/mage/cards/o/OreskosExplorer.java +++ b/Mage.Sets/src/mage/cards/o/OreskosExplorer.java @@ -53,7 +53,7 @@ class OreskosExplorerEffect extends OneShotEffect { public OreskosExplorerEffect() { super(Outcome.PutLandInPlay); - this.staticText = "search your library for up to X Plains cards, where X is the number of players who control more lands than you. Reveal those cards, put them into your hand, then shuffle your library"; + this.staticText = "search your library for up to X Plains cards, where X is the number of players who control more lands than you. Reveal those cards, put them into your hand, then shuffle"; } public OreskosExplorerEffect(final OreskosExplorerEffect effect) { diff --git a/Mage.Sets/src/mage/cards/o/OrimsThunder.java b/Mage.Sets/src/mage/cards/o/OrimsThunder.java index 0392e3cc529d..2700155061a9 100644 --- a/Mage.Sets/src/mage/cards/o/OrimsThunder.java +++ b/Mage.Sets/src/mage/cards/o/OrimsThunder.java @@ -37,7 +37,7 @@ public OrimsThunder(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new OrimsThunderEffect2(), KickedCondition.instance, - "If Orim's Thunder was kicked, it deals damage equal to that permanent's converted mana cost to target creature") + "If this spell was kicked, it deals damage equal to that permanent's mana value to target creature") ); this.getSpellAbility().setTargetAdjuster(OrimsThunderAdjuster.instance); } @@ -80,7 +80,7 @@ public boolean apply(Game game, Ability source) { MageObject firstTarget = game.getLastKnownInformation(source.getFirstTarget(), Zone.BATTLEFIELD); Permanent secondTarget = game.getPermanent(source.getTargets().get(1).getFirstTarget()); if (firstTarget != null) { - damage = firstTarget.getConvertedManaCost(); + damage = firstTarget.getManaValue(); } boolean kicked = KickedCondition.instance.apply(game, source); if (kicked && secondTarget != null) { diff --git a/Mage.Sets/src/mage/cards/o/OriqLoremage.java b/Mage.Sets/src/mage/cards/o/OriqLoremage.java new file mode 100644 index 000000000000..12c8efe52afa --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OriqLoremage.java @@ -0,0 +1,89 @@ +package mage.cards.o; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OriqLoremage extends CardImpl { + + public OriqLoremage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // {T}: Search your library for a card, put it into your graveyard, then shuffle. If it's an instant or sorcery card, put a +1/+1 counter on Oriq Loremage. + this.addAbility(new SimpleActivatedAbility(new OriqLoremageEffect(), new TapSourceCost())); + } + + private OriqLoremage(final OriqLoremage card) { + super(card); + } + + @Override + public OriqLoremage copy() { + return new OriqLoremage(this); + } +} + +class OriqLoremageEffect extends OneShotEffect { + + OriqLoremageEffect() { + super(Outcome.Benefit); + staticText = "search your library for a card, put it into your graveyard, then shuffle. " + + "If it's an instant or sorcery card, put a +1/+1 counter on {this}"; + } + + private OriqLoremageEffect(final OriqLoremageEffect effect) { + super(effect); + } + + @Override + public OriqLoremageEffect copy() { + return new OriqLoremageEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + TargetCardInLibrary target = new TargetCardInLibrary(); + player.searchLibrary(target, source, game); + Card card = player.getLibrary().getCard(target.getFirstTarget(), game); + if (card == null) { + player.shuffleLibrary(source, game); + return true; + } + player.moveCards(card, Zone.GRAVEYARD, source, game); + player.shuffleLibrary(source, game); + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (!card.isInstantOrSorcery() || permanent == null) { + return true; + } + permanent.addCounters(CounterType.P1P1.createInstance(), source.getControllerId(), source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/o/OrzhovAdvokist.java b/Mage.Sets/src/mage/cards/o/OrzhovAdvokist.java index ad80b9c97d97..6f58f1588aac 100644 --- a/Mage.Sets/src/mage/cards/o/OrzhovAdvokist.java +++ b/Mage.Sets/src/mage/cards/o/OrzhovAdvokist.java @@ -61,7 +61,7 @@ class OrzhovAdvokistEffect extends OneShotEffect { public OrzhovAdvokistEffect() { super(Outcome.Benefit); this.staticText = "each player may put two +1/+1 counters on a creature they control. " - + "If a player does, creatures that player controls can't attack you or a planeswalker you control until your next turn"; + + "If a player does, creatures that player controls can't attack you or planeswalkers you control until your next turn"; } public OrzhovAdvokistEffect(final OrzhovAdvokistEffect effect) { diff --git a/Mage.Sets/src/mage/cards/o/OrzhovCharm.java b/Mage.Sets/src/mage/cards/o/OrzhovCharm.java index 407f902c25e5..3f510dab8209 100644 --- a/Mage.Sets/src/mage/cards/o/OrzhovCharm.java +++ b/Mage.Sets/src/mage/cards/o/OrzhovCharm.java @@ -1,22 +1,17 @@ - package mage.cards.o; -import java.util.LinkedList; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.ComparisonType; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.*; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -24,38 +19,37 @@ import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.Objects; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class OrzhovCharm extends CardImpl { - private static final FilterCard filter = new FilterCreatureCard("creature card with converted mana cost 1 or less from your graveyard"); + private static final FilterCard filter + = new FilterCreatureCard("creature card with mana value 1 or less from your graveyard"); + static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 2)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 2)); } public OrzhovCharm(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{W}{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}{B}"); //Choose one - Return target creature you control and all Auras you control attached to it to their owner's hand this.getSpellAbility().addEffect(new OrzhovCharmReturnToHandEffect()); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); // or destroy target creature and you lose life equal to its toughness; - Mode mode = new Mode(); - mode.addEffect(new OrzhovCharmDestroyAndLoseLifeEffect()); + Mode mode = new Mode(new OrzhovCharmDestroyAndLoseLifeEffect()); mode.addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addMode(mode); // or return target creature card with converted mana cost 1 or less from your graveyard to the battlefield. - Mode mode2 = new Mode(); - mode2.addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); - mode2.addTarget(new TargetCardInYourGraveyard(filter)); - this.getSpellAbility().addMode(mode2); - - + mode = new Mode(new ReturnFromGraveyardToBattlefieldTargetEffect()); + mode.addTarget(new TargetCardInYourGraveyard(filter)); + this.getSpellAbility().addMode(mode); } private OrzhovCharm(final OrzhovCharm card) { @@ -70,12 +64,12 @@ public OrzhovCharm copy() { class OrzhovCharmReturnToHandEffect extends OneShotEffect { - public OrzhovCharmReturnToHandEffect() { + OrzhovCharmReturnToHandEffect() { super(Outcome.ReturnToHand); this.staticText = "Return target creature you control and all Auras you control attached to it to their owner's hand"; } - public OrzhovCharmReturnToHandEffect(final OrzhovCharmReturnToHandEffect effect) { + private OrzhovCharmReturnToHandEffect(final OrzhovCharmReturnToHandEffect effect) { super(effect); } @@ -86,34 +80,31 @@ public OrzhovCharmReturnToHandEffect copy() { @Override public boolean apply(Game game, Ability source) { - Permanent target = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (target != null) { - LinkedList attachments = new LinkedList<>(); - attachments.addAll(target.getAttachments()); - for (UUID attachmentId : attachments) { - Permanent attachment = game.getPermanent(attachmentId); - if (attachment != null && attachment.isControlledBy(source.getControllerId()) - && attachment.hasSubtype(SubType.AURA, game)) { - attachment.moveToZone(Zone.HAND, source, game, false); - } - } - if (target.isControlledBy(source.getControllerId())) { - target.moveToZone(Zone.HAND, source, game, false); - } - return true; + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (player == null || permanent == null) { + return false; } - return false; + Cards cards = new CardsImpl(permanent); + permanent.getAttachments() + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .filter(p -> p.isControlledBy(source.getControllerId())) + .filter(p -> p.hasSubtype(SubType.AURA, game)) + .forEach(cards::add); + return player.moveCards(cards, Zone.HAND, source, game); } } class OrzhovCharmDestroyAndLoseLifeEffect extends OneShotEffect { - public OrzhovCharmDestroyAndLoseLifeEffect() { + OrzhovCharmDestroyAndLoseLifeEffect() { super(Outcome.DestroyPermanent); this.staticText = "destroy target creature and you lose life equal to its toughness"; } - public OrzhovCharmDestroyAndLoseLifeEffect(final OrzhovCharmDestroyAndLoseLifeEffect effect) { + private OrzhovCharmDestroyAndLoseLifeEffect(final OrzhovCharmDestroyAndLoseLifeEffect effect) { super(effect); } @@ -126,14 +117,14 @@ public OrzhovCharmDestroyAndLoseLifeEffect copy() { public boolean apply(Game game, Ability source) { Permanent target = game.getPermanent(getTargetPointer().getFirst(game, source)); Player controller = game.getPlayer(source.getControllerId()); - if (target != null && controller != null) { - int toughness = target.getToughness().getValue(); - target.destroy(source, game, false); - if (toughness > 0) { - controller.loseLife(toughness, game, source, false); - } - return true; + if (target == null || controller == null) { + return false; + } + int toughness = target.getToughness().getValue(); + target.destroy(source, game, false); + if (toughness > 0) { + controller.loseLife(toughness, game, source, false); } - return false; + return true; } } diff --git a/Mage.Sets/src/mage/cards/o/OsgirTheReconstructor.java b/Mage.Sets/src/mage/cards/o/OsgirTheReconstructor.java new file mode 100644 index 000000000000..22b430937ea5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OsgirTheReconstructor.java @@ -0,0 +1,144 @@ + +package mage.cards.o; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.CostAdjuster; +import mage.abilities.costs.VariableCost; +import mage.abilities.costs.common.ExileFromGraveCost; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.VariableManaCost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.common.FilterArtifactCard; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetControlledPermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetadjustment.TargetAdjuster; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * + * @author Arketec + */ +public final class OsgirTheReconstructor extends CardImpl { + + public OsgirTheReconstructor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{W}"); + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.GIANT); + this.subtype.add(SubType.ARTIFICER); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // {1}, Sacrifice an artifact: Target creature you control gets +2/+0 until end of turn. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(2, 0, Duration.EndOfTurn), new ManaCostsImpl<>("{1}")); + ability.addTarget(new TargetControlledCreaturePermanent()); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT_AN))); + this.addAbility(ability); + + // {X},{T}, Exile an artifact with mana value X from your graveyard: Create two tokens that are copies of the exiled card. Activate only as + Ability copyAbility = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, + new OsgirTheReconstructorCreateArtifactTokensEffect(), + new ManaCostsImpl("{X}")); + copyAbility.addCost(new TapSourceCost()); + copyAbility.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(), "Exile an artifact card with mana value X from your graveyard")); + + copyAbility.setCostAdjuster(OsgirTheReconstructorCostAdjuster.instance); + + this.addAbility(copyAbility); + } + + private OsgirTheReconstructor(final OsgirTheReconstructor card) { + super(card); + } + + @Override + public OsgirTheReconstructor copy() { + return new OsgirTheReconstructor(this); + } +} + +enum OsgirTheReconstructorCostAdjuster implements CostAdjuster { + instance; + + @Override + public void adjustCosts(Ability ability, Game game) { + int xValue = ability.getManaCostsToPay().getX(); + Player controller = game.getPlayer(ability.getControllerId()); + if (controller == null) { + return; + } + FilterCard filter = new FilterArtifactCard("an artifact card with mana value "+xValue+" from your graveyard"); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, xValue)); + for (Cost cost: ability.getCosts()) { + if (cost instanceof ExileFromGraveCost) { + cost.getTargets().set(0, new TargetCardInYourGraveyard(filter)); + } + } + } +} + +class OsgirTheReconstructorCreateArtifactTokensEffect extends OneShotEffect { + + public OsgirTheReconstructorCreateArtifactTokensEffect() { + super(Outcome.Benefit); + this.staticText = "Create two tokens that are copies of the exiled card."; + } + + public OsgirTheReconstructorCreateArtifactTokensEffect(final OsgirTheReconstructorCreateArtifactTokensEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = null; + for (Cost cost : source.getCosts()) { + if (!(cost instanceof ExileFromGraveCost)) { + continue; + } + card = ((ExileFromGraveCost) cost).getExiledCards().get(0); + } + + if (player == null || card == null) { + return false; + } + + CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(player.getId(), null, false, 2); + effect.setTargetPointer(new FixedTarget(card.getId(), game.getState().getZoneChangeCounter(card.getId()))); + effect.apply(game, source); + + return true; + } + + @Override + public OsgirTheReconstructorCreateArtifactTokensEffect copy() { + return new OsgirTheReconstructorCreateArtifactTokensEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/OupheVandals.java b/Mage.Sets/src/mage/cards/o/OupheVandals.java index 49fda4397e7f..e6befc1402b0 100644 --- a/Mage.Sets/src/mage/cards/o/OupheVandals.java +++ b/Mage.Sets/src/mage/cards/o/OupheVandals.java @@ -15,7 +15,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterStackObject; -import mage.filter.predicate.ability.ArtifactSourcePredicate; +import mage.filter.predicate.other.ArtifactSourcePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.StackObject; diff --git a/Mage.Sets/src/mage/cards/o/OuterRimSlaver.java b/Mage.Sets/src/mage/cards/o/OuterRimSlaver.java index ca6afe57c452..4bfa3462e908 100644 --- a/Mage.Sets/src/mage/cards/o/OuterRimSlaver.java +++ b/Mage.Sets/src/mage/cards/o/OuterRimSlaver.java @@ -12,7 +12,7 @@ import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/o/Outmaneuver.java b/Mage.Sets/src/mage/cards/o/Outmaneuver.java index d6a4c6b9c1cb..378585bfa820 100644 --- a/Mage.Sets/src/mage/cards/o/Outmaneuver.java +++ b/Mage.Sets/src/mage/cards/o/Outmaneuver.java @@ -1,8 +1,6 @@ package mage.cards.o; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.effects.AsThoughEffectImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -16,26 +14,21 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import mage.target.targetadjustment.TargetAdjuster; + +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class Outmaneuver extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - - static { - filter.add(BlockedPredicate.instance); - } - public Outmaneuver(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{R}"); // X target blocked creatures assign their combat damage this turn as though they weren't blocked. this.getSpellAbility().addEffect(new OutmaneuverEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); - + this.getSpellAbility().setTargetAdjuster(OutmaneuverAdjuster.instance); } private Outmaneuver(final Outmaneuver card) { @@ -46,42 +39,38 @@ private Outmaneuver(final Outmaneuver card) { public Outmaneuver copy() { return new Outmaneuver(this); } +} + +enum OutmaneuverAdjuster implements TargetAdjuster { + instance; + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(BlockedPredicate.instance); + } @Override public void adjustTargets(Ability ability, Game game) { - if (ability instanceof SpellAbility) { - ability.getTargets().clear(); - int numberOfTargets = ability.getManaCostsToPay().getX(); - numberOfTargets = Math.min(game.getBattlefield().getAllActivePermanents(filter, - ability.getControllerId(), game).size(), numberOfTargets); - ability.addTarget(new TargetCreaturePermanent(numberOfTargets, - numberOfTargets, filter, false)); - } + ability.getTargets().clear(); + int numberOfTargets = ability.getManaCostsToPay().getX(); + ability.addTarget(new TargetCreaturePermanent(numberOfTargets, numberOfTargets, filter, false)); } } class OutmaneuverEffect extends AsThoughEffectImpl { - public OutmaneuverEffect() { + OutmaneuverEffect() { super(AsThoughEffectType.DAMAGE_NOT_BLOCKED, Duration.EndOfTurn, Outcome.Damage); this.staticText = "X target blocked creatures assign their combat damage this turn as though they weren't blocked."; } - public OutmaneuverEffect(OutmaneuverEffect effect) { + private OutmaneuverEffect(OutmaneuverEffect effect) { super(effect); } @Override public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { - Permanent blockedCreature = game.getPermanent(sourceId); - if (blockedCreature != null) { - Player controller = game.getPlayer(blockedCreature.getControllerId()); - if (controller != null) { - return controller.chooseUse(Outcome.Damage, "Do you wish to assign combat damage for " - + blockedCreature.getLogName() + " as though it weren't blocked?", source, game); - } - } - return false; + return targetPointer.getTargets(game, source).contains(sourceId); } @Override diff --git a/Mage.Sets/src/mage/cards/o/Overblaze.java b/Mage.Sets/src/mage/cards/o/Overblaze.java index c47b0204efe6..09f45adf68c7 100644 --- a/Mage.Sets/src/mage/cards/o/Overblaze.java +++ b/Mage.Sets/src/mage/cards/o/Overblaze.java @@ -61,9 +61,8 @@ public OverblazeEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE - || event.getType() == GameEvent.EventType.DAMAGE_PLAYER - || event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER; + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT + || event.getType() == GameEvent.EventType.DAMAGE_PLAYER; } @Override diff --git a/Mage.Sets/src/mage/cards/o/OvergrownArch.java b/Mage.Sets/src/mage/cards/o/OvergrownArch.java new file mode 100644 index 000000000000..fca357f306c7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OvergrownArch.java @@ -0,0 +1,52 @@ +package mage.cards.o; + +import mage.MageInt; +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.GainLifeEffect; +import mage.abilities.effects.common.LearnEffect; +import mage.abilities.keyword.DefenderAbility; +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 OvergrownArch extends CardImpl { + + public OvergrownArch(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.PLANT); + this.subtype.add(SubType.WALL); + this.power = new MageInt(0); + this.toughness = new MageInt(4); + + // Defender + this.addAbility(DefenderAbility.getInstance()); + + // {T}: You gain 1 life. + this.addAbility(new SimpleActivatedAbility(new GainLifeEffect(1), new TapSourceCost())); + + // {2}, Sacrifice Overgrown Arch: Learn. + Ability ability = new SimpleActivatedAbility(new LearnEffect(), new GenericManaCost(2)); + ability.addCost(new SacrificeSourceCost()); + this.addAbility(ability); + } + + private OvergrownArch(final OvergrownArch card) { + super(card); + } + + @Override + public OvergrownArch copy() { + return new OvergrownArch(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/OvergrowthElemental.java b/Mage.Sets/src/mage/cards/o/OvergrowthElemental.java index 3f8f14806ac5..6188872bdbd3 100644 --- a/Mage.Sets/src/mage/cards/o/OvergrowthElemental.java +++ b/Mage.Sets/src/mage/cards/o/OvergrowthElemental.java @@ -16,7 +16,7 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; diff --git a/Mage.Sets/src/mage/cards/o/Overload.java b/Mage.Sets/src/mage/cards/o/Overload.java index 05d4aad44b3a..fa1b1fbd8106 100644 --- a/Mage.Sets/src/mage/cards/o/Overload.java +++ b/Mage.Sets/src/mage/cards/o/Overload.java @@ -46,7 +46,7 @@ class OverloadEffect extends OneShotEffect { OverloadEffect() { super(Outcome.DestroyPermanent); - this.staticText = "Destroy target artifact if its converted mana cost is 2 or less. if this spell was kicked, destroy that artifact if its converted mana cost is 5 or less instead."; + this.staticText = "Destroy target artifact if its mana value is 2 or less. If this spell was kicked, destroy that artifact if its mana value is 5 or less instead."; } OverloadEffect(final OverloadEffect effect) { @@ -64,7 +64,7 @@ public boolean apply(Game game, Ability source) { if (controller != null) { Permanent targetArtifact = game.getPermanent(this.getTargetPointer().getFirst(game, source)); if (targetArtifact != null) { - int cmc = targetArtifact.getConvertedManaCost(); + int cmc = targetArtifact.getManaValue(); if (cmc <= 2 || (KickedCondition.instance.apply(game, source) && cmc <= 5)) { targetArtifact.destroy(source, game, false); } diff --git a/Mage.Sets/src/mage/cards/o/Oversimplify.java b/Mage.Sets/src/mage/cards/o/Oversimplify.java new file mode 100644 index 000000000000..e4a8f6625c79 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/Oversimplify.java @@ -0,0 +1,98 @@ +package mage.cards.o; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Controllable; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.QuandrixToken; +import mage.game.permanent.token.Token; +import mage.players.Player; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class Oversimplify extends CardImpl { + + public Oversimplify(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{G}{U}"); + + // Exile all creatures. Each player creates a 0/0 green and blue Fractal creature token and puts a number of +1/+1 counters on it equal to the total power of creatures they controlled that were exiled this way. + this.getSpellAbility().addEffect(new OversimplifyEffect()); + } + + private Oversimplify(final Oversimplify card) { + super(card); + } + + @Override + public Oversimplify copy() { + return new Oversimplify(this); + } +} + +class OversimplifyEffect extends OneShotEffect { + + OversimplifyEffect() { + super(Outcome.Benefit); + staticText = "exile all creatures. Each player creates a 0/0 green and blue Fractal creature token " + + "and puts a number of +1/+1 counters on it equal to " + + "the total power of creatures they controlled that were exiled this way"; + } + + private OversimplifyEffect(final OversimplifyEffect effect) { + super(effect); + } + + @Override + public OversimplifyEffect copy() { + return new OversimplifyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + List permanents = game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_PERMANENT_CREATURE, + source.getControllerId(), source.getSourceId(), game + ); + Map playerMap = permanents + .stream() + .filter(Objects::nonNull) + .collect(Collectors.toMap( + Controllable::getControllerId, + p -> p.getPower().getValue(), + Integer::sum + )); + controller.moveCards(new CardsImpl(permanents), Zone.EXILED, source, game); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Token token = new QuandrixToken(); + token.putOntoBattlefield(1, game, source, playerId); + int counter = playerMap.getOrDefault(playerId, 0); + for (UUID tokenId : token.getLastAddedTokenIds()) { + Permanent permanent = game.getPermanent(tokenId); + if (permanent != null) { + permanent.addCounters(CounterType.P1P1.createInstance(counter), playerId, source, game); + } + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/o/OverwhelmingIntellect.java b/Mage.Sets/src/mage/cards/o/OverwhelmingIntellect.java index 161dd19f8e04..298d8fdc5aed 100644 --- a/Mage.Sets/src/mage/cards/o/OverwhelmingIntellect.java +++ b/Mage.Sets/src/mage/cards/o/OverwhelmingIntellect.java @@ -43,7 +43,7 @@ class OverwhelmingIntellectEffect extends OneShotEffect { public OverwhelmingIntellectEffect() { super(Outcome.Detriment); - staticText = "Counter target creature spell. Draw cards equal to that spell's converted mana cost"; + staticText = "Counter target creature spell. Draw cards equal to that spell's mana value"; } public OverwhelmingIntellectEffect(final OverwhelmingIntellectEffect effect) { @@ -61,7 +61,7 @@ public boolean apply(Game game, Ability source) { Spell spell = game.getStack().getSpell(getTargetPointer().getFirst(game, source)); if (controller != null && spell != null) { game.getStack().counter(source.getFirstTarget(), source, game); - controller.drawCards(spell.getConvertedManaCost(), source, game); + controller.drawCards(spell.getManaValue(), source, game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/o/OwlinShieldmage.java b/Mage.Sets/src/mage/cards/o/OwlinShieldmage.java new file mode 100644 index 000000000000..26389dc7d4cf --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OwlinShieldmage.java @@ -0,0 +1,42 @@ +package mage.cards.o; + +import mage.MageInt; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.WardAbility; +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 OwlinShieldmage extends CardImpl { + + public OwlinShieldmage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{B}"); + + this.subtype.add(SubType.BIRD); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Ward—Pay 3 life + this.addAbility(new WardAbility(new PayLifeCost(3))); + } + + private OwlinShieldmage(final OwlinShieldmage card) { + super(card); + } + + @Override + public OwlinShieldmage copy() { + return new OwlinShieldmage(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PackHunt.java b/Mage.Sets/src/mage/cards/p/PackHunt.java index a9339047ecdf..c2a18839fe8c 100644 --- a/Mage.Sets/src/mage/cards/p/PackHunt.java +++ b/Mage.Sets/src/mage/cards/p/PackHunt.java @@ -45,7 +45,7 @@ class PackHuntEffect extends OneShotEffect { public PackHuntEffect() { super(Outcome.Benefit); - this.staticText = "Search your library for up to three cards with the same name as target creature, reveal them, and put them into your hand. Then shuffle your library"; + this.staticText = "Search your library for up to three cards with the same name as target creature, reveal them, and put them into your hand. Then shuffle"; } public PackHuntEffect(final PackHuntEffect effect) { diff --git a/Mage.Sets/src/mage/cards/p/PactOfTheTitan.java b/Mage.Sets/src/mage/cards/p/PactOfTheTitan.java index 103fce76fe8d..e8a8e2c45a5a 100644 --- a/Mage.Sets/src/mage/cards/p/PactOfTheTitan.java +++ b/Mage.Sets/src/mage/cards/p/PactOfTheTitan.java @@ -1,4 +1,3 @@ - package mage.cards.p; import java.util.UUID; @@ -24,6 +23,7 @@ public PactOfTheTitan(UUID ownerId, CardSetInfo setInfo) { // Create a 4/4 red Giant creature token. this.getSpellAbility().addEffect(new CreateTokenEffect(new GiantToken())); + // At the beginning of your next upkeep, pay {4}{R}. If you don't, you lose the game. this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new PactDelayedTriggeredAbility(new ManaCostsImpl("{4}{R}")))); } diff --git a/Mage.Sets/src/mage/cards/p/PadeemConsulOfInnovation.java b/Mage.Sets/src/mage/cards/p/PadeemConsulOfInnovation.java index 98a2f3995a47..970ffc25aeb0 100644 --- a/Mage.Sets/src/mage/cards/p/PadeemConsulOfInnovation.java +++ b/Mage.Sets/src/mage/cards/p/PadeemConsulOfInnovation.java @@ -37,7 +37,7 @@ public PadeemConsulOfInnovation(UUID ownerId, CardSetInfo setInfo) { // At the beginning of your upkeep, if you control the artifact with the highest converted mana cost or tied for the highest converted mana cost, draw a card. Ability ability = new ConditionalInterveningIfTriggeredAbility(new BeginningOfUpkeepTriggeredAbility(new DrawCardSourceControllerEffect(1), TargetController.YOU, false), new ControlsPermanentGreatestCMCCondition(new FilterArtifactPermanent()), - "At the beginning of your upkeep, if you control the artifact with the highest converted mana cost or tied for the highest converted mana cost, draw a card."); + "At the beginning of your upkeep, if you control the artifact with the highest mana value or tied for the highest mana value, draw a card."); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/p/PainSeer.java b/Mage.Sets/src/mage/cards/p/PainSeer.java index 00b1b8f79b93..8efb6a56db62 100644 --- a/Mage.Sets/src/mage/cards/p/PainSeer.java +++ b/Mage.Sets/src/mage/cards/p/PainSeer.java @@ -46,7 +46,7 @@ class PainSeerEffect extends OneShotEffect { public PainSeerEffect() { super(Outcome.DrawCard); - this.staticText = "reveal the top card of your library and put that card into your hand. You lose life equal to that card's converted mana cost"; + this.staticText = "reveal the top card of your library and put that card into your hand. You lose life equal to that card's mana value"; } public PainSeerEffect(final PainSeerEffect effect) { @@ -72,7 +72,7 @@ public boolean apply(Game game, Ability source) { Cards cards = new CardsImpl(card); player.revealCards("Pain Seer", cards, game); if(player.moveCards(card, Zone.HAND, source, game)) { - player.loseLife(card.getConvertedManaCost(), game, source, false); + player.loseLife(card.getManaValue(), game, source, false); return true; } } diff --git a/Mage.Sets/src/mage/cards/p/PainsReward.java b/Mage.Sets/src/mage/cards/p/PainsReward.java index 9e379ccff6e4..cde4a2753183 100644 --- a/Mage.Sets/src/mage/cards/p/PainsReward.java +++ b/Mage.Sets/src/mage/cards/p/PainsReward.java @@ -92,7 +92,7 @@ public boolean apply(Game game, Ability source) { private int chooseLifeAmountToBid(Player player, int currentBig, Game game) { int newBid; - if (!player.isHuman() && !player.isTestMode()) { + if (player.isComputer()) { // AI choose newBid = currentBig + 1; } else { diff --git a/Mage.Sets/src/mage/cards/p/PakoArcaneRetriever.java b/Mage.Sets/src/mage/cards/p/PakoArcaneRetriever.java index 154b97c78ec0..805c31fa51bf 100644 --- a/Mage.Sets/src/mage/cards/p/PakoArcaneRetriever.java +++ b/Mage.Sets/src/mage/cards/p/PakoArcaneRetriever.java @@ -58,6 +58,10 @@ public static boolean checkWatcher(UUID playerId, Card card, Game game) { PakoArcaneRetrieverWatcher watcher = game.getState().getWatcher(PakoArcaneRetrieverWatcher.class); return watcher != null && watcher.checkCard(playerId, card, game); } + + public static Watcher createWatcher() { + return new PakoArcaneRetrieverWatcher(); + } } class PakoArcaneRetrieverEffect extends OneShotEffect { @@ -124,13 +128,23 @@ public void watch(GameEvent event, Game game) { } void addCard(UUID playerId, Card card, Game game) { - playerMap.computeIfAbsent(playerId, u -> new HashSet()).add(new MageObjectReference(card, game)); + playerMap.computeIfAbsent(playerId, u -> new HashSet()) + .add(new MageObjectReference(card, game)); } boolean checkCard(UUID playerId, Card card, Game game) { - return card != null && playerMap - .computeIfAbsent(playerId, u -> new HashSet<>()) + if (card == null) + return false; + + // If the card has been moved onto the stack (e.g. by attempting to cast), the + // number of zone change counters will be 1 more than when the pako effect + // exiled it and the watcher took a reference. + // https://github.com/magefree/mage/issues/7585 + final int zoneChangeDifference = game.getState().getZone(card.getId()) == Zone.STACK ? 1 : 0; + return playerMap.computeIfAbsent(playerId, u -> new HashSet()) .stream() - .anyMatch(mageObjectReference -> mageObjectReference.refersTo(card.getId(), game)); + .anyMatch(ref -> ref.getSourceId().equals(card.getId()) + && ref.getZoneChangeCounter() == (game.getState().getZoneChangeCounter(card.getId()) + - zoneChangeDifference)); } } diff --git a/Mage.Sets/src/mage/cards/p/PaladinOfPrahv.java b/Mage.Sets/src/mage/cards/p/PaladinOfPrahv.java index 45bfa9e5aca7..185d778a14b1 100644 --- a/Mage.Sets/src/mage/cards/p/PaladinOfPrahv.java +++ b/Mage.Sets/src/mage/cards/p/PaladinOfPrahv.java @@ -75,9 +75,8 @@ public PaladinOfPrahvTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { switch(event.getType()) { - case DAMAGED_CREATURE: + case DAMAGED_PERMANENT: case DAMAGED_PLAYER: - case DAMAGED_PLANESWALKER: return true; } return false; diff --git a/Mage.Sets/src/mage/cards/p/Paleoloth.java b/Mage.Sets/src/mage/cards/p/Paleoloth.java index a176d7bac4c2..a7d7bf81dbc1 100644 --- a/Mage.Sets/src/mage/cards/p/Paleoloth.java +++ b/Mage.Sets/src/mage/cards/p/Paleoloth.java @@ -14,7 +14,7 @@ import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCardInYourGraveyard; /** diff --git a/Mage.Sets/src/mage/cards/p/PalisadeGiant.java b/Mage.Sets/src/mage/cards/p/PalisadeGiant.java index bb85add7e4fe..42f26f03dac7 100644 --- a/Mage.Sets/src/mage/cards/p/PalisadeGiant.java +++ b/Mage.Sets/src/mage/cards/p/PalisadeGiant.java @@ -75,9 +75,8 @@ class PalisadeGiantReplacementEffect extends ReplacementEffectImpl { @Override public boolean checksEventType(GameEvent event, Game game) { switch (event.getType()) { - case DAMAGE_CREATURE: + case DAMAGE_PERMANENT: case DAMAGE_PLAYER: - case DAMAGE_PLANESWALKER: return true; default: return false; @@ -89,7 +88,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { if (event.getType() == GameEvent.EventType.DAMAGE_PLAYER && event.getPlayerId().equals(source.getControllerId())) { return true; } - if (event.getType() == GameEvent.EventType.DAMAGE_CREATURE || event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER) { + if (event.getType() == GameEvent.EventType.DAMAGE_PERMANENT) { Permanent targetPermanent = game.getPermanent(event.getTargetId()); Permanent sourcePermanent = game.getPermanent(source.getSourceId()); if (targetPermanent != null && diff --git a/Mage.Sets/src/mage/cards/p/PalladiaMorsTheRuiner.java b/Mage.Sets/src/mage/cards/p/PalladiaMorsTheRuiner.java index e32f033102e9..4b38ca8ff304 100644 --- a/Mage.Sets/src/mage/cards/p/PalladiaMorsTheRuiner.java +++ b/Mage.Sets/src/mage/cards/p/PalladiaMorsTheRuiner.java @@ -100,9 +100,8 @@ public PalladiaMorsTheRuinerWatcher() { @Override public void watch(GameEvent event, Game game) { switch (event.getType()) { - case DAMAGED_CREATURE: + case DAMAGED_PERMANENT: case DAMAGED_PLAYER: - case DAMAGED_PLANESWALKER: break; default: return; diff --git a/Mage.Sets/src/mage/cards/p/PallidMycoderm.java b/Mage.Sets/src/mage/cards/p/PallidMycoderm.java index d3a6bf71e6dd..8308a37fb84e 100644 --- a/Mage.Sets/src/mage/cards/p/PallidMycoderm.java +++ b/Mage.Sets/src/mage/cards/p/PallidMycoderm.java @@ -1,7 +1,5 @@ - package mage.cards.p; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; @@ -12,43 +10,60 @@ import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.TargetController; import mage.counters.CounterType; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.game.permanent.token.SaprolingToken; -import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class PallidMycoderm extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Each creature you control that's a Fungus or a Saproling"); - private static final FilterControlledCreaturePermanent filterSaproling = new FilterControlledCreaturePermanent("a Saproling"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); + private static final FilterControlledPermanent filterSaproling + = new FilterControlledPermanent(SubType.SAPROLING, "a Saproling"); + static { filter.add(TargetController.YOU.getControllerPredicate()); - filter.add(Predicates.or(SubType.FUNGUS.getPredicate(), SubType.SAPROLING.getPredicate())); - filterSaproling.add(SubType.SAPROLING.getPredicate()); + filter.add(Predicates.or( + SubType.FUNGUS.getPredicate(), + SubType.SAPROLING.getPredicate() + )); } public PallidMycoderm(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); this.subtype.add(SubType.FUNGUS); this.power = new MageInt(2); this.toughness = new MageInt(4); // At the beginning of your upkeep, put a spore counter on Pallid Mycoderm. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new AddCountersSourceEffect(CounterType.SPORE.createInstance()), TargetController.YOU, false)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new AddCountersSourceEffect(CounterType.SPORE.createInstance()), TargetController.YOU, false + )); + // Remove three spore counters from Pallid Mycoderm: Create a 1/1 green Saproling creature token. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new SaprolingToken()), new RemoveCountersSourceCost(CounterType.SPORE.createInstance(3)))); + this.addAbility(new SimpleActivatedAbility( + new CreateTokenEffect(new SaprolingToken()), + new RemoveCountersSourceCost(CounterType.SPORE.createInstance(3)) + )); + // Sacrifice a Saproling: Each creature you control that's a Fungus or a Saproling gets +1/+1 until end of turn. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, - new BoostAllEffect(1,1,Duration.EndOfTurn, filter, false), - new SacrificeTargetCost(new TargetControlledCreaturePermanent(1,1,filterSaproling, false)))); + this.addAbility(new SimpleActivatedAbility( + new BoostAllEffect(1, 1, Duration.EndOfTurn, filter, false) + .setText("each creature you control that's a Fungus or a Saproling gets +1/+1 until end of turn"), + new SacrificeTargetCost(new TargetControlledPermanent(filterSaproling)) + )); } private PallidMycoderm(final PallidMycoderm card) { diff --git a/Mage.Sets/src/mage/cards/p/PanopticMirror.java b/Mage.Sets/src/mage/cards/p/PanopticMirror.java index c1ecc18b4edf..8faeea8f0df1 100644 --- a/Mage.Sets/src/mage/cards/p/PanopticMirror.java +++ b/Mage.Sets/src/mage/cards/p/PanopticMirror.java @@ -11,7 +11,7 @@ import mage.constants.*; import mage.filter.FilterCard; import mage.filter.common.FilterInstantOrSorceryCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -51,7 +51,7 @@ class PanopticMirrorExileEffect extends OneShotEffect { public PanopticMirrorExileEffect() { super(Outcome.Exile); - this.staticText = "You may exile an instant or sorcery card with converted mana cost X from your hand"; + this.staticText = "You may exile an instant or sorcery card with mana value X from your hand"; } public PanopticMirrorExileEffect(final PanopticMirrorExileEffect effect) { @@ -68,8 +68,8 @@ public boolean apply(Game game, Ability source) { source.getManaCostsToPay().getX(); int count = source.getManaCostsToPay().getX(); - FilterInstantOrSorceryCard filter = new FilterInstantOrSorceryCard("instant or sorcery card with converted mana cost equal to " + count); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, count)); + FilterInstantOrSorceryCard filter = new FilterInstantOrSorceryCard("instant or sorcery card with mana value equal to " + count); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, count)); String choiceText = "Exile a " + filter.getMessage() + " from your hand?"; Player player = game.getPlayer(source.getControllerId()); diff --git a/Mage.Sets/src/mage/cards/p/ParadoxZone.java b/Mage.Sets/src/mage/cards/p/ParadoxZone.java new file mode 100644 index 000000000000..f763ca33bc3c --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/ParadoxZone.java @@ -0,0 +1,55 @@ +package mage.cards.p; + +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CountersSourceCount; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DoubleCountersSourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.game.permanent.token.QuandrixToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ParadoxZone extends CardImpl { + + private static final DynamicValue xValue = new CountersSourceCount(CounterType.GROWTH); + + public ParadoxZone(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{G}"); + + // Paradox Zone enters the battlefield with a growth counter on it. + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.GROWTH.createInstance()), + "with a growth counter on it" + )); + + // At the beginning of your end step, double the number of growth counters on Paradox Zone. Then create a 0/0 blue and green Fractal creature token. Put X +1/+1 counters on it, where X is the number of growth counters on Paradox Zone. + Ability ability = new BeginningOfEndStepTriggeredAbility( + new DoubleCountersSourceEffect(CounterType.GROWTH), TargetController.YOU, false + ); + Effect effect = QuandrixToken.getEffect( + xValue, "Put X +1/+1 counters on it, where X is the number of growth counters on {this}" + ); + ability.addEffect(effect.concatBy("Then")); + this.addAbility(ability); + } + + private ParadoxZone(final ParadoxZone card) { + super(card); + } + + @Override + public ParadoxZone copy() { + return new ParadoxZone(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/ParagonOfEternalWilds.java b/Mage.Sets/src/mage/cards/p/ParagonOfEternalWilds.java index 594160c93b6e..677375e1d182 100644 --- a/Mage.Sets/src/mage/cards/p/ParagonOfEternalWilds.java +++ b/Mage.Sets/src/mage/cards/p/ParagonOfEternalWilds.java @@ -22,7 +22,7 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/p/ParagonOfFierceDefiance.java b/Mage.Sets/src/mage/cards/p/ParagonOfFierceDefiance.java index c56793f6e107..3642008f5265 100644 --- a/Mage.Sets/src/mage/cards/p/ParagonOfFierceDefiance.java +++ b/Mage.Sets/src/mage/cards/p/ParagonOfFierceDefiance.java @@ -21,7 +21,7 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/p/ParagonOfGatheringMists.java b/Mage.Sets/src/mage/cards/p/ParagonOfGatheringMists.java index a5392fb99a17..a097a654ad35 100644 --- a/Mage.Sets/src/mage/cards/p/ParagonOfGatheringMists.java +++ b/Mage.Sets/src/mage/cards/p/ParagonOfGatheringMists.java @@ -22,7 +22,7 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/p/ParagonOfNewDawns.java b/Mage.Sets/src/mage/cards/p/ParagonOfNewDawns.java index eb16ead2d390..768bb2d8e8f0 100644 --- a/Mage.Sets/src/mage/cards/p/ParagonOfNewDawns.java +++ b/Mage.Sets/src/mage/cards/p/ParagonOfNewDawns.java @@ -21,7 +21,7 @@ import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/p/ParagonOfOpenGraves.java b/Mage.Sets/src/mage/cards/p/ParagonOfOpenGraves.java index 3da4b9819b21..005a1a2a2cc0 100644 --- a/Mage.Sets/src/mage/cards/p/ParagonOfOpenGraves.java +++ b/Mage.Sets/src/mage/cards/p/ParagonOfOpenGraves.java @@ -21,7 +21,7 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/p/ParallaxNexus.java b/Mage.Sets/src/mage/cards/p/ParallaxNexus.java index 7d06bba24894..8140564837fb 100644 --- a/Mage.Sets/src/mage/cards/p/ParallaxNexus.java +++ b/Mage.Sets/src/mage/cards/p/ParallaxNexus.java @@ -1,12 +1,9 @@ - package mage.cards.p; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.ActivateAsSorceryActivatedAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; import mage.abilities.costs.common.RemoveCountersSourceCost; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.ExileFromZoneTargetEffect; import mage.abilities.effects.common.ReturnFromExileEffect; import mage.abilities.keyword.FadingAbility; @@ -15,17 +12,15 @@ import mage.constants.CardType; import mage.constants.Zone; import mage.counters.CounterType; -import mage.filter.FilterCard; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author spjspj */ public final class ParallaxNexus extends CardImpl { - private UUID exileId = UUID.randomUUID(); - public ParallaxNexus(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); @@ -33,14 +28,16 @@ public ParallaxNexus(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new FadingAbility(5, this)); // Remove a fade counter from Parallax Nexus: Target opponent exiles a card from their hand. Activate this ability only any time you could cast a sorcery. - Effect effect = new ExileFromZoneTargetEffect(Zone.HAND, exileId, "Parallax Nexus", new FilterCard()); - effect.setText("Target opponent exiles a card from their hand"); - Ability ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, effect, new RemoveCountersSourceCost(CounterType.FADE.createInstance())); + Ability ability = new ActivateAsSorceryActivatedAbility( + Zone.BATTLEFIELD, + new ExileFromZoneTargetEffect(Zone.HAND, true), + new RemoveCountersSourceCost(CounterType.FADE.createInstance()) + ); ability.addTarget(new TargetOpponent()); this.addAbility(ability); // When Parallax Nexus leaves the battlefield, each player returns to their hand all cards they own exiled with Parallax Nexus. - this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileEffect(exileId, Zone.HAND), false)); + this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileEffect(Zone.HAND), false)); } private ParallaxNexus(final ParallaxNexus card) { diff --git a/Mage.Sets/src/mage/cards/p/ParallectricFeedback.java b/Mage.Sets/src/mage/cards/p/ParallectricFeedback.java index ad4709b34b93..12eacba11bd9 100644 --- a/Mage.Sets/src/mage/cards/p/ParallectricFeedback.java +++ b/Mage.Sets/src/mage/cards/p/ParallectricFeedback.java @@ -41,7 +41,7 @@ class ParallectricFeedbackEffect extends OneShotEffect { public ParallectricFeedbackEffect() { super(Outcome.Damage); - staticText = "{this} deals damage to target spell's controller equal to that spell's converted mana cost"; + staticText = "{this} deals damage to target spell's controller equal to that spell's mana value"; } public ParallectricFeedbackEffect(final ParallectricFeedbackEffect effect) { @@ -61,7 +61,7 @@ public boolean apply(Game game, Ability source) { if (spell != null) { Player spellController = game.getPlayer(spell.getControllerId()); if (spellController != null) { - spellController.damage(spell.getConvertedManaCost(), source.getSourceId(), source, game); + spellController.damage(spell.getManaValue(), source.getSourceId(), source, game); return true; } } diff --git a/Mage.Sets/src/mage/cards/p/PartTheWaterveil.java b/Mage.Sets/src/mage/cards/p/PartTheWaterveil.java index 91974017df4f..ab46d3aec9af 100644 --- a/Mage.Sets/src/mage/cards/p/PartTheWaterveil.java +++ b/Mage.Sets/src/mage/cards/p/PartTheWaterveil.java @@ -20,7 +20,7 @@ public PartTheWaterveil(UUID ownerId, CardSetInfo setInfo) { // Take an extra turn after this one. Exile Part the Waterveil. this.getSpellAbility().addEffect(new AddExtraTurnControllerEffect()); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); // Awaken 6-{6}{U}{U}{U} this.addAbility(new AwakenAbility(this, 6, "{6}{U}{U}{U}")); diff --git a/Mage.Sets/src/mage/cards/p/PashalikMons.java b/Mage.Sets/src/mage/cards/p/PashalikMons.java index 21e759168770..acf20bd78c76 100644 --- a/Mage.Sets/src/mage/cards/p/PashalikMons.java +++ b/Mage.Sets/src/mage/cards/p/PashalikMons.java @@ -17,7 +17,7 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.GoblinToken; import mage.target.common.TargetAnyTarget; import mage.target.common.TargetControlledPermanent; diff --git a/Mage.Sets/src/mage/cards/p/PastInFlames.java b/Mage.Sets/src/mage/cards/p/PastInFlames.java index 5566a948b9c4..e8339f0280f3 100644 --- a/Mage.Sets/src/mage/cards/p/PastInFlames.java +++ b/Mage.Sets/src/mage/cards/p/PastInFlames.java @@ -1,5 +1,6 @@ package mage.cards.p; +import mage.MageObject; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCostsImpl; @@ -63,7 +64,7 @@ public void init(Ability source, Game game) { if (this.affectedObjectsSet) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { - player.getGraveyard().stream().map((cardId) -> game.getCard(cardId)).filter((card) -> (card.isInstant() || card.isSorcery())).forEachOrdered((card) -> { + player.getGraveyard().stream().map((cardId) -> game.getCard(cardId)).filter(MageObject::isInstantOrSorcery).forEachOrdered((card) -> { affectedObjectList.add(new MageObjectReference(card, game)); }); } diff --git a/Mage.Sets/src/mage/cards/p/PathOfMettle.java b/Mage.Sets/src/mage/cards/p/PathOfMettle.java index 21ebb110bba9..78af4037d564 100644 --- a/Mage.Sets/src/mage/cards/p/PathOfMettle.java +++ b/Mage.Sets/src/mage/cards/p/PathOfMettle.java @@ -22,6 +22,7 @@ import mage.filter.predicate.mageobject.AbilityPredicate; import mage.game.Game; import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; /** * @author LevelX2 @@ -101,8 +102,9 @@ public boolean checkTrigger(GameEvent event, Game game) { int attackerCount = 0; if (game.getCombat() != null) { if (isControlledBy(game.getCombat().getAttackingPlayerId())) { - for (UUID attacker : game.getCombat().getAttackers()) { - if (filter.match(game.getPermanent(attacker), game)) { + for (UUID attackerId : game.getCombat().getAttackers()) { + Permanent attacker = game.getPermanent(attackerId); + if (attacker != null && filter.match(attacker, game)) { attackerCount++; } } diff --git a/Mage.Sets/src/mage/cards/p/PathToExile.java b/Mage.Sets/src/mage/cards/p/PathToExile.java index 508a51437ab1..8e7be81df6ca 100644 --- a/Mage.Sets/src/mage/cards/p/PathToExile.java +++ b/Mage.Sets/src/mage/cards/p/PathToExile.java @@ -47,7 +47,7 @@ class PathToExileEffect extends OneShotEffect { public PathToExileEffect() { super(Outcome.Exile); - staticText = "Exile target creature. Its controller may search their library for a basic land card, put that card onto the battlefield tapped, then shuffle their library"; + staticText = "Exile target creature. Its controller may search their library for a basic land card, put that card onto the battlefield tapped, then shuffle"; } public PathToExileEffect(final PathToExileEffect effect) { diff --git a/Mage.Sets/src/mage/cards/p/PatternMatcher.java b/Mage.Sets/src/mage/cards/p/PatternMatcher.java index 97099f2cef73..6bac73e92c4f 100644 --- a/Mage.Sets/src/mage/cards/p/PatternMatcher.java +++ b/Mage.Sets/src/mage/cards/p/PatternMatcher.java @@ -55,7 +55,7 @@ class RegularExpression extends OneShotEffect { RegularExpression() { super(Outcome.Benefit); staticText = "search your library for a card with the same name as another creature you control, " + - "reveal it, put it into your hand, then shuffle your library."; + "reveal it, put it into your hand, then shuffle."; } private RegularExpression(final RegularExpression effect) { diff --git a/Mage.Sets/src/mage/cards/p/PatternOfRebirth.java b/Mage.Sets/src/mage/cards/p/PatternOfRebirth.java index 2ce8801334e6..c963483b94fa 100644 --- a/Mage.Sets/src/mage/cards/p/PatternOfRebirth.java +++ b/Mage.Sets/src/mage/cards/p/PatternOfRebirth.java @@ -36,7 +36,7 @@ public PatternOfRebirth(UUID ownerId, CardSetInfo setInfo) { // When enchanted creature dies, that creature's controller may search their library for a creature card and put that card onto the battlefield. If that player does, they shuffle their library. Effect effect = new SearchLibraryPutInPlayTargetPlayerEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_CREATURE), false, false, Outcome.PutCreatureInPlay); - effect.setText("that creature's controller may search their library for a creature card and put that card onto the battlefield. If that player does, they shuffle their library"); + effect.setText("that creature's controller may search their library for a creature card and put that card onto the battlefield. If that player does, they shuffle"); this.addAbility(new DiesAttachedTriggeredAbility(effect, "enchanted creature", true, true, SetTargetPointer.ATTACHED_TO_CONTROLLER)); } diff --git a/Mage.Sets/src/mage/cards/p/PeacewalkerColossus.java b/Mage.Sets/src/mage/cards/p/PeacewalkerColossus.java index 375b7ac72c3a..7e32a6c2720a 100644 --- a/Mage.Sets/src/mage/cards/p/PeacewalkerColossus.java +++ b/Mage.Sets/src/mage/cards/p/PeacewalkerColossus.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledArtifactPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/p/PeelFromReality.java b/Mage.Sets/src/mage/cards/p/PeelFromReality.java index 0a426b442f04..e9c75a5eaff1 100644 --- a/Mage.Sets/src/mage/cards/p/PeelFromReality.java +++ b/Mage.Sets/src/mage/cards/p/PeelFromReality.java @@ -1,15 +1,10 @@ package mage.cards.p; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -24,7 +19,8 @@ public PeelFromReality(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); // Return target creature you control and target creature you don't control to their owners' hands. - this.getSpellAbility().addEffect(new PeelFromRealityEffect()); + this.getSpellAbility().addEffect(new ReturnToHandTargetEffect(true) + .setText("return target creature you control and target creature you don't control to their owners' hands")); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); } @@ -38,36 +34,3 @@ public PeelFromReality copy() { return new PeelFromReality(this); } } - -class PeelFromRealityEffect extends OneShotEffect { - - PeelFromRealityEffect() { - super(Outcome.ReturnToHand); - this.staticText = "Return target creature you control and target creature you don't control to their owners' hands"; - } - - private PeelFromRealityEffect(final PeelFromRealityEffect effect) { - super(effect); - } - - @Override - public PeelFromRealityEffect copy() { - return new PeelFromRealityEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - boolean result = false; - - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent != null) { - result |= permanent.moveToZone(Zone.HAND, source, game, false); - } - permanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if (permanent != null) { - result |= permanent.moveToZone(Zone.HAND, source, game, false); - } - - return result; - } -} diff --git a/Mage.Sets/src/mage/cards/p/PegasusCourser.java b/Mage.Sets/src/mage/cards/p/PegasusCourser.java index fcf9aa877882..f593ca328d81 100644 --- a/Mage.Sets/src/mage/cards/p/PegasusCourser.java +++ b/Mage.Sets/src/mage/cards/p/PegasusCourser.java @@ -13,7 +13,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.filter.common.FilterAttackingCreature; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetAttackingCreature; /** diff --git a/Mage.Sets/src/mage/cards/p/PelakkaPredation.java b/Mage.Sets/src/mage/cards/p/PelakkaPredation.java index 4b1afec77d9e..bd09c73c0395 100644 --- a/Mage.Sets/src/mage/cards/p/PelakkaPredation.java +++ b/Mage.Sets/src/mage/cards/p/PelakkaPredation.java @@ -3,7 +3,6 @@ import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.effects.common.discard.DiscardCardYouChooseTargetEffect; import mage.abilities.mana.BlackManaAbility; -import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; @@ -11,7 +10,7 @@ import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetOpponent; import java.util.UUID; @@ -21,10 +20,10 @@ */ public final class PelakkaPredation extends ModalDoubleFacesCard { - private static final FilterCard filter = new FilterCard("a card from it with converted mana cost 3 or greater"); + private static final FilterCard filter = new FilterCard("a card from it with mana value 3 or greater"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 2)); + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 2)); } public PelakkaPredation(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/p/PentarchPaladin.java b/Mage.Sets/src/mage/cards/p/PentarchPaladin.java index cc7fe7f1f84a..bf7b6338c094 100644 --- a/Mage.Sets/src/mage/cards/p/PentarchPaladin.java +++ b/Mage.Sets/src/mage/cards/p/PentarchPaladin.java @@ -21,7 +21,7 @@ import mage.abilities.effects.common.DestroyTargetEffect; import mage.constants.ComparisonType; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.target.targetadjustment.TargetAdjuster; @@ -76,7 +76,7 @@ public void adjustTargets(Ability ability, Game game) { if (chosenColor != null) { filter.add(new ColorPredicate(chosenColor)); } else { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, -5));// Pretty sure this is always false + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, -5));// Pretty sure this is always false } TargetPermanent oldTargetPermanent = new TargetPermanent(filter); ability.addTarget(oldTargetPermanent); diff --git a/Mage.Sets/src/mage/cards/p/PenumbraSpider.java b/Mage.Sets/src/mage/cards/p/PenumbraSpider.java index f767bcc8530c..363526fe5b8a 100644 --- a/Mage.Sets/src/mage/cards/p/PenumbraSpider.java +++ b/Mage.Sets/src/mage/cards/p/PenumbraSpider.java @@ -1,4 +1,3 @@ - package mage.cards.p; import java.util.UUID; @@ -27,6 +26,7 @@ public PenumbraSpider(UUID ownerId, CardSetInfo setInfo) { // Reach this.addAbility(ReachAbility.getInstance()); + // When Penumbra Spider dies, create a 2/4 black Spider creature token with reach. this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new PenumbraSpiderToken()), false)); } diff --git a/Mage.Sets/src/mage/cards/p/Peregrination.java b/Mage.Sets/src/mage/cards/p/Peregrination.java index 6b8b484447b7..e8a9de463c3b 100644 --- a/Mage.Sets/src/mage/cards/p/Peregrination.java +++ b/Mage.Sets/src/mage/cards/p/Peregrination.java @@ -30,7 +30,7 @@ public Peregrination(UUID ownerId, CardSetInfo setInfo) { // Seach your library for up to two basic land cards, reveal those cards, and put one onto the battlefield tapped and the other into your hand. Shuffle your library, then scry 1. this.getSpellAbility().addEffect(new PeregrinationEffect()); Effect effect = new ScryEffect(1); - effect.setText("then scry 1 (Look at the top card of your library. You may put that card on the bottom of your library.)"); + effect.concatBy(", then"); this.getSpellAbility().addEffect(effect); } @@ -50,7 +50,7 @@ class PeregrinationEffect extends OneShotEffect { public PeregrinationEffect() { super(Outcome.PutLandInPlay); - staticText = "Search your library for up to two basic land cards, reveal those cards, and put one onto the battlefield tapped and the other into your hand. Shuffle your library"; + staticText = "Search your library for up to two basic land cards, reveal those cards, and put one onto the battlefield tapped and the other into your hand. Shuffle"; } public PeregrinationEffect(final PeregrinationEffect effect) { diff --git a/Mage.Sets/src/mage/cards/p/PerilousVoyage.java b/Mage.Sets/src/mage/cards/p/PerilousVoyage.java index 3264e12b6d58..774bc9bd3fcb 100644 --- a/Mage.Sets/src/mage/cards/p/PerilousVoyage.java +++ b/Mage.Sets/src/mage/cards/p/PerilousVoyage.java @@ -1,7 +1,5 @@ - package mage.cards.p; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; @@ -16,13 +14,15 @@ import mage.players.Player; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class PerilousVoyage extends CardImpl { - private static final FilterNonlandPermanent filter = new FilterNonlandPermanent("nonland permanent you don't control"); + private static final FilterNonlandPermanent filter + = new FilterNonlandPermanent("nonland permanent you don't control"); static { filter.add(TargetController.NOT_YOU.getControllerPredicate()); @@ -49,11 +49,12 @@ public PerilousVoyage copy() { class PerilousVoyageEffect extends OneShotEffect { PerilousVoyageEffect() { - super(Outcome.Benefit); - this.staticText = "Return target nonland permanent you don't control to its owner's hand. If its converted mana cost was 2 or less, scry 2"; + super(Outcome.ReturnToHand); + this.staticText = "Return target nonland permanent you don't control to its owner's hand. " + + "If its mana value was 2 or less, scry 2"; } - PerilousVoyageEffect(final PerilousVoyageEffect effect) { + private PerilousVoyageEffect(final PerilousVoyageEffect effect) { super(effect); } @@ -66,13 +67,14 @@ public PerilousVoyageEffect copy() { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent != null) { - boolean isLittle = permanent.getConvertedManaCost() < 3; - permanent.moveToZone(Zone.HAND, source, game, true); - if (isLittle && player != null) { - player.scry(2, source, game); - } + if (player == null || permanent == null) { + return false; + } + boolean flag = permanent.getManaValue() <= 2; + player.moveCards(permanent, Zone.HAND, source, game); + if (flag) { + player.scry(2, source, game); } - return false; + return true; } } diff --git a/Mage.Sets/src/mage/cards/p/PerniciousDeed.java b/Mage.Sets/src/mage/cards/p/PerniciousDeed.java index 63c5affa433f..a84867f98a37 100644 --- a/Mage.Sets/src/mage/cards/p/PerniciousDeed.java +++ b/Mage.Sets/src/mage/cards/p/PerniciousDeed.java @@ -15,7 +15,7 @@ import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; /** @@ -46,7 +46,7 @@ class PerniciousDeedEffect extends OneShotEffect { public PerniciousDeedEffect() { super(Outcome.DestroyPermanent); - staticText = "Destroy each artifact, creature, and enchantment with converted mana cost X or less"; + staticText = "Destroy each artifact, creature, and enchantment with mana value X or less"; } public PerniciousDeedEffect(final PerniciousDeedEffect effect) { @@ -67,7 +67,7 @@ public boolean apply(Game game, Ability source) { CardType.ARTIFACT.getPredicate(), CardType.CREATURE.getPredicate(), CardType.ENCHANTMENT.getPredicate())); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, source.getManaCostsToPay().getX() + 1)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, source.getManaCostsToPay().getX() + 1)); return new DestroyAllEffect(filter).apply(game, source); } diff --git a/Mage.Sets/src/mage/cards/p/PerplexingTest.java b/Mage.Sets/src/mage/cards/p/PerplexingTest.java new file mode 100644 index 000000000000..db40d316edad --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PerplexingTest.java @@ -0,0 +1,46 @@ +package mage.cards.p; + +import mage.abilities.Mode; +import mage.abilities.effects.common.ReturnToHandFromBattlefieldAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.TokenPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PerplexingTest extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("nontoken creatures"); + + static { + filter.add(Predicates.not(TokenPredicate.instance)); + } + + public PerplexingTest(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}{U}"); + + // Choose one — + // • Return all creature tokens to their owners' hands. + this.getSpellAbility().addEffect(new ReturnToHandFromBattlefieldAllEffect(StaticFilters.FILTER_CREATURE_TOKENS)); + + // • Return all nontoken creatures to their owners' hands. + this.getSpellAbility().addMode(new Mode(new ReturnToHandFromBattlefieldAllEffect(filter))); + } + + private PerplexingTest(final PerplexingTest card) { + super(card); + } + + @Override + public PerplexingTest copy() { + return new PerplexingTest(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PestInfestation.java b/Mage.Sets/src/mage/cards/p/PestInfestation.java new file mode 100644 index 000000000000..84843fa58fa2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PestInfestation.java @@ -0,0 +1,59 @@ +package mage.cards.p; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.MultipliedValue; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.token.WitherbloomToken; +import mage.target.TargetPermanent; +import mage.target.targetadjustment.TargetAdjuster; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PestInfestation extends CardImpl { + + private static final DynamicValue xValue = new MultipliedValue(ManacostVariableValue.instance, 2); + + public PestInfestation(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{X}{G}"); + + // Destroy up to X target artifacts and/or enchantments. Create twice X 1/1 black and green Pest creature tokens with "When this creature dies, you gain 1 life." + this.getSpellAbility().addEffect(new DestroyTargetEffect() + .setText("destroy up to X target artifacts and/or enchantments.")); + this.getSpellAbility().addEffect(new CreateTokenEffect(new WitherbloomToken(), xValue) + .setText("Create twice X 1/1 black and green Pest creature tokens with \"When this creature dies, you gain 1 life.\"")); + this.getSpellAbility().setTargetAdjuster(PestInfestationAdjuster.instance); + } + + private PestInfestation(final PestInfestation card) { + super(card); + } + + @Override + public PestInfestation copy() { + return new PestInfestation(this); + } +} + +enum PestInfestationAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + ability.getTargets().clear(); + ability.addTarget(new TargetPermanent( + 0, ability.getManaCostsToPay().getX(), + StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT, false + )); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PestSummoning.java b/Mage.Sets/src/mage/cards/p/PestSummoning.java new file mode 100644 index 000000000000..65664350b2d8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PestSummoning.java @@ -0,0 +1,34 @@ +package mage.cards.p; + +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.WitherbloomToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PestSummoning extends CardImpl { + + public PestSummoning(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B/G}{B/G}"); + + this.subtype.add(SubType.LESSON); + + // Create two 1/1 black and green Pest creature tokens with "When this creature dies, you gain 1 life." + this.getSpellAbility().addEffect(new CreateTokenEffect(new WitherbloomToken(), 2)); + } + + private PestSummoning(final PestSummoning card) { + super(card); + } + + @Override + public PestSummoning copy() { + return new PestSummoning(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PestilentCauldron.java b/Mage.Sets/src/mage/cards/p/PestilentCauldron.java new file mode 100644 index 000000000000..8b54f08adc70 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PestilentCauldron.java @@ -0,0 +1,126 @@ +package mage.cards.p; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.common.ControllerGotLifeCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.*; +import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.permanent.token.WitherbloomToken; +import mage.players.Player; +import mage.target.common.TargetCardInASingleGraveyard; +import mage.target.common.TargetCardInYourGraveyard; +import mage.watchers.common.PlayerGainedLifeWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PestilentCauldron extends ModalDoubleFacesCard { + + private static final FilterCard filter + = new FilterCard("creature, land, and/or planeswalker cards from your graveyard"); + + static { + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + CardType.LAND.getPredicate(), + CardType.PLANESWALKER.getPredicate() + )); + } + + public PestilentCauldron(UUID ownerId, CardSetInfo setInfo) { + super( + ownerId, setInfo, + new CardType[]{CardType.ARTIFACT}, new SubType[]{}, "{2}{B}", + "Restorative Burst", + new CardType[]{CardType.SORCERY}, new SubType[]{}, "{3}{G}{G}" + ); + + // 1. + // Pestilent Cauldron + // Artifact + // {T}, Discard a card: Create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life." + Ability ability = new SimpleActivatedAbility( + new CreateTokenEffect(new WitherbloomToken()), new TapSourceCost() + ); + ability.addCost(new DiscardCardCost()); + this.getLeftHalfCard().addAbility(ability); + + // {1}, {T}: Each opponent mills cards equal to the amount of life you gained this turn. + ability = new SimpleActivatedAbility(new MillCardsEachPlayerEffect( + ControllerGotLifeCount.instance, TargetController.OPPONENT + ).setText("each opponent mills cards equal to the amount of life you gained this turn"), new GenericManaCost(1)); + ability.addCost(new TapSourceCost()); + ability.addWatcher(new PlayerGainedLifeWatcher()); + this.getLeftHalfCard().addAbility(ability.addHint(ControllerGotLifeCount.getHint())); + + // {4}, {T}: Exile four target cards from a single graveyard. Draw a card. + ability = new SimpleActivatedAbility(new ExileTargetEffect(), new GenericManaCost(4)); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetCardInASingleGraveyard( + 4, 4, StaticFilters.FILTER_CARD_CARDS + )); + ability.addEffect(new DrawCardSourceControllerEffect(1)); + this.getLeftHalfCard().addAbility(ability); + + // 2. + // Restorative Burst + // Sorcery + // Return up to two target creature, land, and/or planeswalker cards from your graveyard to your hand. Each player gains 4 life. Exile Restorative Burst. + this.getRightHalfCard().getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); + this.getRightHalfCard().getSpellAbility().addEffect(new RestorativeBurstEffect()); + this.getRightHalfCard().getSpellAbility().addEffect(new ExileSpellEffect()); + this.getRightHalfCard().getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, filter)); + } + + private PestilentCauldron(final PestilentCauldron card) { + super(card); + } + + @Override + public PestilentCauldron copy() { + return new PestilentCauldron(this); + } +} + +class RestorativeBurstEffect extends OneShotEffect { + + RestorativeBurstEffect() { + super(Outcome.GainLife); + staticText = "Each player gains 4 life."; + } + + private RestorativeBurstEffect(final RestorativeBurstEffect effect) { + super(effect); + } + + @Override + public RestorativeBurstEffect copy() { + return new RestorativeBurstEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + player.gainLife(4, game, source); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/p/PhantomWings.java b/Mage.Sets/src/mage/cards/p/PhantomWings.java index d986f34b8b74..8318431b711d 100644 --- a/Mage.Sets/src/mage/cards/p/PhantomWings.java +++ b/Mage.Sets/src/mage/cards/p/PhantomWings.java @@ -1,7 +1,5 @@ - package mage.cards.p; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; @@ -13,38 +11,38 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AttachmentType; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class PhantomWings extends CardImpl { public PhantomWings(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); this.subtype.add(SubType.AURA); - // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); + // Enchanted creature has flying. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(FlyingAbility.getInstance(), AttachmentType.AURA))); + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( + FlyingAbility.getInstance(), AttachmentType.AURA + ))); + // Sacrifice Phantom Wings: Return enchanted creature to its owner's hand. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new PhantomWingsReturnEffect(), new SacrificeSourceCost())); - + this.addAbility(new SimpleActivatedAbility(new PhantomWingsReturnEffect(), new SacrificeSourceCost())); } private PhantomWings(final PhantomWings card) { @@ -56,14 +54,14 @@ public PhantomWings copy() { return new PhantomWings(this); } - private static class PhantomWingsReturnEffect extends OneShotEffect { + private static final class PhantomWingsReturnEffect extends OneShotEffect { - public PhantomWingsReturnEffect() { + private PhantomWingsReturnEffect() { super(Outcome.ReturnToHand); staticText = "Return enchanted creature to its owner's hand"; } - public PhantomWingsReturnEffect(final PhantomWingsReturnEffect effect) { + private PhantomWingsReturnEffect(final PhantomWingsReturnEffect effect) { super(effect); } @@ -74,15 +72,13 @@ public PhantomWingsReturnEffect copy() { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (permanent != null && permanent.getAttachedTo() != null) - { - Permanent enchantedCreature = game.getPermanent(permanent.getAttachedTo()); - if (enchantedCreature != null) { - return enchantedCreature.moveToZone(Zone.HAND, source, game, false); - } - } - return false; + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = source.getSourcePermanentOrLKI(game); + if (player == null || permanent == null || permanent.getAttachedTo() == null) { + return false; + } + Permanent enchantedCreature = game.getPermanent(permanent.getAttachedTo()); + return enchantedCreature != null && player.moveCards(enchantedCreature, Zone.HAND, source, game); } } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/p/PhaseDolphin.java b/Mage.Sets/src/mage/cards/p/PhaseDolphin.java index 86173b7b7756..f157acc96dd1 100644 --- a/Mage.Sets/src/mage/cards/p/PhaseDolphin.java +++ b/Mage.Sets/src/mage/cards/p/PhaseDolphin.java @@ -10,7 +10,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterAttackingCreature; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianDelver.java b/Mage.Sets/src/mage/cards/p/PhyrexianDelver.java index cc192608b9c2..cd04e49362f6 100644 --- a/Mage.Sets/src/mage/cards/p/PhyrexianDelver.java +++ b/Mage.Sets/src/mage/cards/p/PhyrexianDelver.java @@ -53,7 +53,7 @@ class PhyrexianDelverEffect extends OneShotEffect { public PhyrexianDelverEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "return target creature card from your graveyard to the battlefield. You lose life equal to that card's converted mana cost"; + this.staticText = "return target creature card from your graveyard to the battlefield. You lose life equal to that card's mana value"; } public PhyrexianDelverEffect(final PhyrexianDelverEffect effect) { @@ -74,7 +74,7 @@ public boolean apply(Game game, Ability source) { if (game.getState().getZone(creatureCard.getId()) == Zone.GRAVEYARD) { result = controller.moveCards(creatureCard, Zone.BATTLEFIELD, source, game); } - controller.loseLife(creatureCard.getConvertedManaCost(), game, source, false); + controller.loseLife(creatureCard.getManaValue(), game, source, false); return result; } return false; diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianDevourer.java b/Mage.Sets/src/mage/cards/p/PhyrexianDevourer.java index b25aade12174..a6bea763ef54 100644 --- a/Mage.Sets/src/mage/cards/p/PhyrexianDevourer.java +++ b/Mage.Sets/src/mage/cards/p/PhyrexianDevourer.java @@ -86,7 +86,7 @@ class PhyrexianDevourerEffect extends OneShotEffect { public PhyrexianDevourerEffect() { super(Outcome.BoostCreature); - this.staticText = "Put X +1/+1 counters on {this}, where X is the exiled card's converted mana cost"; + this.staticText = "Put X +1/+1 counters on {this}, where X is the exiled card's mana value"; } public PhyrexianDevourerEffect(final PhyrexianDevourerEffect effect) { @@ -109,7 +109,7 @@ public boolean apply(Game game, Ability source) { } } if (card != null) { - int amount = card.getConvertedManaCost(); + int amount = card.getManaValue(); if (amount > 0) { return new AddCountersSourceEffect(CounterType.P1P1.createInstance(amount)).apply(game, source); } diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianDreadnought.java b/Mage.Sets/src/mage/cards/p/PhyrexianDreadnought.java index 4a72ceb7fba1..c8f0c49bc24d 100644 --- a/Mage.Sets/src/mage/cards/p/PhyrexianDreadnought.java +++ b/Mage.Sets/src/mage/cards/p/PhyrexianDreadnought.java @@ -16,7 +16,7 @@ import mage.constants.Outcome; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianNegator.java b/Mage.Sets/src/mage/cards/p/PhyrexianNegator.java index 56a803f6dd09..3f22d7d60fb1 100644 --- a/Mage.Sets/src/mage/cards/p/PhyrexianNegator.java +++ b/Mage.Sets/src/mage/cards/p/PhyrexianNegator.java @@ -63,7 +63,7 @@ public PhyrexianNegatorTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianObliterator.java b/Mage.Sets/src/mage/cards/p/PhyrexianObliterator.java index 389587460f14..7a3173044666 100644 --- a/Mage.Sets/src/mage/cards/p/PhyrexianObliterator.java +++ b/Mage.Sets/src/mage/cards/p/PhyrexianObliterator.java @@ -66,7 +66,7 @@ public PhyrexianObliteratorTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianPurge.java b/Mage.Sets/src/mage/cards/p/PhyrexianPurge.java index 5ab26d3e36e1..b73602680d1e 100644 --- a/Mage.Sets/src/mage/cards/p/PhyrexianPurge.java +++ b/Mage.Sets/src/mage/cards/p/PhyrexianPurge.java @@ -27,8 +27,8 @@ public PhyrexianPurge(UUID ownerId, CardSetInfo setInfo) { // Destroy any number of target creatures. // Phyrexian Purge costs 3 life more to cast for each target. this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, Integer.MAX_VALUE)); - this.getSpellAbility().addEffect(new DestroyMultiTargetEffect()); - this.getSpellAbility().addEffect(new InfoEffect("

{this} costs 3 life more to cast for each target")); + this.getSpellAbility().addEffect(new InfoEffect("this spell costs 3 life more to cast for each target")); + this.getSpellAbility().addEffect(new DestroyMultiTargetEffect().concatBy("
")); this.getSpellAbility().setTargetAdjuster(PhyrexianPurgeTargetAdjuster.instance); this.getSpellAbility().setCostAdjuster(PhyrexianPurgeCostAdjuster.instance); } diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianRebirth.java b/Mage.Sets/src/mage/cards/p/PhyrexianRebirth.java index 35bea5d9633a..22bd1af0a123 100644 --- a/Mage.Sets/src/mage/cards/p/PhyrexianRebirth.java +++ b/Mage.Sets/src/mage/cards/p/PhyrexianRebirth.java @@ -1,7 +1,5 @@ - package mage.cards.p; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; @@ -13,8 +11,9 @@ import mage.game.permanent.Permanent; import mage.game.permanent.token.PhyrexianRebirthHorrorToken; +import java.util.UUID; + /** - * * @author ayratn */ public final class PhyrexianRebirth extends CardImpl { @@ -35,27 +34,29 @@ public PhyrexianRebirth copy() { return new PhyrexianRebirth(this); } - class PhyrexianRebirthEffect extends OneShotEffect { + private static final class PhyrexianRebirthEffect extends OneShotEffect { - public PhyrexianRebirthEffect() { + private PhyrexianRebirthEffect() { super(Outcome.DestroyPermanent); - staticText = "Destroy all creatures, then create an X/X colorless Horror artifact creature token, where X is the number of creatures destroyed this way"; + staticText = "Destroy all creatures, then create an X/X colorless Horror artifact creature token, " + + "where X is the number of creatures destroyed this way"; } - public PhyrexianRebirthEffect(PhyrexianRebirthEffect ability) { + private PhyrexianRebirthEffect(PhyrexianRebirthEffect ability) { super(ability); } @Override public boolean apply(Game game, Ability source) { int count = 0; - for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game + )) { count += permanent.destroy(source, game, false) ? 1 : 0; } - PhyrexianRebirthHorrorToken horrorToken = new PhyrexianRebirthHorrorToken(); - horrorToken.getPower().modifyBaseValue(count); - horrorToken.getToughness().modifyBaseValue(count); - horrorToken.putOntoBattlefield(1, game, source, source.getControllerId()); + new PhyrexianRebirthHorrorToken(count, count).putOntoBattlefield( + 1, game, source, source.getControllerId() + ); return true; } @@ -63,7 +64,5 @@ public boolean apply(Game game, Ability source) { public PhyrexianRebirthEffect copy() { return new PhyrexianRebirthEffect(this); } - } - } diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianTotem.java b/Mage.Sets/src/mage/cards/p/PhyrexianTotem.java index 54c1bdcde916..5fb1dfdf7512 100644 --- a/Mage.Sets/src/mage/cards/p/PhyrexianTotem.java +++ b/Mage.Sets/src/mage/cards/p/PhyrexianTotem.java @@ -98,7 +98,7 @@ public boolean checkInterveningIfClause(Game game) { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianUnlife.java b/Mage.Sets/src/mage/cards/p/PhyrexianUnlife.java index 22120169f305..42302257da6b 100644 --- a/Mage.Sets/src/mage/cards/p/PhyrexianUnlife.java +++ b/Mage.Sets/src/mage/cards/p/PhyrexianUnlife.java @@ -1,42 +1,34 @@ - package mage.cards.p; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.continuous.DontLoseByZeroOrLessLifeEffect; -import mage.abilities.keyword.LifelinkAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; -import mage.counters.CounterType; import mage.game.Game; -import mage.game.events.DamagePlayerEvent; -import mage.game.events.DamagedPlayerEvent; +import mage.game.events.DamageEvent; import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** - * * @author BetaSteward */ public final class PhyrexianUnlife extends CardImpl { public PhyrexianUnlife(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{W}"); - + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); // You don't lose the game for having 0 or less life. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontLoseByZeroOrLessLifeEffect(Duration.WhileOnBattlefield))); + this.addAbility(new SimpleStaticAbility(new DontLoseByZeroOrLessLifeEffect(Duration.WhileOnBattlefield))); // As long as you have 0 or less life, all damage is dealt to you as though its source had infect. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PhyrexianUnlifeEffect2())); - + this.addAbility(new SimpleStaticAbility(new PhyrexianUnlifeEffect())); } private PhyrexianUnlife(final PhyrexianUnlife card) { @@ -49,72 +41,61 @@ public PhyrexianUnlife copy() { } } -class PhyrexianUnlifeEffect2 extends ReplacementEffectImpl { +class PhyrexianUnlifeEffect extends ReplacementEffectImpl { - int lastCheckedDamageStepNum = 0; - boolean startedWithLifeAboveZero = false; + private int lastCheckedDamageStepNum = 0; + private boolean startedWithLifeAboveZero = false; - public PhyrexianUnlifeEffect2() { + PhyrexianUnlifeEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit); staticText = "As long as you have 0 or less life, all damage is dealt to you as though its source had infect"; } - public PhyrexianUnlifeEffect2(final PhyrexianUnlifeEffect2 effect) { + private PhyrexianUnlifeEffect(final PhyrexianUnlifeEffect effect) { super(effect); this.lastCheckedDamageStepNum = effect.lastCheckedDamageStepNum; this.startedWithLifeAboveZero = effect.startedWithLifeAboveZero; } @Override - public PhyrexianUnlifeEffect2 copy() { - return new PhyrexianUnlifeEffect2(this); + public PhyrexianUnlifeEffect copy() { + return new PhyrexianUnlifeEffect(this); } @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - DamagePlayerEvent damageEvent = (DamagePlayerEvent) event; - int actualDamage = damageEvent.getAmount(); - if (actualDamage > 0) { - Player player = game.getPlayer(damageEvent.getPlayerId()); - Permanent damageSource = game.getPermanent(damageEvent.getSourceId()); - player.addCounters(CounterType.POISON.createInstance(actualDamage), source.getControllerId(), source, game); - if (damageSource != null && damageSource.getAbilities().containsKey(LifelinkAbility.getInstance().getId())) { - Player controlPlayer = game.getPlayer(damageSource.getControllerId()); - controlPlayer.gainLife(actualDamage, game, source); - } - game.fireEvent(new DamagedPlayerEvent(damageEvent.getPlayerId(), damageEvent.getSourceId(), damageEvent.getPlayerId(), actualDamage, damageEvent.isCombatDamage())); - } - return true; + ((DamageEvent) event).setAsThoughInfect(true); + return false; } - + @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.DAMAGE_PLAYER; } - + @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getPlayerId().equals(source.getControllerId())) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - // The decision if the player has more than 0 life has to be checked only at start of a combat damage step - // and all damage in the same step has to be handled the same beause by the rules it's all done at once - if (((DamagePlayerEvent) event).isCombatDamage()) { - if (lastCheckedDamageStepNum != game.getState().getStepNum()) { - lastCheckedDamageStepNum = game.getState().getStepNum(); - startedWithLifeAboveZero = player.getLife() > 0; - } - if (startedWithLifeAboveZero) { - return false; - } - } - if (player.getLife() <= 0) { - return true; - } + if (!event.getPlayerId().equals(source.getControllerId())) { + return false; + } + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + // The decision if the player has more than 0 life has to be checked only at start of a combat damage step + // and all damage in the same step has to be handled the same beause by the rules it's all done at once + if (((DamageEvent) event).isCombatDamage()) { + if (lastCheckedDamageStepNum != game.getState().getStepNum()) { + lastCheckedDamageStepNum = game.getState().getStepNum(); + startedWithLifeAboveZero = player.getLife() > 0; + } + if (startedWithLifeAboveZero) { + return false; } } - + if (player.getLife() < 1) { + return true; + } return false; } - } diff --git a/Mage.Sets/src/mage/cards/p/Phytohydra.java b/Mage.Sets/src/mage/cards/p/Phytohydra.java index 44a4ec16c856..68ec49d7f159 100644 --- a/Mage.Sets/src/mage/cards/p/Phytohydra.java +++ b/Mage.Sets/src/mage/cards/p/Phytohydra.java @@ -11,7 +11,7 @@ import mage.constants.*; import mage.counters.CounterType; import mage.game.Game; -import mage.game.events.DamageCreatureEvent; +import mage.game.events.DamageEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -55,7 +55,7 @@ class PhytohydraEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - DamageCreatureEvent damageEvent = (DamageCreatureEvent) event; + DamageEvent damageEvent = (DamageEvent) event; Permanent p = game.getPermanent(source.getSourceId()); if (p != null) { p.addCounters(CounterType.P1P1.createInstance(damageEvent.getAmount()), source.getControllerId(), source, game); @@ -65,7 +65,7 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/p/PiaNalaar.java b/Mage.Sets/src/mage/cards/p/PiaNalaar.java index f0a0e60f8eda..6056d4b9f345 100644 --- a/Mage.Sets/src/mage/cards/p/PiaNalaar.java +++ b/Mage.Sets/src/mage/cards/p/PiaNalaar.java @@ -44,7 +44,7 @@ public PiaNalaar(UUID ownerId, CardSetInfo setInfo) { // {1}{R}: Target artifact creature gets +1/+0 until end of turn. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl<>("{1}{R}")); - ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_ARTIFACT_CREATURE_PERMANENT)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_CREATURE)); this.addAbility(ability); // {1}, Sacrifice an artifact: Target creature can't block this turn. diff --git a/Mage.Sets/src/mage/cards/p/PickTheBrain.java b/Mage.Sets/src/mage/cards/p/PickTheBrain.java index 6ee165fa7026..45a96d78c3a8 100644 --- a/Mage.Sets/src/mage/cards/p/PickTheBrain.java +++ b/Mage.Sets/src/mage/cards/p/PickTheBrain.java @@ -89,6 +89,6 @@ public String getText(Mode mode) { return "Target opponent reveals their hand. You choose a nonland card from it and exile that card.

" + "Delirium — If there are four or more card types among cards in your graveyard, " + "search that player's graveyard, hand, and library for any number of cards " - + "with the same name as the exiled card, exile those cards, then that player shuffles their library"; + + "with the same name as the exiled card, exile those cards, then that player shuffles"; } } diff --git a/Mage.Sets/src/mage/cards/p/PigmentStorm.java b/Mage.Sets/src/mage/cards/p/PigmentStorm.java new file mode 100644 index 000000000000..d9d833bea320 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PigmentStorm.java @@ -0,0 +1,32 @@ +package mage.cards.p; + +import mage.abilities.effects.common.DamageWithExcessEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PigmentStorm extends CardImpl { + + public PigmentStorm(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}{R}"); + + // Pigment Storm deals 5 damage to target creature. Excess damage is dealt to that creature's controller instead. + this.getSpellAbility().addEffect(new DamageWithExcessEffect(5)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private PigmentStorm(final PigmentStorm card) { + super(card); + } + + @Override + public PigmentStorm copy() { + return new PigmentStorm(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PilgrimOfTheAges.java b/Mage.Sets/src/mage/cards/p/PilgrimOfTheAges.java new file mode 100644 index 000000000000..f3eb502311a1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PilgrimOfTheAges.java @@ -0,0 +1,58 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToHandEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PilgrimOfTheAges extends CardImpl { + + private static final FilterCard filter = new FilterCard("a basic Plains card"); + + static { + filter.add(SuperType.BASIC.getPredicate()); + filter.add(SubType.PLAINS.getPredicate()); + } + + public PilgrimOfTheAges(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // When Pilgrim of the Ages enters the battlefield, you may search your library for a basic Plains card, reveal it, put it into your hand, then shuffle. + this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandEffect( + new TargetCardInLibrary(filter), true, true + ), true)); + + // {6}: Return Pilgrim of the Ages from your graveyard to your hand. + this.addAbility(new SimpleActivatedAbility( + Zone.GRAVEYARD, new ReturnSourceFromGraveyardToHandEffect(), new GenericManaCost(6) + )); + } + + private PilgrimOfTheAges(final PilgrimOfTheAges card) { + super(card); + } + + @Override + public PilgrimOfTheAges copy() { + return new PilgrimOfTheAges(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PilgrimsEye.java b/Mage.Sets/src/mage/cards/p/PilgrimsEye.java index b2d5dadf669e..6be15f3761f6 100644 --- a/Mage.Sets/src/mage/cards/p/PilgrimsEye.java +++ b/Mage.Sets/src/mage/cards/p/PilgrimsEye.java @@ -1,5 +1,3 @@ - - package mage.cards.p; import mage.MageInt; @@ -10,38 +8,30 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.SuperType; -import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.target.common.TargetCardInLibrary; import java.util.UUID; /** - * * @author Loki */ public final class PilgrimsEye extends CardImpl { - private static final FilterCard filter = new FilterCard("basic land card"); - static { - filter.add(SuperType.BASIC.getPredicate()); - filter.add(CardType.LAND.getPredicate()); - } - - public PilgrimsEye (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{3}"); + public PilgrimsEye(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); this.subtype.add(SubType.THOPTER); this.power = new MageInt(1); this.toughness = new MageInt(1); this.addAbility(FlyingAbility.getInstance()); + // When Pilgrim's Eye enters the battlefield, you may search your library for a basic land card, reveal it, put it into your hand, then shuffle your library. - this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true, true), true)); + this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true), true)); } - public PilgrimsEye (final PilgrimsEye card) { + public PilgrimsEye(final PilgrimsEye card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/p/PillardropRescuer.java b/Mage.Sets/src/mage/cards/p/PillardropRescuer.java new file mode 100644 index 000000000000..0cf5fce39806 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PillardropRescuer.java @@ -0,0 +1,56 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.keyword.FlyingAbility; +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.predicate.mageobject.ManaValuePredicate; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PillardropRescuer extends CardImpl { + + private static final FilterCard filter + = new FilterCard("creature card with mana value 3 or less from your graveyard"); + + static { + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); + } + + public PillardropRescuer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}"); + + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Pillardrop Rescuer enters the battlefield, return target creature card with mana value 3 or less from your graveyard to your hand. + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect()); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + } + + private PillardropRescuer(final PillardropRescuer card) { + super(card); + } + + @Override + public PillardropRescuer copy() { + return new PillardropRescuer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PillardropWarden.java b/Mage.Sets/src/mage/cards/p/PillardropWarden.java new file mode 100644 index 000000000000..a18517c68175 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PillardropWarden.java @@ -0,0 +1,58 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.common.FilterInstantOrSorceryCard; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PillardropWarden extends CardImpl { + + private static final FilterCard filter + = new FilterInstantOrSorceryCard("instant or sorcery card from your graveyard"); + + public PillardropWarden(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.DWARF); + this.power = new MageInt(1); + this.toughness = new MageInt(5); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // {2}, {T}, Sacrifice Pillardrop Warden: Return target instant or sorcery card from your graveyard to your hand. Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility( + new ReturnFromGraveyardToHandTargetEffect(), new GenericManaCost(2) + ); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + } + + private PillardropWarden(final PillardropWarden card) { + super(card); + } + + @Override + public PillardropWarden copy() { + return new PillardropWarden(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PiousEvangel.java b/Mage.Sets/src/mage/cards/p/PiousEvangel.java index 3b30ad6aa5bf..e4dd6ae851da 100644 --- a/Mage.Sets/src/mage/cards/p/PiousEvangel.java +++ b/Mage.Sets/src/mage/cards/p/PiousEvangel.java @@ -21,7 +21,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledPermanent; /** diff --git a/Mage.Sets/src/mage/cards/p/PiousWarrior.java b/Mage.Sets/src/mage/cards/p/PiousWarrior.java index 3d1f0ee0a472..d2ec1767b59b 100644 --- a/Mage.Sets/src/mage/cards/p/PiousWarrior.java +++ b/Mage.Sets/src/mage/cards/p/PiousWarrior.java @@ -13,9 +13,8 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamagedCreatureEvent; +import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.players.Player; /** @@ -64,12 +63,12 @@ public PiousWarriorTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.sourceId) && ((DamagedCreatureEvent)event).isCombatDamage() ) { + if (event.getTargetId().equals(this.sourceId) && ((DamagedEvent)event).isCombatDamage() ) { this.getEffects().get(0).setValue("damageAmount", event.getAmount()); return true; } diff --git a/Mage.Sets/src/mage/cards/p/PirsWhim.java b/Mage.Sets/src/mage/cards/p/PirsWhim.java index 535d5045b823..eeb076cacc2a 100644 --- a/Mage.Sets/src/mage/cards/p/PirsWhim.java +++ b/Mage.Sets/src/mage/cards/p/PirsWhim.java @@ -47,7 +47,7 @@ class PirsWhimEffect extends OneShotEffect { super(Outcome.Benefit); this.staticText = "For each player, choose friend or foe. " + "Each friend searches their library for a land card, " - + "puts it onto the battlefield tapped, then shuffles their library. " + + "puts it onto the battlefield tapped, then shuffles. " + "Each foe sacrifices an artifact or enchantment they control."; } diff --git a/Mage.Sets/src/mage/cards/p/PitFight.java b/Mage.Sets/src/mage/cards/p/PitFight.java index 8419d9273384..5db874c40305 100644 --- a/Mage.Sets/src/mage/cards/p/PitFight.java +++ b/Mage.Sets/src/mage/cards/p/PitFight.java @@ -7,7 +7,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/p/PitilessPlunderer.java b/Mage.Sets/src/mage/cards/p/PitilessPlunderer.java index d473fb1a03e1..75d7c90e0df4 100644 --- a/Mage.Sets/src/mage/cards/p/PitilessPlunderer.java +++ b/Mage.Sets/src/mage/cards/p/PitilessPlunderer.java @@ -11,7 +11,7 @@ import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.TreasureToken; /** diff --git a/Mage.Sets/src/mage/cards/p/PlagueBelcher.java b/Mage.Sets/src/mage/cards/p/PlagueBelcher.java index 8b4a441cb623..822d4429dfb0 100644 --- a/Mage.Sets/src/mage/cards/p/PlagueBelcher.java +++ b/Mage.Sets/src/mage/cards/p/PlagueBelcher.java @@ -16,7 +16,7 @@ import mage.constants.TargetController; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/p/PlagueReaver.java b/Mage.Sets/src/mage/cards/p/PlagueReaver.java index 1453d09e7cf8..d3c1801198f2 100644 --- a/Mage.Sets/src/mage/cards/p/PlagueReaver.java +++ b/Mage.Sets/src/mage/cards/p/PlagueReaver.java @@ -15,7 +15,7 @@ import mage.filter.FilterCard; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/p/Plaguebearer.java b/Mage.Sets/src/mage/cards/p/Plaguebearer.java index 382988507909..270cacdfc2cb 100644 --- a/Mage.Sets/src/mage/cards/p/Plaguebearer.java +++ b/Mage.Sets/src/mage/cards/p/Plaguebearer.java @@ -24,7 +24,7 @@ */ public final class Plaguebearer extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("nonblack creature with converted mana cost X"); + private static final FilterPermanent filter = new FilterPermanent("nonblack creature with mana value X"); static { filter.add(CardType.CREATURE.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/p/PlanarChaos.java b/Mage.Sets/src/mage/cards/p/PlanarChaos.java index 9b549738f084..993b9c2c451d 100644 --- a/Mage.Sets/src/mage/cards/p/PlanarChaos.java +++ b/Mage.Sets/src/mage/cards/p/PlanarChaos.java @@ -15,6 +15,7 @@ import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.Spell; @@ -33,7 +34,7 @@ public PlanarChaos(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new BeginningOfUpkeepTriggeredAbility(new PlanarChaosUpkeepEffect(), TargetController.YOU, false)); // Whenever a player casts a spell, that player flips a coin. If they lose the flip, counter that spell. - this.addAbility(new SpellCastAllTriggeredAbility(new PlanarChaosCastAllEffect(), new FilterSpell("a spell"), false, SetTargetPointer.SPELL)); + this.addAbility(new SpellCastAllTriggeredAbility(new PlanarChaosCastAllEffect(), StaticFilters.FILTER_SPELL_A, false, SetTargetPointer.SPELL)); } private PlanarChaos(final PlanarChaos card) { diff --git a/Mage.Sets/src/mage/cards/p/PlaneswalkersFavor.java b/Mage.Sets/src/mage/cards/p/PlaneswalkersFavor.java index 2fd191ff7640..62f9bb6f814c 100644 --- a/Mage.Sets/src/mage/cards/p/PlaneswalkersFavor.java +++ b/Mage.Sets/src/mage/cards/p/PlaneswalkersFavor.java @@ -56,7 +56,7 @@ class PlaneswalkersFavorEffect extends OneShotEffect { public PlaneswalkersFavorEffect() { super(Outcome.Damage); - staticText = "Target opponent reveals a card at random from their hand. Target creature gets +X/+X until end of turn, where X is the revealed card's converted mana cost"; + staticText = "Target opponent reveals a card at random from their hand. Target creature gets +X/+X until end of turn, where X is the revealed card's mana value"; } public PlaneswalkersFavorEffect(final PlaneswalkersFavorEffect effect) { @@ -71,7 +71,7 @@ public boolean apply(Game game, Ability source) { Card card = opponent.getHand().getRandom(game); if (card != null) { revealed.add(card); - int boostValue = card.getConvertedManaCost(); + int boostValue = card.getManaValue(); opponent.revealCards("Planeswalker's Favor", revealed, game); ContinuousEffect effect = new BoostTargetEffect(boostValue, boostValue, Duration.EndOfTurn); effect.setTargetPointer(new FixedTarget(source.getTargets().get(1).getFirstTarget())); diff --git a/Mage.Sets/src/mage/cards/p/PlaneswalkersFury.java b/Mage.Sets/src/mage/cards/p/PlaneswalkersFury.java index 0195e886610c..c6817d6ee832 100644 --- a/Mage.Sets/src/mage/cards/p/PlaneswalkersFury.java +++ b/Mage.Sets/src/mage/cards/p/PlaneswalkersFury.java @@ -42,7 +42,7 @@ class PlaneswalkersFuryEffect extends OneShotEffect { public PlaneswalkersFuryEffect() { super(Outcome.Damage); - staticText = "Target opponent reveals a card at random from their hand. Planeswalker's Fury deals damage equal to that card's converted mana cost to that player"; + staticText = "Target opponent reveals a card at random from their hand. Planeswalker's Fury deals damage equal to that card's mana value to that player"; } public PlaneswalkersFuryEffect(final PlaneswalkersFuryEffect effect) { @@ -58,7 +58,7 @@ public boolean apply(Game game, Ability source) { if (card != null) { revealed.add(card); opponent.revealCards("Planeswalker's Fury", revealed, game); - opponent.damage(card.getConvertedManaCost(), source.getSourceId(), source, game); + opponent.damage(card.getManaValue(), source.getSourceId(), source, game); } return true; } diff --git a/Mage.Sets/src/mage/cards/p/PlaneswalkersMirth.java b/Mage.Sets/src/mage/cards/p/PlaneswalkersMirth.java index 23e82793036c..a467798081fd 100644 --- a/Mage.Sets/src/mage/cards/p/PlaneswalkersMirth.java +++ b/Mage.Sets/src/mage/cards/p/PlaneswalkersMirth.java @@ -47,7 +47,7 @@ class PlaneswalkersMirthEffect extends OneShotEffect { public PlaneswalkersMirthEffect() { super(Outcome.Damage); - staticText = "Target opponent reveals a card at random from their hand. You gain life equal to that card's converted mana cost"; + staticText = "Target opponent reveals a card at random from their hand. You gain life equal to that card's mana value"; } public PlaneswalkersMirthEffect(final PlaneswalkersMirthEffect effect) { @@ -64,7 +64,7 @@ public boolean apply(Game game, Ability source) { if (card != null) { revealed.add(card); opponent.revealCards("Planeswalker's Mirth", revealed, game); - player.gainLife(card.getConvertedManaCost(), game, source); + player.gainLife(card.getManaValue(), game, source); } return true; } diff --git a/Mage.Sets/src/mage/cards/p/PlaneswalkersMischief.java b/Mage.Sets/src/mage/cards/p/PlaneswalkersMischief.java index 0cfb3a5c42ef..565c3a6c02c6 100644 --- a/Mage.Sets/src/mage/cards/p/PlaneswalkersMischief.java +++ b/Mage.Sets/src/mage/cards/p/PlaneswalkersMischief.java @@ -80,7 +80,7 @@ public boolean apply(Game game, Ability source) { AsThoughEffect effect = new PlaneswalkersMischiefCastFromExileEffect(); effect.setTargetPointer(new FixedTarget(revealedCard.getId())); game.addEffect(effect, source); - OneShotEffect effect2 = new ReturnFromExileEffect(source.getSourceId(), Zone.HAND); + OneShotEffect effect2 = new ReturnFromExileEffect(Zone.HAND); Condition condition = new PlaneswalkersMischiefCondition(source.getSourceId(), revealedCard.getId()); ConditionalOneShotEffect effect3 = new ConditionalOneShotEffect(effect2, condition, "if you haven't cast it, return it to its owner's hand."); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect3); diff --git a/Mage.Sets/src/mage/cards/p/PlaneswalkersScorn.java b/Mage.Sets/src/mage/cards/p/PlaneswalkersScorn.java index 5e0a72d985a7..8b80b4a4278f 100644 --- a/Mage.Sets/src/mage/cards/p/PlaneswalkersScorn.java +++ b/Mage.Sets/src/mage/cards/p/PlaneswalkersScorn.java @@ -56,7 +56,7 @@ class PlaneswalkersScornEffect extends OneShotEffect { public PlaneswalkersScornEffect() { super(Outcome.Damage); - staticText = "Target opponent reveals a card at random from their hand. Target creature gets -X/-X until end of turn, where X is the revealed card's converted mana cost"; + staticText = "Target opponent reveals a card at random from their hand. Target creature gets -X/-X until end of turn, where X is the revealed card's mana value"; } public PlaneswalkersScornEffect(final PlaneswalkersScornEffect effect) { @@ -71,7 +71,7 @@ public boolean apply(Game game, Ability source) { Card card = opponent.getHand().getRandom(game); if (card != null) { revealed.add(card); - int boostValue = -1 * card.getConvertedManaCost(); + int boostValue = -1 * card.getManaValue(); opponent.revealCards("Planeswalker's Scorn", revealed, game); ContinuousEffect effect = new BoostTargetEffect(boostValue, boostValue, Duration.EndOfTurn); effect.setTargetPointer(new FixedTarget(source.getTargets().get(1).getFirstTarget())); diff --git a/Mage.Sets/src/mage/cards/p/PlarggDeanOfChaos.java b/Mage.Sets/src/mage/cards/p/PlarggDeanOfChaos.java new file mode 100644 index 000000000000..9dad935a3ff6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PlarggDeanOfChaos.java @@ -0,0 +1,185 @@ +package mage.cards.p; + +import mage.ApprovingObject; +import mage.abilities.Ability; +import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.UntapAllControllerEffect; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.cards.Card; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.cards.ModalDoubleFacesCard; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.filter.predicate.permanent.UntappedPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class PlarggDeanOfChaos extends ModalDoubleFacesCard { + + private static final FilterCreaturePermanent tappedFilter = new FilterCreaturePermanent("tapped creatures you control"); + private static final FilterCreaturePermanent untappedFilter = new FilterCreaturePermanent("untapped creatures you control"); + + static { + tappedFilter.add(TappedPredicate.instance); + tappedFilter.add(TargetController.YOU.getControllerPredicate()); + + untappedFilter.add(UntappedPredicate.instance); + untappedFilter.add(TargetController.YOU.getControllerPredicate()); + } + + public PlarggDeanOfChaos(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.ORC, SubType.SHAMAN}, "{1}{R}", + "Augusta, Dean of Order", new CardType[]{CardType.CREATURE}, new SubType[]{SubType.HUMAN, SubType.CLERIC}, "{2}{W}"); + + // 1. + // Plargg, Dean of Chaos + // Legendary Creature - Orc Shaman + this.getLeftHalfCard().addSuperType(SuperType.LEGENDARY); + this.getLeftHalfCard().setPT(2, 2); + + // {T}, Discard a card: Draw a card. + SimpleActivatedAbility rummageAbility = new SimpleActivatedAbility(new DrawCardSourceControllerEffect(1), new TapSourceCost()); + rummageAbility.addCost(new DiscardCardCost()); + this.getLeftHalfCard().addAbility(rummageAbility); + + // {4}{R}, {T}: Reveal cards from the top of your library until you reveal a nonlegendary, nonland card with mana value 3 or less. You may cast that card without paying its mana cost. Put all revealed cards not cast this way on the bottom of your library in a random order. + SimpleActivatedAbility ability = new SimpleActivatedAbility(new PlarggDeanOfChaosEffect(), new ManaCostsImpl<>("{4}{R}")); + ability.addCost(new TapSourceCost()); + this.getLeftHalfCard().addAbility(ability); + + // 2. + // Augusta, Dean of Order + // Legendary Creature - Human Cleric + this.getRightHalfCard().addSuperType(SuperType.LEGENDARY); + this.getRightHalfCard().setPT(1, 3); + + // Other tapped creatures you control get +1/+0. + this.getRightHalfCard().addAbility(new SimpleStaticAbility(new BoostAllEffect( + StaticValue.get(1), StaticValue.get(0), Duration.WhileOnBattlefield, tappedFilter, true))); + + // Other untapped creatures you control get +0/+1. + this.getRightHalfCard().addAbility(new SimpleStaticAbility(new BoostAllEffect( + StaticValue.get(0), StaticValue.get(1), Duration.WhileOnBattlefield, untappedFilter, true))); + + // Whenever you attack, untap each creature you control, then tap any number of creatures you control. + AttacksWithCreaturesTriggeredAbility augustaAbility = new AttacksWithCreaturesTriggeredAbility( + new UntapAllControllerEffect(StaticFilters.FILTER_PERMANENT_CREATURES, "untap each creature you control"), 0); + augustaAbility.addEffect(new AugustaDeanOfOrderEffect().concatBy(", then")); + this.getRightHalfCard().addAbility(augustaAbility); + } + + private PlarggDeanOfChaos(final PlarggDeanOfChaos card) { + super(card); + } + + @Override + public PlarggDeanOfChaos copy() { + return new PlarggDeanOfChaos(this); + } +} + +class PlarggDeanOfChaosEffect extends OneShotEffect { + + public PlarggDeanOfChaosEffect() { + super(Outcome.PlayForFree); + this.staticText = "reveal cards from the top of your library until you reveal a " + + "nonlegendary, nonland card with mana value 3 or less. " + + "You may cast that card without paying its mana cost. Put all revealed " + + "cards not cast this way on the bottom of your library in a random order"; + } + + public PlarggDeanOfChaosEffect(PlarggDeanOfChaosEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + boolean cardWasCast = false; + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null && controller.getLibrary().hasCards()) { + CardsImpl toReveal = new CardsImpl(); + Card eligibleCard = null; + for (Card card : controller.getLibrary().getCards(game)) { + toReveal.add(card); + if (!card.isLand() && !card.isLegendary() && card.getManaValue() < 4) { + eligibleCard = card; + break; + } + } + controller.revealCards(source, toReveal, game); + if (eligibleCard != null + && controller.chooseUse(Outcome.PlayForFree, "Cast " + eligibleCard.getLogName() + " without paying its mana cost?", source, game)) { + game.getState().setValue("PlayFromNotOwnHandZone" + eligibleCard.getId(), Boolean.TRUE); + cardWasCast = controller.cast(controller.chooseAbilityForCast(eligibleCard, game, true), + game, true, new ApprovingObject(source, game)); + game.getState().setValue("PlayFromNotOwnHandZone" + eligibleCard.getId(), null); + if (cardWasCast) { + toReveal.remove(eligibleCard); + } + } + controller.putCardsOnBottomOfLibrary(toReveal, game, source, false); + } + return cardWasCast; + } + + @Override + public PlarggDeanOfChaosEffect copy() { + return new PlarggDeanOfChaosEffect(this); + } +} + +class AugustaDeanOfOrderEffect extends OneShotEffect { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(); + + static { + filter.add(UntappedPredicate.instance); + } + + public AugustaDeanOfOrderEffect() { + super(Outcome.Benefit); + staticText = "tap any number of creatures you control"; + } + + public AugustaDeanOfOrderEffect(AugustaDeanOfOrderEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + TargetControlledCreaturePermanent target = new TargetControlledCreaturePermanent(0, Integer.MAX_VALUE, filter, true); + Player controller = game.getPlayer(source.getControllerId()); + controller.chooseTarget(Outcome.Benefit, target, source, game); + target.getTargets().forEach(t -> { + Permanent permanent = game.getPermanent(t); + permanent.tap(source, game); + }); + return true; + } + + @Override + public AugustaDeanOfOrderEffect copy() { + return new AugustaDeanOfOrderEffect(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/p/PlasmCapture.java b/Mage.Sets/src/mage/cards/p/PlasmCapture.java index 04faec4080f7..1452fbaab454 100644 --- a/Mage.Sets/src/mage/cards/p/PlasmCapture.java +++ b/Mage.Sets/src/mage/cards/p/PlasmCapture.java @@ -44,7 +44,7 @@ class PlasmCaptureCounterEffect extends OneShotEffect { public PlasmCaptureCounterEffect() { super(Outcome.Benefit); - this.staticText = "Counter target spell. At the beginning of your next precombat main phase, add X mana in any combination of colors, where X is that spell's converted mana cost"; + this.staticText = "Counter target spell. At the beginning of your next precombat main phase, add X mana in any combination of colors, where X is that spell's mana value"; } public PlasmCaptureCounterEffect(final PlasmCaptureCounterEffect effect) { @@ -62,7 +62,7 @@ public boolean apply(Game game, Ability source) { if (spell != null) { game.getStack().counter(getTargetPointer().getFirst(game, source), source, game); // mana gets added also if counter is not successful - int mana = spell.getConvertedManaCost(); + int mana = spell.getManaValue(); AtTheBeginOfMainPhaseDelayedTriggeredAbility delayedAbility = new AtTheBeginOfMainPhaseDelayedTriggeredAbility(new AddManaInAnyCombinationEffect(mana), false, TargetController.YOU, PhaseSelection.NEXT_PRECOMBAT_MAIN); game.addDelayedTriggeredAbility(delayedAbility, source); diff --git a/Mage.Sets/src/mage/cards/p/PlaxcasterFrogling.java b/Mage.Sets/src/mage/cards/p/PlaxcasterFrogling.java index 7d11902ad1dc..a9c28bf51685 100644 --- a/Mage.Sets/src/mage/cards/p/PlaxcasterFrogling.java +++ b/Mage.Sets/src/mage/cards/p/PlaxcasterFrogling.java @@ -25,7 +25,7 @@ */ public final class PlaxcasterFrogling extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature with a +1/+1 counter on it"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with a +1/+1 counter on it"); static { filter.add(CounterType.P1P1.getPredicate()); } diff --git a/Mage.Sets/src/mage/cards/p/PleaForPower.java b/Mage.Sets/src/mage/cards/p/PleaForPower.java index 778699d1327f..a50950064c6d 100644 --- a/Mage.Sets/src/mage/cards/p/PleaForPower.java +++ b/Mage.Sets/src/mage/cards/p/PleaForPower.java @@ -1,25 +1,25 @@ - package mage.cards.p; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.turn.AddExtraTurnControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.choices.TwoChoiceVote; import mage.constants.CardType; import mage.constants.Outcome; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * - * @author emerald000 + * @author emerald000, TheElk801 */ public final class PleaForPower extends CardImpl { public PleaForPower(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}"); // Will of the council - Starting with you, each player votes for time or knowledge. If time gets more votes, take an extra turn after this one. If knowledge gets more votes or the vote is tied, draw three cards. this.getSpellAbility().addEffect(new PleaForPowerEffect()); @@ -39,10 +39,12 @@ class PleaForPowerEffect extends OneShotEffect { PleaForPowerEffect() { super(Outcome.Benefit); - this.staticText = "Will of the council — Starting with you, each player votes for time or knowledge. If time gets more votes, take an extra turn after this one. If knowledge gets more votes or the vote is tied, draw three cards"; + this.staticText = "Will of the council — Starting with you, " + + "each player votes for time or knowledge. If time gets more votes, take an extra turn after this one. " + + "If knowledge gets more votes or the vote is tied, draw three cards"; } - PleaForPowerEffect(final PleaForPowerEffect effect) { + private PleaForPowerEffect(final PleaForPowerEffect effect) { super(effect); } @@ -54,28 +56,21 @@ public PleaForPowerEffect copy() { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - int timeCount = 0; - int knowledgeCount = 0; - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - if (player.chooseUse(Outcome.ExtraTurn, "Choose time?", source, game)) { - timeCount++; - game.informPlayers(player.getLogName() + " has chosen: time"); - } else { - knowledgeCount++; - game.informPlayers(player.getLogName() + " has chosen: knowledge"); - } - } - } - if (timeCount > knowledgeCount) { - new AddExtraTurnControllerEffect().apply(game, source); - } else { - controller.drawCards(3, source, game); - } - return true; + if (controller == null) { + return false; + } + + // Outcome.Detriment - AI will draw cards all the time (Knowledge choice) + // TODO: add AI hint logic in the choice method, see Tyrant's Choice as example + TwoChoiceVote vote = new TwoChoiceVote("Time (extra turn)", "Knowledge (draw 3 cards)", Outcome.Detriment); + vote.doVotes(source, game); + + int timeCount = vote.getVoteCount(true); + int knowledgeCount = vote.getVoteCount(false); + if (timeCount > knowledgeCount) { + return new AddExtraTurnControllerEffect().apply(game, source); + } else { + return controller.drawCards(3, source, game) > 0; } - return false; } } diff --git a/Mage.Sets/src/mage/cards/p/PlumbTheForbidden.java b/Mage.Sets/src/mage/cards/p/PlumbTheForbidden.java new file mode 100644 index 000000000000..378d01c85f92 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PlumbTheForbidden.java @@ -0,0 +1,81 @@ +package mage.cards.p; + +import mage.abilities.Ability; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.common.CopySourceSpellEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.LoseLifeSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PlumbTheForbidden extends CardImpl { + + public PlumbTheForbidden(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{B}"); + + // As an additional cost to cast this spell, you may sacrifice one or more creatures. When you do, copy this spell for each creature sacrificed this way. + this.getSpellAbility().addCost(new PlumbTheForbiddenCost()); + + // You draw a card and you lose 1 life. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).setText("you draw a card")); + this.getSpellAbility().addEffect(new LoseLifeSourceControllerEffect(1).concatBy("and")); + } + + private PlumbTheForbidden(final PlumbTheForbidden card) { + super(card); + } + + @Override + public PlumbTheForbidden copy() { + return new PlumbTheForbidden(this); + } +} + +class PlumbTheForbiddenCost extends SacrificeTargetCost { + + PlumbTheForbiddenCost() { + super(new TargetControlledPermanent(0, Integer.MAX_VALUE, StaticFilters.FILTER_CONTROLLED_CREATURES, true)); + this.text = "you may sacrifice one or more creatures. When you do, " + + "copy this spell for each creature sacrificed this way"; + } + + private PlumbTheForbiddenCost(final PlumbTheForbiddenCost cost) { + super(cost); + } + + @Override + public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { + if (!super.pay(ability, game, source, controllerId, noMana, costToPay)) { + return false; + } + int sacrificed = getPermanents().size(); + if (sacrificed > 0) { + game.fireReflexiveTriggeredAbility(new ReflexiveTriggeredAbility( + new CopySourceSpellEffect(sacrificed), false, "when you do, " + + "copy this spell for each creature sacrificed this way" + ), source); + } + return true; + } + + @Override + public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { + return true; + } + + @Override + public PlumbTheForbiddenCost copy() { + return new PlumbTheForbiddenCost(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PoetsQuill.java b/Mage.Sets/src/mage/cards/p/PoetsQuill.java new file mode 100644 index 000000000000..a77aa3f4f75b --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PoetsQuill.java @@ -0,0 +1,53 @@ +package mage.cards.p; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.LearnEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PoetsQuill extends CardImpl { + + public PoetsQuill(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{B}"); + + this.subtype.add(SubType.EQUIPMENT); + + // When Poet's Quill enters the battlefield, learn. + this.addAbility(new EntersBattlefieldTriggeredAbility(new LearnEffect())); + + // Equipped creature gets +1/+1 and has lifelink. + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(1, 1)); + ability.addEffect(new GainAbilityAttachedEffect( + LifelinkAbility.getInstance(), AttachmentType.EQUIPMENT + ).setText("and has lifelink")); + this.addAbility(ability); + + // Equip {1}{B} + this.addAbility(new EquipAbility(Outcome.AddAbility, new ManaCostsImpl<>("{1}{B}"))); + } + + private PoetsQuill(final PoetsQuill card) { + super(card); + } + + @Override + public PoetsQuill copy() { + return new PoetsQuill(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PoisonbellyOgre.java b/Mage.Sets/src/mage/cards/p/PoisonbellyOgre.java index 1ec2c72fb35e..29c1ecaf7ae8 100644 --- a/Mage.Sets/src/mage/cards/p/PoisonbellyOgre.java +++ b/Mage.Sets/src/mage/cards/p/PoisonbellyOgre.java @@ -12,7 +12,7 @@ import mage.constants.SetTargetPointer; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/p/PollenRemedy.java b/Mage.Sets/src/mage/cards/p/PollenRemedy.java index 4aedd2274c3b..0cc10fb6ec4b 100644 --- a/Mage.Sets/src/mage/cards/p/PollenRemedy.java +++ b/Mage.Sets/src/mage/cards/p/PollenRemedy.java @@ -35,7 +35,7 @@ public PollenRemedy(UUID ownerId, CardSetInfo setInfo) { // If Pollen Remedy was kicked, prevent the next 6 damage this way instead. Effect effect = new ConditionalReplacementEffect(new PreventDamageToTargetMultiAmountEffect(Duration.EndOfTurn, 6), KickedCondition.instance, new PreventDamageToTargetMultiAmountEffect(Duration.EndOfTurn, 3)); - effect.setText("Prevent the next 3 damage that would be dealt this turn to any number of targets, divided as you choose. if this spell was kicked, prevent the next 6 damage this way instead."); + effect.setText("Prevent the next 3 damage that would be dealt this turn to any number of targets, divided as you choose. If this spell was kicked, prevent the next 6 damage this way instead."); this.getSpellAbility().addEffect(effect); this.getSpellAbility().setTargetAdjuster(PollenRemedyAdjuster.instance); } diff --git a/Mage.Sets/src/mage/cards/p/PollutedDelta.java b/Mage.Sets/src/mage/cards/p/PollutedDelta.java index 25805ace50a4..63e3549f900b 100644 --- a/Mage.Sets/src/mage/cards/p/PollutedDelta.java +++ b/Mage.Sets/src/mage/cards/p/PollutedDelta.java @@ -21,7 +21,7 @@ public PollutedDelta(UUID ownerId, CardSetInfo setInfo) { this.frameColor = new ObjectColor("UB"); // {tap}, Pay 1 life, Sacrifice Polluted Delta: Search your library for an Island or Swamp card and put it onto the battlefield. Then shuffle your library. - this.addAbility(new FetchLandActivatedAbility(EnumSet.of(SubType.ISLAND, SubType.SWAMP))); + this.addAbility(new FetchLandActivatedAbility(SubType.ISLAND, SubType.SWAMP)); } private PollutedDelta(final PollutedDelta card) { diff --git a/Mage.Sets/src/mage/cards/p/PolukranosUnchained.java b/Mage.Sets/src/mage/cards/p/PolukranosUnchained.java index 389bcebbd58e..083f3092f385 100644 --- a/Mage.Sets/src/mage/cards/p/PolukranosUnchained.java +++ b/Mage.Sets/src/mage/cards/p/PolukranosUnchained.java @@ -18,7 +18,7 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; diff --git a/Mage.Sets/src/mage/cards/p/Ponder.java b/Mage.Sets/src/mage/cards/p/Ponder.java index 72561caccc60..67f7965df863 100644 --- a/Mage.Sets/src/mage/cards/p/Ponder.java +++ b/Mage.Sets/src/mage/cards/p/Ponder.java @@ -20,7 +20,7 @@ public Ponder(UUID ownerId, CardSetInfo setInfo) { // Look at the top three cards of your library, then put them back in any order. You may shuffle your library. this.getSpellAbility().addEffect(new LookLibraryControllerEffect(3, true, true)); // Draw a card. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); } private Ponder(final Ponder card) { diff --git a/Mage.Sets/src/mage/cards/p/Pongify.java b/Mage.Sets/src/mage/cards/p/Pongify.java index aa8e0fc5cb6e..292adafd708e 100644 --- a/Mage.Sets/src/mage/cards/p/Pongify.java +++ b/Mage.Sets/src/mage/cards/p/Pongify.java @@ -43,7 +43,7 @@ class PongifyEffect extends OneShotEffect { public PongifyEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "That creature's controller creates a 3/3 green Ape creature token"; + this.staticText = "Its controller creates a 3/3 green Ape creature token"; } public PongifyEffect(final PongifyEffect effect) { diff --git a/Mage.Sets/src/mage/cards/p/PontiffOfBlight.java b/Mage.Sets/src/mage/cards/p/PontiffOfBlight.java index 708a6687f292..81e7a43f1a24 100644 --- a/Mage.Sets/src/mage/cards/p/PontiffOfBlight.java +++ b/Mage.Sets/src/mage/cards/p/PontiffOfBlight.java @@ -11,7 +11,7 @@ import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/p/PopQuiz.java b/Mage.Sets/src/mage/cards/p/PopQuiz.java new file mode 100644 index 000000000000..719da61d1756 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PopQuiz.java @@ -0,0 +1,34 @@ +package mage.cards.p; + +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.LearnEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PopQuiz extends CardImpl { + + public PopQuiz(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}"); + + // Draw a card. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + + // Learn. + this.getSpellAbility().addEffect(new LearnEffect().concatBy("
")); + } + + private PopQuiz(final PopQuiz card) { + super(card); + } + + @Override + public PopQuiz copy() { + return new PopQuiz(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/Portent.java b/Mage.Sets/src/mage/cards/p/Portent.java index bc1115a57974..ae35cccdceef 100644 --- a/Mage.Sets/src/mage/cards/p/Portent.java +++ b/Mage.Sets/src/mage/cards/p/Portent.java @@ -47,7 +47,7 @@ class PortentEffect extends OneShotEffect { public PortentEffect() { super(Outcome.DrawCard); - this.staticText = "look at the top three cards of target player's library, then put them back in any order. You may have that player shuffle their library"; + this.staticText = "look at the top three cards of target player's library, then put them back in any order. You may have that player shuffle"; } public PortentEffect(final PortentEffect effect) { @@ -69,7 +69,7 @@ public boolean apply(Game game, Ability source) { Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 3)); controller.lookAtCards(source, null, cards, game); controller.putCardsOnTopOfLibrary(cards, game, source, true); - if (controller.chooseUse(Outcome.Neutral, "You may have that player shuffle their library", source, game)) { + if (controller.chooseUse(Outcome.Neutral, "You may have that player shuffle", source, game)) { player.shuffleLibrary(source, game); } return true; diff --git a/Mage.Sets/src/mage/cards/p/PossibilityStorm.java b/Mage.Sets/src/mage/cards/p/PossibilityStorm.java index 4d50d90d98ce..6593c79dcf10 100644 --- a/Mage.Sets/src/mage/cards/p/PossibilityStorm.java +++ b/Mage.Sets/src/mage/cards/p/PossibilityStorm.java @@ -127,7 +127,7 @@ public boolean apply(Game game, Ability source) { if (card != null && sharesType(card, spell.getCardType()) && !card.isLand() - && card.getSpellAbility().canChooseTarget(game)) { + && card.getSpellAbility().canChooseTarget(game, spellController.getId())) { if (spellController.chooseUse(Outcome.PlayForFree, "Cast " + card.getLogName() + " without paying cost?", source, game)) { spellController.cast(card.getSpellAbility(), game, true, new ApprovingObject(source, game)); } diff --git a/Mage.Sets/src/mage/cards/p/PostmortemLunge.java b/Mage.Sets/src/mage/cards/p/PostmortemLunge.java index 9ef675a188fc..4f706ff79c4d 100644 --- a/Mage.Sets/src/mage/cards/p/PostmortemLunge.java +++ b/Mage.Sets/src/mage/cards/p/PostmortemLunge.java @@ -14,7 +14,7 @@ import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -55,8 +55,8 @@ enum PostmortemLungeAdjuster implements TargetAdjuster { public void adjustTargets(Ability ability, Game game) { ability.getTargets().clear(); int xValue = ability.getManaCostsToPay().getX(); - FilterCard filter = new FilterCreatureCard("creature card with converted mana cost " + xValue + " or less from your graveyard"); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, xValue + 1)); + FilterCard filter = new FilterCreatureCard("creature card with mana value " + xValue + " or less from your graveyard"); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); ability.getTargets().add(new TargetCardInYourGraveyard(filter)); } } @@ -65,7 +65,7 @@ class PostmortemLungeEffect extends OneShotEffect { public PostmortemLungeEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "Return target creature card with converted mana cost X from your graveyard to the battlefield. It gains haste. Exile it at the beginning of the next end step"; + this.staticText = "Return target creature card with mana value X from your graveyard to the battlefield. It gains haste. Exile it at the beginning of the next end step"; } public PostmortemLungeEffect(final PostmortemLungeEffect effect) { diff --git a/Mage.Sets/src/mage/cards/p/PowderKeg.java b/Mage.Sets/src/mage/cards/p/PowderKeg.java index 7e1896b8dc27..afc9a2511951 100644 --- a/Mage.Sets/src/mage/cards/p/PowderKeg.java +++ b/Mage.Sets/src/mage/cards/p/PowderKeg.java @@ -19,7 +19,7 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; @@ -55,7 +55,7 @@ class PowderKegEffect extends OneShotEffect { public PowderKegEffect() { super(Outcome.DestroyPermanent); - staticText = "Destroy each artifact and creature with converted mana cost equal to the number of fuse counters on {this}"; + staticText = "Destroy each artifact and creature with mana value equal to the number of fuse counters on {this}"; } public PowderKegEffect(final PowderKegEffect effect) { @@ -71,7 +71,7 @@ public boolean apply(Game game, Ability source) { int count = sourcePermanent.getCounters(game).getCount(CounterType.FUSE); FilterPermanent filter = new FilterPermanent(); filter.add(Predicates.or(CardType.ARTIFACT.getPredicate(), CardType.CREATURE.getPredicate())); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, count)); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, count)); for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { perm.destroy(source, game, false); } diff --git a/Mage.Sets/src/mage/cards/p/PracticalResearch.java b/Mage.Sets/src/mage/cards/p/PracticalResearch.java new file mode 100644 index 000000000000..7400485360e7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PracticalResearch.java @@ -0,0 +1,39 @@ +package mage.cards.p; + +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.discard.DiscardControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PracticalResearch extends CardImpl { + + public PracticalResearch(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}{R}"); + + // Draw four cards. Then discard two cards unless you discard an instant or sorcery card. + DiscardCardCost cost = new DiscardCardCost(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY); + cost.setText("discard an instant or sorcery card instead of discarding two cards"); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(4)); + this.getSpellAbility().addEffect(new DoIfCostPaid( + null, new DiscardControllerEffect(2), cost + ).setText("Then discard two cards unless you discard an instant or sorcery card")); + } + + private PracticalResearch(final PracticalResearch card) { + super(card); + } + + @Override + public PracticalResearch copy() { + return new PracticalResearch(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PraetorsCounsel.java b/Mage.Sets/src/mage/cards/p/PraetorsCounsel.java index a916b4443271..25b94bfc357f 100644 --- a/Mage.Sets/src/mage/cards/p/PraetorsCounsel.java +++ b/Mage.Sets/src/mage/cards/p/PraetorsCounsel.java @@ -27,7 +27,7 @@ public PraetorsCounsel(UUID ownerId, CardSetInfo setInfo) { // Return all cards from your graveyard to your hand. Exile Praetor's Counsel. You have no maximum hand size for the rest of the game. this.getSpellAbility().addEffect(new PraetorsCounselEffect()); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); this.getSpellAbility().addEffect(new MaximumHandSizeControllerEffect(Integer.MAX_VALUE, Duration.EndOfGame, HandSizeModification.SET)); } diff --git a/Mage.Sets/src/mage/cards/p/PraetorsGrasp.java b/Mage.Sets/src/mage/cards/p/PraetorsGrasp.java index 9ed9698d9fe0..35b78ff81492 100644 --- a/Mage.Sets/src/mage/cards/p/PraetorsGrasp.java +++ b/Mage.Sets/src/mage/cards/p/PraetorsGrasp.java @@ -46,7 +46,7 @@ class PraetorsGraspEffect extends OneShotEffect { public PraetorsGraspEffect() { super(Outcome.PlayForFree); staticText = "Search target opponent's library for a card and exile it " - + "face down. Then that player shuffles their library. You may " + + "face down. Then that player shuffles. You may " + "look at and play that card for as long as it remains exiled"; } diff --git a/Mage.Sets/src/mage/cards/p/Precognition.java b/Mage.Sets/src/mage/cards/p/Precognition.java index c773497f3503..173bb3013ec9 100644 --- a/Mage.Sets/src/mage/cards/p/Precognition.java +++ b/Mage.Sets/src/mage/cards/p/Precognition.java @@ -70,7 +70,7 @@ public boolean apply(Game game, Ability source) { if (card != null) { Cards cards = new CardsImpl(card); controller.lookAtCards("Precognition", cards, game); - if (controller.chooseUse(outcome, "Do you wish to put card on the bottom of player's library?", source, game)) { + if (controller.chooseUse(outcome, "Put that card on the bottom of its owner's library?", source, game)) { controller.moveCardToLibraryWithInfo(card, source, game, Zone.LIBRARY, false, false); } else { game.informPlayers(controller.getLogName() + " puts the card back on top of the library."); diff --git a/Mage.Sets/src/mage/cards/p/PrecognitionField.java b/Mage.Sets/src/mage/cards/p/PrecognitionField.java index 9d5b8e1a6e36..3f9ac0ef0dbf 100644 --- a/Mage.Sets/src/mage/cards/p/PrecognitionField.java +++ b/Mage.Sets/src/mage/cards/p/PrecognitionField.java @@ -40,8 +40,8 @@ public PrecognitionField(UUID ownerId, CardSetInfo setInfo) { // You may look at the top card of your library. this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect())); - // You may cast the top card of your library if it's an instant or sorcery card. - this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter))); + // You may cast instant and sorcery spells from the top of your library. + this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false))); // {3}: Exile the top card of your library. this.addAbility(new SimpleActivatedAbility(new PrecognitionFieldExileEffect(), new GenericManaCost(3))); diff --git a/Mage.Sets/src/mage/cards/p/PrecursorGolem.java b/Mage.Sets/src/mage/cards/p/PrecursorGolem.java index 1b74a18058fa..6cc52fe281d4 100644 --- a/Mage.Sets/src/mage/cards/p/PrecursorGolem.java +++ b/Mage.Sets/src/mage/cards/p/PrecursorGolem.java @@ -1,8 +1,7 @@ - package mage.cards.p; -import java.util.UUID; import mage.MageInt; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -13,25 +12,30 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.FilterInPlay; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.FilterPermanent; +import mage.filter.predicate.mageobject.MageObjectReferencePredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.game.permanent.token.GolemToken; import mage.game.stack.Spell; +import mage.game.stack.StackObject; import mage.players.Player; import mage.target.Target; import mage.util.TargetAddress; +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + /** * @author duncant */ public final class PrecursorGolem extends CardImpl { public PrecursorGolem(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{5}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}"); this.subtype.add(SubType.GOLEM); this.power = new MageInt(3); @@ -60,7 +64,7 @@ class PrecursorGolemCopyTriggeredAbility extends TriggeredAbilityImpl { super(Zone.BATTLEFIELD, new PrecursorGolemCopySpellEffect(), false); } - PrecursorGolemCopyTriggeredAbility(final PrecursorGolemCopyTriggeredAbility ability) { + private PrecursorGolemCopyTriggeredAbility(final PrecursorGolemCopyTriggeredAbility ability) { super(ability); } @@ -77,76 +81,82 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { Spell spell = game.getStack().getSpell(event.getTargetId()); - return checkSpell(spell, game); - } - - private boolean checkSpell(Spell spell, Game game) { - if (spell != null - && (spell.isInstant() || spell.isSorcery())) { - UUID targetGolem = null; - for (TargetAddress addr : TargetAddress.walk(spell)) { - Target targetInstance = addr.getTarget(spell); - for (UUID target : targetInstance.getTargets()) { - Permanent permanent = game.getPermanent(target); - if (permanent == null || !permanent.hasSubtype(SubType.GOLEM, game)) { + if (spell == null || !spell.isInstantOrSorcery()) { + return false; + } + UUID targetGolem = null; + for (TargetAddress addr : TargetAddress.walk(spell)) { + Target targetInstance = addr.getTarget(spell); + for (UUID target : targetInstance.getTargets()) { + Permanent permanent = game.getPermanent(target); + if (permanent == null || !permanent.hasSubtype(SubType.GOLEM, game)) { + return false; + } + if (targetGolem == null) { + targetGolem = target; + } else // If a spell has multiple targets, but it's targeting the same Golem with all of them, Precursor Golem's last ability will trigger + { + if (!targetGolem.equals(target)) { return false; } - if (targetGolem == null) { - targetGolem = target; - } else // If a spell has multiple targets, but it's targeting the same Golem with all of them, Precursor Golem's last ability will trigger - { - if (!targetGolem.equals(target)) { - return false; - } - } } } - if (targetGolem != null) { - getEffects().get(0).setValue("triggeringSpell", spell); - getEffects().get(0).setValue("targetedGolem", targetGolem); - return true; - } } - return false; + if (targetGolem == null) { + return false; + } + getEffects().setValue("triggeringSpell", spell); + getEffects().setValue("targetedGolem", targetGolem); + return true; } @Override public String getRule() { - return "Whenever a player casts an instant or sorcery spell that targets only a single Golem, that player copies that spell for each other Golem that spell could target. Each copy targets a different one of those Golems."; + return "Whenever a player casts an instant or sorcery spell that targets only a single Golem, " + + "that player copies that spell for each other Golem that spell could target. " + + "Each copy targets a different one of those Golems."; } } -class PrecursorGolemCopySpellEffect extends CopySpellForEachItCouldTargetEffect { +class PrecursorGolemCopySpellEffect extends CopySpellForEachItCouldTargetEffect { - public PrecursorGolemCopySpellEffect() { - this(new FilterCreaturePermanent(SubType.GOLEM, "Golem")); - } + private static final FilterPermanent filter = new FilterPermanent(SubType.GOLEM, ""); - public PrecursorGolemCopySpellEffect(PrecursorGolemCopySpellEffect effect) { - super(effect); + PrecursorGolemCopySpellEffect() { + super(); } - private PrecursorGolemCopySpellEffect(FilterInPlay filter) { - super(filter); + private PrecursorGolemCopySpellEffect(PrecursorGolemCopySpellEffect effect) { + super(effect); } @Override protected Player getPlayer(Game game, Ability source) { - return game.getPlayer(source.getControllerId()); - } - - @Override - protected Spell getSpell(Game game, Ability source) { - return (Spell) getValue("triggeringSpell"); + Spell spell = getStackObject(game, source); + if (spell == null) { + return null; + } + return game.getPlayer(spell.getControllerId()); } @Override - protected boolean changeTarget(Target target, Game game, Ability source) { - return true; + protected List getPossibleTargets(StackObject stackObject, Player player, Ability source, Game game) { + Permanent permanent = (Permanent) getValue("targetedGolem"); + return game.getBattlefield() + .getActivePermanents( + filter, player.getId(), source.getSourceId(), game + ).stream() + .filter(Objects::nonNull) + .filter(p -> !p.equals(permanent)) + .filter(p -> stackObject.canTarget(game, p.getId())) + .map(p -> new MageObjectReference(p, game)) + .map(MageObjectReferencePredicate::new) + .collect(Collectors.toList()); } @Override - protected void modifyCopy(Spell copy, Game game, Ability source) { + protected Spell getStackObject(Game game, Ability source) { + return (Spell) getValue("triggeringSpell"); } @Override diff --git a/Mage.Sets/src/mage/cards/p/PrideOfTheClouds.java b/Mage.Sets/src/mage/cards/p/PrideOfTheClouds.java index 177aac5e52b9..dcb00281a688 100644 --- a/Mage.Sets/src/mage/cards/p/PrideOfTheClouds.java +++ b/Mage.Sets/src/mage/cards/p/PrideOfTheClouds.java @@ -19,7 +19,7 @@ import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.TokenImpl; /** * diff --git a/Mage.Sets/src/mage/cards/p/PrideSovereign.java b/Mage.Sets/src/mage/cards/p/PrideSovereign.java index ea68b500cee8..625d470ed469 100644 --- a/Mage.Sets/src/mage/cards/p/PrideSovereign.java +++ b/Mage.Sets/src/mage/cards/p/PrideSovereign.java @@ -20,7 +20,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.CatToken2; /** diff --git a/Mage.Sets/src/mage/cards/p/PriestOfForgottenGods.java b/Mage.Sets/src/mage/cards/p/PriestOfForgottenGods.java index c24a3c6dde49..639d3647fa08 100644 --- a/Mage.Sets/src/mage/cards/p/PriestOfForgottenGods.java +++ b/Mage.Sets/src/mage/cards/p/PriestOfForgottenGods.java @@ -10,7 +10,7 @@ import mage.constants.SubType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPlayer; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/p/PriestOfYawgmoth.java b/Mage.Sets/src/mage/cards/p/PriestOfYawgmoth.java index dbb490200293..8dbc04fd3470 100644 --- a/Mage.Sets/src/mage/cards/p/PriestOfYawgmoth.java +++ b/Mage.Sets/src/mage/cards/p/PriestOfYawgmoth.java @@ -33,7 +33,7 @@ public PriestOfYawgmoth(UUID ownerId, CardSetInfo setInfo) { // {T}, Sacrifice an artifact: Add an amount of {B} equal to the sacrificed artifact's converted mana cost. Ability ability = new DynamicManaAbility(Mana.BlackMana(1), new SacrificeCostConvertedMana("artifact"), new TapSourceCost(), - "add an amount of {B} equal to the sacrificed artifact's converted mana cost", + "add an amount of {B} equal to the sacrificed artifact's mana value", false, new HighestCMCOfPermanentValue(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT, true) ); diff --git a/Mage.Sets/src/mage/cards/p/PrimalDruid.java b/Mage.Sets/src/mage/cards/p/PrimalDruid.java index 3911928a002c..f2d7ebc90b28 100644 --- a/Mage.Sets/src/mage/cards/p/PrimalDruid.java +++ b/Mage.Sets/src/mage/cards/p/PrimalDruid.java @@ -29,7 +29,7 @@ public PrimalDruid(UUID ownerId, CardSetInfo setInfo) { // When Primal Druid dies, you may search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library. Effect effect = new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true); - effect.setText("you may search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library"); + effect.setText("you may search your library for a basic land card, put it onto the battlefield tapped, then shuffle"); this.addAbility(new DiesSourceTriggeredAbility(effect, true)); } diff --git a/Mage.Sets/src/mage/cards/p/PrimalForcemage.java b/Mage.Sets/src/mage/cards/p/PrimalForcemage.java index 379a2d122ebb..29b37d06f45b 100644 --- a/Mage.Sets/src/mage/cards/p/PrimalForcemage.java +++ b/Mage.Sets/src/mage/cards/p/PrimalForcemage.java @@ -14,7 +14,7 @@ import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/p/PrimalGrowth.java b/Mage.Sets/src/mage/cards/p/PrimalGrowth.java index 35eee8d2c9d0..dff7aee9df8b 100644 --- a/Mage.Sets/src/mage/cards/p/PrimalGrowth.java +++ b/Mage.Sets/src/mage/cards/p/PrimalGrowth.java @@ -33,7 +33,7 @@ public PrimalGrowth(UUID ownerId, CardSetInfo setInfo) { new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 2, StaticFilters.FILTER_CARD_BASIC_LAND), false, true), new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 1, StaticFilters.FILTER_CARD_BASIC_LAND), false, true), KickedCondition.instance, - "Search your library for a basic land card, put that card onto the battlefield, then shuffle your library. If this spell was kicked, instead search your library for up to two basic land cards, put them onto the battlefield, then shuffle your library")); + "Search your library for a basic land card, put that card onto the battlefield, then shuffle. If this spell was kicked, instead search your library for up to two basic land cards, put them onto the battlefield, then shuffle")); } private PrimalGrowth(final PrimalGrowth card) { diff --git a/Mage.Sets/src/mage/cards/p/PrimalWhisperer.java b/Mage.Sets/src/mage/cards/p/PrimalWhisperer.java index eaa8e7d73a70..6d41b7ed3f46 100644 --- a/Mage.Sets/src/mage/cards/p/PrimalWhisperer.java +++ b/Mage.Sets/src/mage/cards/p/PrimalWhisperer.java @@ -15,7 +15,7 @@ import mage.constants.Duration; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.other.FaceDownPredicate; +import mage.filter.predicate.card.FaceDownPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/p/PrimeSpeakerVannifar.java b/Mage.Sets/src/mage/cards/p/PrimeSpeakerVannifar.java index 887b3bbd484e..13985768e0e8 100644 --- a/Mage.Sets/src/mage/cards/p/PrimeSpeakerVannifar.java +++ b/Mage.Sets/src/mage/cards/p/PrimeSpeakerVannifar.java @@ -13,7 +13,7 @@ import mage.constants.*; import mage.filter.FilterCard; import mage.filter.StaticFilters; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -61,9 +61,9 @@ class PrimeSpeakerVannifarEffect extends OneShotEffect { PrimeSpeakerVannifarEffect() { super(Outcome.Benefit); - staticText = "Search your library for a creature card with converted mana cost equal to 1 " + - "plus the sacrificed creature's converted mana cost, put that card " + - "onto the battlefield, then shuffle your library"; + staticText = "Search your library for a creature card with mana value equal to 1 " + + "plus the sacrificed creature's mana value, put that card " + + "onto the battlefield, then shuffle"; } private PrimeSpeakerVannifarEffect(final PrimeSpeakerVannifarEffect effect) { @@ -86,9 +86,9 @@ public boolean apply(Game game, Ability source) { if (sacrificedPermanent == null || controller == null) { return false; } - int newConvertedCost = sacrificedPermanent.getConvertedManaCost() + 1; - FilterCard filter = new FilterCard("creature card with converted mana cost " + newConvertedCost); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, newConvertedCost)); + int newConvertedCost = sacrificedPermanent.getManaValue() + 1; + FilterCard filter = new FilterCard("creature card with mana value " + newConvertedCost); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, newConvertedCost)); filter.add(CardType.CREATURE.getPredicate()); TargetCardInLibrary target = new TargetCardInLibrary(filter); if (controller.searchLibrary(target, source, game)) { diff --git a/Mage.Sets/src/mage/cards/p/PrimevalProtector.java b/Mage.Sets/src/mage/cards/p/PrimevalProtector.java index 30dfe356236b..1249e1312456 100644 --- a/Mage.Sets/src/mage/cards/p/PrimevalProtector.java +++ b/Mage.Sets/src/mage/cards/p/PrimevalProtector.java @@ -21,7 +21,7 @@ import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; @@ -74,7 +74,7 @@ class PrimevalProtectorCostReductionEffect extends CostModificationEffectImpl { PrimevalProtectorCostReductionEffect() { super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "{this} costs {1} less to cast for each creature your opponents control"; + staticText = "this spell costs {1} less to cast for each creature your opponents control"; } PrimevalProtectorCostReductionEffect(PrimevalProtectorCostReductionEffect effect) { diff --git a/Mage.Sets/src/mage/cards/p/PrimordialHydra.java b/Mage.Sets/src/mage/cards/p/PrimordialHydra.java index 722b81ec22c7..2732b3c6397b 100644 --- a/Mage.Sets/src/mage/cards/p/PrimordialHydra.java +++ b/Mage.Sets/src/mage/cards/p/PrimordialHydra.java @@ -1,15 +1,13 @@ - package mage.cards.p; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DoubleCountersSourceEffect; import mage.abilities.effects.common.EntersBattlefieldWithXCountersEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.keyword.TrampleAbility; @@ -17,19 +15,17 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Outcome; import mage.constants.TargetController; -import mage.constants.Zone; import mage.counters.CounterType; -import mage.game.Game; -import mage.game.permanent.Permanent; + +import java.util.UUID; /** - * * @author Loki */ public final class PrimordialHydra extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.P1P1, 10, Integer.MAX_VALUE); private static final String staticText = "{this} has trample as long as it has ten or more +1/+1 counters on it"; public PrimordialHydra(UUID ownerId, CardSetInfo setInfo) { @@ -44,12 +40,14 @@ public PrimordialHydra(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new EntersBattlefieldAbility(new EntersBattlefieldWithXCountersEffect(CounterType.P1P1.createInstance()))); // At the beginning of your upkeep, double the number of +1/+1 counters on Primordial Hydra. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new PrimordialHydraDoubleEffect(), TargetController.YOU, false)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new DoubleCountersSourceEffect(CounterType.P1P1), TargetController.YOU, false + )); // Primordial Hydra has trample as long as it has ten or more +1/+1 counters on it. - ConditionalContinuousEffect effect = new ConditionalContinuousEffect(new GainAbilitySourceEffect(TrampleAbility.getInstance()), new SourceHasCounterCondition(CounterType.P1P1, 10, Integer.MAX_VALUE), staticText); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); - + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(TrampleAbility.getInstance()), condition, staticText + ))); } private PrimordialHydra(final PrimordialHydra card) { @@ -61,33 +59,3 @@ public PrimordialHydra copy() { return new PrimordialHydra(this); } } - -class PrimordialHydraDoubleEffect extends OneShotEffect { - - PrimordialHydraDoubleEffect() { - super(Outcome.BoostCreature); - staticText = "double the number of +1/+1 counters on {this}"; - } - - PrimordialHydraDoubleEffect(final PrimordialHydraDoubleEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (sourcePermanent != null) { - int amount = sourcePermanent.getCounters(game).getCount(CounterType.P1P1); - if (amount > 0) { - sourcePermanent.addCounters(CounterType.P1P1.createInstance(amount), source.getControllerId(), source, game); - } - return true; - } - return false; - } - - @Override - public PrimordialHydraDoubleEffect copy() { - return new PrimordialHydraDoubleEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/p/PrimordialMist.java b/Mage.Sets/src/mage/cards/p/PrimordialMist.java index 2b9149eed510..ca77430dc391 100644 --- a/Mage.Sets/src/mage/cards/p/PrimordialMist.java +++ b/Mage.Sets/src/mage/cards/p/PrimordialMist.java @@ -19,7 +19,7 @@ import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.other.FaceDownPredicate; +import mage.filter.predicate.card.FaceDownPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/p/PrinceOfThralls.java b/Mage.Sets/src/mage/cards/p/PrinceOfThralls.java index c39fc7433d4e..5106a5d8afa0 100644 --- a/Mage.Sets/src/mage/cards/p/PrinceOfThralls.java +++ b/Mage.Sets/src/mage/cards/p/PrinceOfThralls.java @@ -73,15 +73,13 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getToZone() == Zone.GRAVEYARD) { - if (zEvent.getFromZone() == Zone.BATTLEFIELD) { - Permanent permanent = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); - if (game.getOpponents(this.getControllerId()).contains(permanent.getControllerId())) { - for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId(), game.getState().getZoneChangeCounter(event.getTargetId()))); - } - return true; + if (zEvent.isDiesEvent()) { + Permanent permanent = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); + if (game.getOpponents(this.getControllerId()).contains(permanent.getControllerId())) { + for (Effect effect : getEffects()) { + effect.setTargetPointer(new FixedTarget(event.getTargetId(), game.getState().getZoneChangeCounter(event.getTargetId()))); } + return true; } } return false; diff --git a/Mage.Sets/src/mage/cards/p/PrincessLeia.java b/Mage.Sets/src/mage/cards/p/PrincessLeia.java index 8b13ecf74e1c..4a40f2aca2c2 100644 --- a/Mage.Sets/src/mage/cards/p/PrincessLeia.java +++ b/Mage.Sets/src/mage/cards/p/PrincessLeia.java @@ -14,7 +14,7 @@ import mage.constants.*; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.RebelToken; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/p/PrismariApprentice.java b/Mage.Sets/src/mage/cards/p/PrismariApprentice.java new file mode 100644 index 000000000000..5083ea1403b2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PrismariApprentice.java @@ -0,0 +1,61 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.stack.Spell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PrismariApprentice extends CardImpl { + + public PrismariApprentice(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, Prismari Apprentice can't be blocked this turn. If that spell has mana value 5 or greater, put a +1/+1 counter on Prismari Apprentice. + Ability ability = new MagecraftAbility(new CantBeBlockedSourceEffect(Duration.EndOfTurn)); + ability.addEffect(new ConditionalOneShotEffect(new AddCountersSourceEffect( + CounterType.P1P1.createInstance()), PrismariApprenticeCondition.instance, + "If that spell has mana value 5 or greater, put a +1/+1 counter on {this}" + )); + this.addAbility(ability); + } + + private PrismariApprentice(final PrismariApprentice card) { + super(card); + } + + @Override + public PrismariApprentice copy() { + return new PrismariApprentice(this); + } +} + +enum PrismariApprenticeCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + Spell spell = (Spell) source.getEffects().get(0).getValue(MagecraftAbility.SPELL_KEY); + return spell != null && spell.getManaValue() >= 5; + } +} diff --git a/Mage.Sets/src/mage/cards/p/PrismariCampus.java b/Mage.Sets/src/mage/cards/p/PrismariCampus.java new file mode 100644 index 000000000000..a41977a39d9b --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PrismariCampus.java @@ -0,0 +1,46 @@ +package mage.cards.p; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.RedManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PrismariCampus extends CardImpl { + + public PrismariCampus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // Prismari Campus enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {U} or {R}. + this.addAbility(new BlueManaAbility()); + this.addAbility(new RedManaAbility()); + + // {4}, {T}: Scry 1. + Ability ability = new SimpleActivatedAbility(new ScryEffect(1), new GenericManaCost(4)); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private PrismariCampus(final PrismariCampus card) { + super(card); + } + + @Override + public PrismariCampus copy() { + return new PrismariCampus(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PrismariCommand.java b/Mage.Sets/src/mage/cards/p/PrismariCommand.java new file mode 100644 index 000000000000..8ca23c6ed357 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PrismariCommand.java @@ -0,0 +1,58 @@ +package mage.cards.p; + +import mage.abilities.Mode; +import mage.abilities.effects.common.CreateTokenTargetEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.DrawDiscardTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.permanent.token.TreasureToken; +import mage.target.TargetPlayer; +import mage.target.common.TargetAnyTarget; +import mage.target.common.TargetArtifactPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PrismariCommand extends CardImpl { + + public PrismariCommand(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}{R}"); + + // Choose two — + this.getSpellAbility().getModes().setMinModes(2); + this.getSpellAbility().getModes().setMaxModes(2); + + // • Prismari Command deals 2 damage to any target. + this.getSpellAbility().addEffect(new DamageTargetEffect(2)); + this.getSpellAbility().addTarget(new TargetAnyTarget()); + + // • Target player draws two cards, then discards two cards. + Mode mode = new Mode(new DrawDiscardTargetEffect(2, 2)); + mode.addTarget(new TargetPlayer()); + this.getSpellAbility().addMode(mode); + + // • Target player creates a Treasure token. + mode = new Mode(new CreateTokenTargetEffect(new TreasureToken())); + mode.addTarget(new TargetPlayer()); + this.getSpellAbility().addMode(mode); + + // • Destroy target artifact. + mode = new Mode(new DestroyTargetEffect()); + mode.addTarget(new TargetArtifactPermanent()); + this.getSpellAbility().addMode(mode); + } + + private PrismariCommand(final PrismariCommand card) { + super(card); + } + + @Override + public PrismariCommand copy() { + return new PrismariCommand(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PrismariPledgemage.java b/Mage.Sets/src/mage/cards/p/PrismariPledgemage.java new file mode 100644 index 000000000000..8d05e57816a3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PrismariPledgemage.java @@ -0,0 +1,43 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.common.MagecraftAbility; +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 PrismariPledgemage extends CardImpl { + + public PrismariPledgemage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U/R}{U/R}"); + + this.subtype.add(SubType.ORC); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Defender + this.addAbility(DefenderAbility.getInstance()); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, Prismari Pledgemage can attack this turn as though it didn't have defender. + this.addAbility(new MagecraftAbility(new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.EndOfTurn))); + } + + private PrismariPledgemage(final PrismariPledgemage card) { + super(card); + } + + @Override + public PrismariPledgemage copy() { + return new PrismariPledgemage(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PrismaticStrands.java b/Mage.Sets/src/mage/cards/p/PrismaticStrands.java index 41597c57dca2..fa7530e69c3b 100644 --- a/Mage.Sets/src/mage/cards/p/PrismaticStrands.java +++ b/Mage.Sets/src/mage/cards/p/PrismaticStrands.java @@ -116,8 +116,7 @@ public boolean apply(Game game, Ability source) { public boolean applies(GameEvent event, Ability source, Game game) { if (super.applies(event, source, game)) { if (event.getType() == GameEvent.EventType.DAMAGE_PLAYER - || event.getType() == GameEvent.EventType.DAMAGE_CREATURE - || event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER) { + || event.getType() == GameEvent.EventType.DAMAGE_PERMANENT) { MageObject sourceObject = game.getObject(event.getSourceId()); if (sourceObject != null && sourceObject.getColor(game).shares(this.color)) { return true; diff --git a/Mage.Sets/src/mage/cards/p/ProclamationOfRebirth.java b/Mage.Sets/src/mage/cards/p/ProclamationOfRebirth.java index 993065665445..e45cba5a1a19 100644 --- a/Mage.Sets/src/mage/cards/p/ProclamationOfRebirth.java +++ b/Mage.Sets/src/mage/cards/p/ProclamationOfRebirth.java @@ -1,7 +1,5 @@ - package mage.cards.p; -import java.util.UUID; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.abilities.keyword.ForecastAbility; @@ -10,27 +8,31 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInYourGraveyard; +import java.util.UUID; + /** - * * @author Plopman */ public final class ProclamationOfRebirth extends CardImpl { - private static final FilterCreatureCard filter1 = new FilterCreatureCard("creature card with converted mana cost {1} or less from your graveyard"); - private static final FilterCreatureCard filter3 = new FilterCreatureCard("creature cards with converted mana cost {1} or less from your graveyard"); + private static final FilterCreatureCard filter1 = new FilterCreatureCard("creature card with mana value 1 or less from your graveyard"); + private static final FilterCreatureCard filter3 = new FilterCreatureCard("creature cards with mana value 1 or less from your graveyard"); + static { - filter1.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 2)); - filter3.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 2)); + filter1.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 2)); + filter3.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 2)); } + public ProclamationOfRebirth(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{W}"); // Return up to three target creature cards with converted mana cost 1 or less from your graveyard to the battlefield. - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0,3,filter3)); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 3, filter3)); this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); + // Forecast - {5}{W}, Reveal Proclamation of Rebirth from your hand: Return target creature card with converted mana cost 1 or less from your graveyard to the battlefield. ForecastAbility ability = new ForecastAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(), new ManaCostsImpl("{5}{W}")); ability.addTarget(new TargetCardInYourGraveyard(filter1)); diff --git a/Mage.Sets/src/mage/cards/p/ProfaneCommand.java b/Mage.Sets/src/mage/cards/p/ProfaneCommand.java index 05327cfc1f31..04d7d0ce49b9 100644 --- a/Mage.Sets/src/mage/cards/p/ProfaneCommand.java +++ b/Mage.Sets/src/mage/cards/p/ProfaneCommand.java @@ -19,7 +19,7 @@ import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.target.TargetPlayer; import mage.target.common.TargetCardInYourGraveyard; @@ -47,8 +47,8 @@ public ProfaneCommand(UUID ownerId, CardSetInfo setInfo) { // * Return target creature card with converted mana cost X or less from your graveyard to the battlefield. Mode mode = new Mode(); - mode.addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect(false, false)); - mode.addTarget(new TargetCardInYourGraveyard(new FilterCreatureCard("creature card with converted mana cost X or less from your graveyard"))); + mode.addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); + mode.addTarget(new TargetCardInYourGraveyard(new FilterCreatureCard("creature card with mana value X or less from your graveyard"))); this.getSpellAbility().addMode(mode); // * Target creature gets -X/-X until end of turn. @@ -90,8 +90,8 @@ public void adjustTargets(Ability ability, Game game) { for (Effect effect : mode.getEffects()) { if (effect instanceof ReturnFromGraveyardToBattlefieldTargetEffect) { mode.getTargets().clear(); - FilterCard filter = new FilterCreatureCard("creature card with converted mana cost " + xValue + " or less from your graveyard"); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, xValue + 1)); + FilterCard filter = new FilterCreatureCard("creature card with mana value " + xValue + " or less from your graveyard"); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); mode.addTarget(new TargetCardInYourGraveyard(filter)); } if (effect instanceof GainAbilityTargetEffect) { diff --git a/Mage.Sets/src/mage/cards/p/ProfaneTransfusion.java b/Mage.Sets/src/mage/cards/p/ProfaneTransfusion.java index 30261eef5fcb..74134d9e0ef6 100644 --- a/Mage.Sets/src/mage/cards/p/ProfaneTransfusion.java +++ b/Mage.Sets/src/mage/cards/p/ProfaneTransfusion.java @@ -2,13 +2,13 @@ import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExchangeLifeTwoTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.game.Game; import mage.game.permanent.token.PhyrexianRebirthHorrorToken; -import mage.game.permanent.token.Token; import mage.players.Player; import mage.target.TargetPlayer; @@ -23,6 +23,7 @@ public ProfaneTransfusion(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{6}{B}{B}{B}"); // Two target players exchange life totals. You create an X/X colorless Horror artifact creature token, where X is the difference between those players' life totals. + this.getSpellAbility().addEffect(new ExchangeLifeTwoTargetEffect()); this.getSpellAbility().addEffect(new ProfaneTransfusionEffect()); this.getSpellAbility().addTarget(new TargetPlayer(2)); } @@ -41,8 +42,7 @@ class ProfaneTransfusionEffect extends OneShotEffect { ProfaneTransfusionEffect() { super(Outcome.Benefit); - staticText = "two target players exchange life totals. " + - "You create an X/X colorless Horror artifact creature token, " + + staticText = "You create an X/X colorless Horror artifact creature token, " + "where X is the difference between those players' life totals"; } @@ -65,30 +65,8 @@ public boolean apply(Game game, Ability source) { if (player1 == null || player2 == null) { return false; } - int lifePlayer1 = player1.getLife(); - int lifePlayer2 = player2.getLife(); - int lifeDifference = Math.abs(lifePlayer1 - lifePlayer2); - - Token token = new PhyrexianRebirthHorrorToken(); - token.setPower(lifeDifference); - token.setToughness(lifeDifference); - - if (lifeDifference == 0 - || !player1.isLifeTotalCanChange() - || !player2.isLifeTotalCanChange()) { - return token.putOntoBattlefield(1, game, source, source.getControllerId()); - } - - if (lifePlayer1 < lifePlayer2 && (!player1.isCanGainLife() || !player2.isCanLoseLife())) { - return token.putOntoBattlefield(1, game, source, source.getControllerId()); - } - - if (lifePlayer1 > lifePlayer2 && (!player1.isCanLoseLife() || !player2.isCanGainLife())) { - return token.putOntoBattlefield(1, game, source, source.getControllerId()); - } - - player1.setLife(lifePlayer2, game, source); - player2.setLife(lifePlayer1, game, source); - return token.putOntoBattlefield(1, game, source, source.getControllerId()); + int lifeDifference = Math.abs(player1.getLife() - player2.getLife()); + return new PhyrexianRebirthHorrorToken(lifeDifference, lifeDifference) + .putOntoBattlefield(1, game, source, source.getControllerId()); } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/p/ProfessorOfSymbology.java b/Mage.Sets/src/mage/cards/p/ProfessorOfSymbology.java new file mode 100644 index 000000000000..a81f4db57595 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/ProfessorOfSymbology.java @@ -0,0 +1,38 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.LearnEffect; +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 ProfessorOfSymbology extends CardImpl { + + public ProfessorOfSymbology(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.KOR); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // When Professor of Symbology enters the battlefield, learn. + this.addAbility(new EntersBattlefieldTriggeredAbility(new LearnEffect())); + } + + private ProfessorOfSymbology(final ProfessorOfSymbology card) { + super(card); + } + + @Override + public ProfessorOfSymbology copy() { + return new ProfessorOfSymbology(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/ProfessorOfZoomancy.java b/Mage.Sets/src/mage/cards/p/ProfessorOfZoomancy.java new file mode 100644 index 000000000000..da4f6e92fc72 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/ProfessorOfZoomancy.java @@ -0,0 +1,39 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.WitherbloomToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ProfessorOfZoomancy extends CardImpl { + + public ProfessorOfZoomancy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.BEAR); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // When Professor of Zoomancy enters the battlefield, create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life." + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new WitherbloomToken()))); + } + + private ProfessorOfZoomancy(final ProfessorOfZoomancy card) { + super(card); + } + + @Override + public ProfessorOfZoomancy copy() { + return new ProfessorOfZoomancy(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/ProfessorOnyx.java b/Mage.Sets/src/mage/cards/p/ProfessorOnyx.java new file mode 100644 index 000000000000..090d468a5ef3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/ProfessorOnyx.java @@ -0,0 +1,146 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.*; +import mage.cards.Card; +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.Predicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetDiscard; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ProfessorOnyx extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent( + "a creature with the greatest power among creatures that player controls" + ); + + static { + filter.add(ProfessorOnyxPredicate.instance); + } + + public ProfessorOnyx(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{4}{B}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.LILIANA); + this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, each opponent loses 2 life and you gain 2 life. + Ability ability = new MagecraftAbility(new LoseLifeOpponentsEffect(2), false); + ability.addEffect(new GainLifeEffect(2).concatBy("and")); + this.addAbility(ability); + + // +1: You lose 1 life. Look at the top three cards of your library. Put one of them into your hand and the rest into your graveyard. + ability = new LoyaltyAbility(new LoseLifeSourceControllerEffect(1), 1); + ability.addEffect(new LookLibraryAndPickControllerEffect( + StaticValue.get(3), false, StaticValue.get(1), StaticFilters.FILTER_CARD, + Zone.GRAVEYARD, false, false, false, Zone.HAND, false + ).setText("Look at the top three cards of your library. Put one of them into your hand and the rest into your graveyard")); + this.addAbility(ability); + + // −3: Each opponent sacrifices a creature with the greatest power among creatures that player controls. + this.addAbility(new LoyaltyAbility(new SacrificeOpponentsEffect(filter), -3)); + + // −8: Each opponent may discard a card. If they don't, they lose 3 life. Repeat this process six more times. + this.addAbility(new LoyaltyAbility(new ProfessorOnyxEffect(), -8)); + } + + private ProfessorOnyx(final ProfessorOnyx card) { + super(card); + } + + @Override + public ProfessorOnyx copy() { + return new ProfessorOnyx(this); + } +} + +enum ProfessorOnyxPredicate implements Predicate { + instance; + + @Override + public boolean apply(Permanent input, Game game) { + return input.getPower().getValue() + >= game + .getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_CONTROLLED_CREATURE, + input.getControllerId(), game + ) + .stream() + .filter(Objects::nonNull) + .map(MageObject::getPower) + .mapToInt(MageInt::getValue) + .max() + .orElse(Integer.MIN_VALUE); + } +} + +class ProfessorOnyxEffect extends OneShotEffect { + + ProfessorOnyxEffect() { + super(Outcome.Benefit); + staticText = "each opponent may discard a card. If they don't, " + + "they lose 3 life. Repeat this process six more times"; + } + + private ProfessorOnyxEffect(final ProfessorOnyxEffect effect) { + super(effect); + } + + @Override + public ProfessorOnyxEffect copy() { + return new ProfessorOnyxEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (int i = 0; i < 6; i++) { + Map playerMap = new HashMap<>(); + for (UUID playerId : game.getOpponents(source.getControllerId())) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + TargetDiscard target = new TargetDiscard( + 0, 1, StaticFilters.FILTER_CARD, playerId + ); + player.choose(Outcome.Discard, target, source.getSourceId(), game); + playerMap.put(playerId, game.getCard(target.getFirstTarget())); + } + for (UUID playerId : game.getOpponents(source.getControllerId())) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + if (playerMap.get(playerId) == null + || !player.discard(playerMap.get(playerId), false, source, game)) { + player.loseLife(3, game, source, false); + } + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/p/ProfessorsWarning.java b/Mage.Sets/src/mage/cards/p/ProfessorsWarning.java new file mode 100644 index 000000000000..a9e7a820455a --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/ProfessorsWarning.java @@ -0,0 +1,43 @@ +package mage.cards.p; + +import mage.abilities.Mode; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.counters.CounterType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ProfessorsWarning extends CardImpl { + + public ProfessorsWarning(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}"); + + // Choose one — + // • Put a +1/+1 counter on target creature. + this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + + // • Target creature gains indestructible until end of turn. + Mode mode = new Mode(new GainAbilityTargetEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn)); + mode.addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addMode(mode); + } + + private ProfessorsWarning(final ProfessorsWarning card) { + super(card); + } + + @Override + public ProfessorsWarning copy() { + return new ProfessorsWarning(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/Prohibit.java b/Mage.Sets/src/mage/cards/p/Prohibit.java index 3351baa16960..4a7e9b100d24 100644 --- a/Mage.Sets/src/mage/cards/p/Prohibit.java +++ b/Mage.Sets/src/mage/cards/p/Prohibit.java @@ -47,9 +47,9 @@ class ProhibitEffect extends OneShotEffect { ProhibitEffect() { super(Outcome.DestroyPermanent); - this.staticText = "Counter target spell if its converted mana cost " - + "is 2 or less. if this spell was kicked, counter that " - + "spell if its converted mana cost is 4 or less instead."; + this.staticText = "Counter target spell if its mana value " + + "is 2 or less. If this spell was kicked, counter that " + + "spell if its mana value is 4 or less instead."; } ProhibitEffect(final ProhibitEffect effect) { @@ -67,7 +67,7 @@ public boolean apply(Game game, Ability source) { if (controller != null) { Spell targetSpell = game.getSpell(this.getTargetPointer().getFirst(game, source)); if (targetSpell != null) { - int cmc = targetSpell.getConvertedManaCost(); + int cmc = targetSpell.getManaValue(); if (cmc <= 2 || (KickedCondition.instance.apply(game, source) && cmc <= 4)) { game.getStack().counter(targetSpell.getId(), source, game); diff --git a/Mage.Sets/src/mage/cards/p/PromiseOfLoyalty.java b/Mage.Sets/src/mage/cards/p/PromiseOfLoyalty.java new file mode 100644 index 000000000000..1a0847bb96e7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PromiseOfLoyalty.java @@ -0,0 +1,137 @@ +package mage.cards.p; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.RestrictionEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PromiseOfLoyalty extends CardImpl { + + public PromiseOfLoyalty(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{W}"); + + // Each player puts a vow counter on a creature they control and sacrifices the rest. Each of those creatures can't attack you or planeswalkers you control for as long as it has a vow counter on it. + this.getSpellAbility().addEffect(new PromiseOfLoyaltyEffect()); + } + + private PromiseOfLoyalty(final PromiseOfLoyalty card) { + super(card); + } + + @Override + public PromiseOfLoyalty copy() { + return new PromiseOfLoyalty(this); + } +} + +class PromiseOfLoyaltyEffect extends OneShotEffect { + + PromiseOfLoyaltyEffect() { + super(Outcome.Benefit); + staticText = "each player puts a vow counter on a creature they control and sacrifices the rest. " + + "Each of those creatures can't attack you or planeswalkers you control for as long as it has a vow counter on it"; + } + + private PromiseOfLoyaltyEffect(final PromiseOfLoyaltyEffect effect) { + super(effect); + } + + @Override + public PromiseOfLoyaltyEffect copy() { + return new PromiseOfLoyaltyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + List permanents = new ArrayList<>(); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null || game.getBattlefield().count( + StaticFilters.FILTER_CONTROLLED_CREATURE, + source.getSourceId(), playerId, game + ) < 1) { + continue; + } + TargetPermanent target = new TargetControlledCreaturePermanent(); + target.setNotTarget(true); + player.choose(outcome, target, source.getSourceId(), game); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent == null) { + continue; + } + permanents.add(permanent); + permanent.addCounters(CounterType.VOW.createInstance(), playerId, source, game); + } + for (Permanent permanent : game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_PERMANENT_CREATURE, source.getSourceId(), game + )) { + if (permanent == null) { + continue; + } + if (permanents.contains(permanent)) { + game.addEffect(new PromiseOfLoyaltyAttackEffect().setTargetPointer(new FixedTarget(permanent, game)), source); + } else { + permanent.sacrifice(source, game); + } + } + return true; + } +} + +class PromiseOfLoyaltyAttackEffect extends RestrictionEffect { + + public PromiseOfLoyaltyAttackEffect() { + super(Duration.Custom); + } + + public PromiseOfLoyaltyAttackEffect(final PromiseOfLoyaltyAttackEffect effect) { + super(effect); + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + Permanent p = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (p == null || p.getCounters(game).getCount(CounterType.VOW) < 1) { + discard(); + return false; + } + return permanent.getId().equals(p.getId()); + } + + @Override + public boolean canAttack(Permanent attacker, UUID defenderId, Ability source, Game game, boolean canUseChooseDialogs) { + if (defenderId == null) { + return true; + } + if (source.isControlledBy(defenderId)) { + return false; + } + Permanent planeswalker = game.getPermanent(defenderId); + return planeswalker == null || !planeswalker.isControlledBy(source.getControllerId()); + } + + + @Override + public PromiseOfLoyaltyAttackEffect copy() { + return new PromiseOfLoyaltyAttackEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PromisingDuskmage.java b/Mage.Sets/src/mage/cards/p/PromisingDuskmage.java new file mode 100644 index 000000000000..3c4fec643402 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PromisingDuskmage.java @@ -0,0 +1,58 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PromisingDuskmage extends CardImpl { + + public PromisingDuskmage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // When Promising Duskmage dies, if it had a +1/+1 counter on it, draw a card. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1)), + PromisingDuskmageCondition.instance, "When {this} dies, " + + "if it had a +1/+1 counter on it, draw a card." + )); + } + + private PromisingDuskmage(final PromisingDuskmage card) { + super(card); + } + + @Override + public PromisingDuskmage copy() { + return new PromisingDuskmage(this); + } +} + +enum PromisingDuskmageCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = (Permanent) source.getEffects().get(0).getValue("permanentLeftBattlefield"); + return permanent != null && permanent.getCounters(game).containsKey(CounterType.P1P1); + } +} diff --git a/Mage.Sets/src/mage/cards/p/ProperBurial.java b/Mage.Sets/src/mage/cards/p/ProperBurial.java index d13f6f5c5c99..b26e9701437c 100644 --- a/Mage.Sets/src/mage/cards/p/ProperBurial.java +++ b/Mage.Sets/src/mage/cards/p/ProperBurial.java @@ -58,8 +58,8 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD - && ((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD) { + ZoneChangeEvent zoneChangeEvent = (ZoneChangeEvent) event; + if (zoneChangeEvent.isDiesEvent()) { Permanent permanent = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); if (permanent != null && permanent.isControlledBy(this.getControllerId()) diff --git a/Mage.Sets/src/mage/cards/p/Prophecy.java b/Mage.Sets/src/mage/cards/p/Prophecy.java index 95a47430952a..466fab7f200e 100644 --- a/Mage.Sets/src/mage/cards/p/Prophecy.java +++ b/Mage.Sets/src/mage/cards/p/Prophecy.java @@ -33,7 +33,7 @@ public Prophecy(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addTarget(new TargetOpponent()); // Draw a card at the beginning of the next turn's upkeep. - this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextUpkeepDelayedTriggeredAbility(new DrawCardSourceControllerEffect(1)), false)); + this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextUpkeepDelayedTriggeredAbility(new DrawCardSourceControllerEffect(1)), false).concatBy("
")); } private Prophecy(final Prophecy card) { @@ -50,7 +50,7 @@ class ProphecyEffect extends OneShotEffect { public ProphecyEffect() { super(Outcome.GainLife); - this.staticText = "Reveal the top card of target opponent's library. If it's a land, you gain 1 life. Then that player shuffles their library"; + this.staticText = "Reveal the top card of target opponent's library. If it's a land, you gain 1 life. Then that player shuffles"; } public ProphecyEffect(final ProphecyEffect effect) { diff --git a/Mage.Sets/src/mage/cards/p/ProteanHulk.java b/Mage.Sets/src/mage/cards/p/ProteanHulk.java index 5e087e9a86d2..59cf04d5c0ac 100644 --- a/Mage.Sets/src/mage/cards/p/ProteanHulk.java +++ b/Mage.Sets/src/mage/cards/p/ProteanHulk.java @@ -45,7 +45,7 @@ public ProteanHulk copy() { class ProteanHulkTarget extends TargetCardInLibrary { private static final FilterCard filter - = new FilterCreatureCard("any number of creature cards with total converted mana cost 6 or less"); + = new FilterCreatureCard("creature cards with total mana value 6 or less"); ProteanHulkTarget() { super(0, Integer.MAX_VALUE, filter); @@ -71,6 +71,6 @@ public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) { } Cards cards = new CardsImpl(this.getTargets()); cards.add(id); - return cards.getCards(game).stream().filter(Objects::nonNull).mapToInt(MageObject::getConvertedManaCost).sum() <= 6; + return cards.getCards(game).stream().filter(Objects::nonNull).mapToInt(MageObject::getManaValue).sum() <= 6; } } diff --git a/Mage.Sets/src/mage/cards/p/ProteanThaumaturge.java b/Mage.Sets/src/mage/cards/p/ProteanThaumaturge.java index 1439abb2c3fe..fe288fc25db0 100644 --- a/Mage.Sets/src/mage/cards/p/ProteanThaumaturge.java +++ b/Mage.Sets/src/mage/cards/p/ProteanThaumaturge.java @@ -12,9 +12,8 @@ import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.util.functions.CopyApplier; diff --git a/Mage.Sets/src/mage/cards/p/PrototypePortal.java b/Mage.Sets/src/mage/cards/p/PrototypePortal.java index 1d5be3b4d79c..f9de015016fd 100644 --- a/Mage.Sets/src/mage/cards/p/PrototypePortal.java +++ b/Mage.Sets/src/mage/cards/p/PrototypePortal.java @@ -4,6 +4,7 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.CostAdjuster; import mage.abilities.costs.VariableCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; @@ -35,11 +36,14 @@ public PrototypePortal(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); // Imprint - When Prototype Portal enters the battlefield, you may exile an artifact card from your hand. - this.addAbility(new EntersBattlefieldTriggeredAbility(new PrototypePortalEffect(), true)); + this.addAbility(new EntersBattlefieldTriggeredAbility( + new PrototypePortalEffect(), true, "Imprint — " + )); // {X}, {tap}: Create a token that's a copy of the exiled card. X is the converted mana cost of that card. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PrototypePortalCreateTokenEffect(), new ManaCostsImpl("{X}")); + Ability ability = new SimpleActivatedAbility(new PrototypePortalCreateTokenEffect(), new ManaCostsImpl("{X}")); ability.addCost(new TapSourceCost()); + ability.setCostAdjuster(PrototypePortalAdjuster.instance); this.addAbility(ability); } @@ -47,6 +51,15 @@ private PrototypePortal(final PrototypePortal card) { super(card); } + @Override + public PrototypePortal copy() { + return new PrototypePortal(this); + } +} + +enum PrototypePortalAdjuster implements CostAdjuster { + instance; + @Override public void adjustCosts(Ability ability, Game game) { Permanent card = game.getPermanent(ability.getSourceId()); @@ -54,7 +67,7 @@ public void adjustCosts(Ability ability, Game game) { if (!card.getImprinted().isEmpty()) { Card imprinted = game.getCard(card.getImprinted().get(0)); if (imprinted != null) { - ability.getManaCostsToPay().add(0, new GenericManaCost(imprinted.getConvertedManaCost())); + ability.getManaCostsToPay().add(0, new GenericManaCost(imprinted.getManaValue())); } } } @@ -66,21 +79,16 @@ public void adjustCosts(Ability ability, Game game) { } } } - - @Override - public PrototypePortal copy() { - return new PrototypePortal(this); - } } class PrototypePortalEffect extends OneShotEffect { - public PrototypePortalEffect() { + PrototypePortalEffect() { super(Outcome.Benefit); staticText = "exile an artifact card from your hand"; } - public PrototypePortalEffect(PrototypePortalEffect effect) { + private PrototypePortalEffect(PrototypePortalEffect effect) { super(effect); } @@ -115,12 +123,12 @@ public PrototypePortalEffect copy() { class PrototypePortalCreateTokenEffect extends OneShotEffect { - public PrototypePortalCreateTokenEffect() { + PrototypePortalCreateTokenEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "Create a token that's a copy of the exiled card. X is the converted mana cost of that card"; + this.staticText = "Create a token that's a copy of the exiled card. X is the mana value of that card"; } - public PrototypePortalCreateTokenEffect(final PrototypePortalCreateTokenEffect effect) { + private PrototypePortalCreateTokenEffect(final PrototypePortalCreateTokenEffect effect) { super(effect); } diff --git a/Mage.Sets/src/mage/cards/p/ProwessOfTheFair.java b/Mage.Sets/src/mage/cards/p/ProwessOfTheFair.java index b9f4d4d5e116..7994ff3fb6c9 100644 --- a/Mage.Sets/src/mage/cards/p/ProwessOfTheFair.java +++ b/Mage.Sets/src/mage/cards/p/ProwessOfTheFair.java @@ -8,7 +8,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.permanent.token.ElfWarriorToken; diff --git a/Mage.Sets/src/mage/cards/p/PsychicBattle.java b/Mage.Sets/src/mage/cards/p/PsychicBattle.java index 6004e9e4633f..bfe4b53f17c1 100644 --- a/Mage.Sets/src/mage/cards/p/PsychicBattle.java +++ b/Mage.Sets/src/mage/cards/p/PsychicBattle.java @@ -88,7 +88,7 @@ class PsychicBattleEffect extends OneShotEffect { public PsychicBattleEffect() { super(Outcome.Benefit); - this.staticText = "each player reveals the top card of their library. The player who reveals the card with the highest converted mana cost may change the target or targets. If two or more cards are tied for highest cost, the target or targets remain unchanged"; + this.staticText = "each player reveals the top card of their library. The player who reveals the card with the highest mana value may change the target or targets. If two or more cards are tied for highest, the target or targets remain unchanged"; } public PsychicBattleEffect(final PsychicBattleEffect effect) { @@ -110,7 +110,7 @@ public boolean apply(Game game, Ability source) { if (player != null && player.getLibrary().hasCards()) { Card card = player.getLibrary().getFromTop(game); player.revealCards(sourceObject.getIdName() + " (" + player.getName() + ')', new CardsImpl(card), game); - manacostMap.put(player, card.getConvertedManaCost()); + manacostMap.put(player, card.getManaValue()); } } diff --git a/Mage.Sets/src/mage/cards/p/PsychicRebuttal.java b/Mage.Sets/src/mage/cards/p/PsychicRebuttal.java index a5bf70086d46..960db894b792 100644 --- a/Mage.Sets/src/mage/cards/p/PsychicRebuttal.java +++ b/Mage.Sets/src/mage/cards/p/PsychicRebuttal.java @@ -1,7 +1,6 @@ package mage.cards.p; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.condition.common.SpellMasteryCondition; @@ -21,8 +20,9 @@ import mage.target.Target; import mage.target.TargetSpell; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class PsychicRebuttal extends CardImpl { @@ -35,7 +35,7 @@ public final class PsychicRebuttal extends CardImpl { } public PsychicRebuttal(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); // Counter target instant or sorcery spell that targets you. this.getSpellAbility().addEffect(new PsychicRebuttalEffect()); @@ -82,15 +82,7 @@ public boolean apply(Game game, Ability source) { if (SpellMasteryCondition.instance.apply(game, source) && controller.chooseUse(Outcome.PlayForFree, "Copy " + spell.getName() + " (you may choose new targets for the copy)?", source, game)) { - - StackObject newStackObject = spell.createCopyOnStack(game, source, source.getControllerId(), true); - if (newStackObject instanceof Spell) { - String activateMessage = ((Spell) newStackObject).getActivatedMessage(game); - if (activateMessage.startsWith(" casts ")) { - activateMessage = activateMessage.substring(6); - } - game.informPlayers(controller.getLogName() + activateMessage); - } + spell.createCopyOnStack(game, source, source.getControllerId(), true); } return true; diff --git a/Mage.Sets/src/mage/cards/p/PsychicSurgery.java b/Mage.Sets/src/mage/cards/p/PsychicSurgery.java index f98892c7866e..1447a72813ea 100644 --- a/Mage.Sets/src/mage/cards/p/PsychicSurgery.java +++ b/Mage.Sets/src/mage/cards/p/PsychicSurgery.java @@ -104,7 +104,7 @@ public boolean apply(Game game, Ability source) { if (controller != null && opponent != null) { Cards cards = new CardsImpl(opponent.getLibrary().getTopCards(game, 2)); controller.lookAtCards(source, null, cards, game); - if (!cards.isEmpty() && controller.chooseUse(Outcome.Exile, "Do you wish to exile a card?", source, game)) { + if (!cards.isEmpty() && controller.chooseUse(Outcome.Exile, "Exile a card?", source, game)) { TargetCard target = new TargetCard(Zone.LIBRARY, new FilterCard("card to exile")); if (controller.choose(Outcome.Exile, cards, target, game)) { Card card = cards.get(target.getFirstTarget(), game); diff --git a/Mage.Sets/src/mage/cards/p/PsychicTheft.java b/Mage.Sets/src/mage/cards/p/PsychicTheft.java index 32431bb15ef3..a69ee956d9df 100644 --- a/Mage.Sets/src/mage/cards/p/PsychicTheft.java +++ b/Mage.Sets/src/mage/cards/p/PsychicTheft.java @@ -1,13 +1,11 @@ package mage.cards.p; import mage.MageObject; +import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.decorator.ConditionalOneShotEffect; -import mage.abilities.effects.AsThoughEffect; -import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnFromExileEffect; import mage.cards.Card; @@ -16,15 +14,15 @@ import mage.constants.*; import mage.filter.common.FilterInstantOrSorceryCard; import mage.game.Game; +import mage.game.events.GameEvent; import mage.game.stack.Spell; import mage.players.Player; import mage.target.TargetCard; import mage.target.TargetPlayer; -import mage.target.targetpointer.FixedTarget; -import mage.watchers.common.SpellsCastWatcher; +import mage.util.CardUtil; +import mage.watchers.Watcher; -import java.util.List; -import java.util.UUID; +import java.util.*; /** * @author L_J (significantly based on code by jeffwadsworth and Styxo) @@ -35,9 +33,9 @@ public PsychicTheft(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{U}"); // Target player reveals their hand. You choose an instant or sorcery card from it and exile that card. You may cast that card for as long as it remains exiled. At the beginning of the next end step, if you haven't cast the card, return it to its owner's hand. - this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addEffect(new PsychicTheftEffect()); - + this.getSpellAbility().addTarget(new TargetPlayer()); + this.getSpellAbility().addWatcher(new PsychicTheftWatcher()); } private PsychicTheft(final PsychicTheft card) { @@ -54,12 +52,14 @@ class PsychicTheftEffect extends OneShotEffect { private static final FilterInstantOrSorceryCard filter = new FilterInstantOrSorceryCard(); - public PsychicTheftEffect() { + PsychicTheftEffect() { super(Outcome.Benefit); - this.staticText = "Target player reveals their hand. You choose an instant or sorcery card from it and exile that card. You may cast that card for as long as it remains exiled. At the beginning of the next end step, if you haven't cast the card, return it to its owner's hand."; + this.staticText = "Target player reveals their hand. You choose an instant or sorcery card from it " + + "and exile that card. You may cast that card for as long as it remains exiled. " + + "At the beginning of the next end step, if you haven't cast the card, return it to its owner's hand."; } - public PsychicTheftEffect(final PsychicTheftEffect effect) { + private PsychicTheftEffect(final PsychicTheftEffect effect) { super(effect); } @@ -72,101 +72,76 @@ public PsychicTheftEffect copy() { public boolean apply(Game game, Ability source) { Player opponent = game.getPlayer(targetPointer.getFirst(game, source)); MageObject sourceObject = game.getObject(source.getSourceId()); - if (opponent != null && sourceObject != null) { - opponent.revealCards(sourceObject.getName(), opponent.getHand(), game); - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - int cardsHand = opponent.getHand().count(filter, game); - Card chosenCard = null; - if (cardsHand > 0) { - TargetCard target = new TargetCard(Zone.HAND, filter); - if (controller.choose(Outcome.Exile, opponent.getHand(), target, game)) { - chosenCard = opponent.getHand().get(target.getFirstTarget(), game); - } - } - if (chosenCard != null) { - - opponent.moveCardToExileWithInfo(chosenCard, source.getSourceId(), sourceObject.getIdName(), source, game, Zone.HAND, true); - - AsThoughEffect effect = new PsychicTheftCastFromExileEffect(); - effect.setTargetPointer(new FixedTarget(chosenCard.getId())); - game.addEffect(effect, source); - - OneShotEffect effect2 = new ReturnFromExileEffect(source.getSourceId(), Zone.HAND); - Condition condition = new PsychicTheftCondition(source.getSourceId(), chosenCard.getId()); - - ConditionalOneShotEffect effect3 = new ConditionalOneShotEffect(effect2, condition, "if you haven't cast it, return it to its owner's hand."); - DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect3); - delayedAbility.addWatcher(new SpellsCastWatcher()); - game.addDelayedTriggeredAbility(delayedAbility, source); - return true; - } + if (opponent == null || sourceObject == null) { + return false; + } + opponent.revealCards(sourceObject.getName(), opponent.getHand(), game); + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + int cardsHand = opponent.getHand().count(filter, game); + Card chosenCard = null; + if (cardsHand > 0) { + TargetCard target = new TargetCard(Zone.HAND, filter); + if (controller.choose(Outcome.Exile, opponent.getHand(), target, game)) { + chosenCard = opponent.getHand().get(target.getFirstTarget(), game); } } - return false; + if (chosenCard == null) { + return false; + } + UUID exileId = CardUtil.getExileZoneId(game, source); + controller.moveCardToExileWithInfo(chosenCard, exileId, sourceObject.getIdName(), source, game, Zone.HAND, true); + + CardUtil.makeCardPlayable(game, source, chosenCard, Duration.Custom, false); + + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility( + new ConditionalOneShotEffect( + new ReturnFromExileEffect(Zone.HAND), + new PsychicTheftCondition(chosenCard, game), + "if you haven't cast it, return it to its owner's hand." + ) + ), source); + return true; } } -class PsychicTheftCastFromExileEffect extends AsThoughEffectImpl { - - PsychicTheftCastFromExileEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit); - staticText = "You may cast that card for as long as it remains exiled"; - } +class PsychicTheftCondition implements Condition { + private final MageObjectReference mor; - PsychicTheftCastFromExileEffect(final PsychicTheftCastFromExileEffect effect) { - super(effect); + PsychicTheftCondition(Card card, Game game) { + this.mor = new MageObjectReference(card.getId(), card.getZoneChangeCounter(game) + 1, game); } @Override public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public PsychicTheftCastFromExileEffect copy() { - return new PsychicTheftCastFromExileEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (targetPointer.getTargets(game, source).contains(objectId) - && game.getState().getZone(objectId) == Zone.EXILED) { - Player player = game.getPlayer(source.getControllerId()); - Card card = game.getCard(objectId); - return player != null - && card != null; - } - return false; + PsychicTheftWatcher watcher = game.getState().getWatcher(PsychicTheftWatcher.class); + return watcher != null && !watcher.checkPlayer(source.getSourceId(), mor); } } -class PsychicTheftCondition implements Condition { +class PsychicTheftWatcher extends Watcher { - protected UUID exileId; - protected UUID cardId; + private final Map> map = new HashMap<>(); - public PsychicTheftCondition(UUID exileId, UUID cardId) { - this.exileId = exileId; - this.cardId = cardId; + PsychicTheftWatcher() { + super(WatcherScope.GAME); } @Override - public boolean apply(Game game, Ability source) { - if (!game.getExile().getExileZone(exileId).contains(cardId)) { - return false; + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.SPELL_CAST) { + return; } - SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class); - if (watcher != null) { - List spells = watcher.getSpellsCastThisTurn(source.getControllerId()); - if (spells != null) { - for (Spell spell : spells) { - if (spell.getSourceId().equals(cardId)) { - return false; - } - } - } + Spell spell = game.getSpell(event.getTargetId()); + if (spell == null || spell.getCard() == null || spell.getCard().getMainCard() == null) { + return; } - return true; + map.computeIfAbsent(event.getPlayerId(), x -> new HashSet<>()).add(new MageObjectReference(spell.getCard().getMainCard(), game)); + } + + boolean checkPlayer(UUID playerId, MageObjectReference mor) { + return map.computeIfAbsent(playerId, x -> new HashSet<>()).contains(mor); } } diff --git a/Mage.Sets/src/mage/cards/p/PsychicTransfer.java b/Mage.Sets/src/mage/cards/p/PsychicTransfer.java index 5313f27d26d5..af63bc2732be 100644 --- a/Mage.Sets/src/mage/cards/p/PsychicTransfer.java +++ b/Mage.Sets/src/mage/cards/p/PsychicTransfer.java @@ -1,7 +1,5 @@ - package mage.cards.p; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; @@ -12,15 +10,15 @@ import mage.players.Player; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author Quercitron */ public final class PsychicTransfer extends CardImpl { public PsychicTransfer(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{U}"); // If the difference between your life total and target player's life total is 5 or less, exchange life totals with that player. this.getSpellAbility().addEffect(new PsychicTransferEffect()); @@ -37,15 +35,15 @@ public PsychicTransfer copy() { } } -class PsychicTransferEffect extends OneShotEffect -{ +class PsychicTransferEffect extends OneShotEffect { - public PsychicTransferEffect() { + PsychicTransferEffect() { super(Outcome.Neutral); - this.staticText = "If the difference between your life total and target player's life total is 5 or less, exchange life totals with that player"; + this.staticText = "If the difference between your life total and target player's " + + "life total is 5 or less, exchange life totals with that player"; } - public PsychicTransferEffect(final PsychicTransferEffect effect) { + private PsychicTransferEffect(final PsychicTransferEffect effect) { super(effect); } @@ -57,32 +55,12 @@ public PsychicTransferEffect copy() { @Override public boolean apply(Game game, Ability source) { Player sourcePlayer = game.getPlayer(source.getControllerId()); - Player targetPlayer = game.getPlayer(source.getTargets().getFirstTarget()); - if (sourcePlayer != null && targetPlayer != null) { - int lifePlayer1 = sourcePlayer.getLife(); - int lifePlayer2 = targetPlayer.getLife(); - - if (Math.abs(lifePlayer1 - lifePlayer2) > 5) { - return false; - } - - if (lifePlayer1 == lifePlayer2) { - return false; - } - - // 20110930 - 118.7, 118.8 - if (lifePlayer1 < lifePlayer2 && (!sourcePlayer.isCanGainLife() || !targetPlayer.isCanLoseLife())) { - return false; - } - - if (lifePlayer1 > lifePlayer2 && (!sourcePlayer.isCanLoseLife() || !targetPlayer.isCanGainLife())) { - return false; - } - - sourcePlayer.setLife(lifePlayer2, game, source); - targetPlayer.setLife(lifePlayer1, game, source); - return true; + Player targetPlayer = game.getPlayer(source.getFirstTarget()); + if (sourcePlayer == null || targetPlayer == null + || Math.abs(sourcePlayer.getLife() - targetPlayer.getLife()) > 5) { + return false; } - return false; + sourcePlayer.exchangeLife(targetPlayer, source, game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/p/PucasMischief.java b/Mage.Sets/src/mage/cards/p/PucasMischief.java index 8fca055d9531..9c3c739ccc9c 100644 --- a/Mage.Sets/src/mage/cards/p/PucasMischief.java +++ b/Mage.Sets/src/mage/cards/p/PucasMischief.java @@ -26,7 +26,7 @@ */ public final class PucasMischief extends CardImpl { - private static final String rule = "you may exchange control of target nonland permanent you control and target nonland permanent an opponent controls with an equal or lesser converted mana cost"; + private static final String rule = "you may exchange control of target nonland permanent you control and target nonland permanent an opponent controls with an equal or lesser mana value"; public PucasMischief(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{U}"); @@ -91,7 +91,7 @@ public PucasMischiefSecondTarget() { this.filter = this.filter.copy(); filter.add(TargetController.OPPONENT.getControllerPredicate()); filter.add(Predicates.not(CardType.LAND.getPredicate())); - setTargetName("permanent an opponent controls with an equal or lesser converted mana cost"); + setTargetName("permanent an opponent controls with an equal or lesser mana value"); } public PucasMischiefSecondTarget(final PucasMischiefSecondTarget target) { @@ -105,7 +105,7 @@ public boolean canTarget(UUID id, Ability source, Game game) { Permanent target1 = game.getPermanent(source.getFirstTarget()); Permanent opponentPermanent = game.getPermanent(id); if (target1 != null && opponentPermanent != null) { - return target1.getConvertedManaCost() >= opponentPermanent.getConvertedManaCost(); + return target1.getManaValue() >= opponentPermanent.getManaValue(); } } return false; @@ -119,7 +119,7 @@ public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game ga if (targetSource != null) { for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, sourceId, game)) { if (!targets.containsKey(permanent.getId()) && permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) { - if (firstTarget.getConvertedManaCost() >= permanent.getConvertedManaCost()) { + if (firstTarget.getManaValue() >= permanent.getManaValue()) { possibleTargets.add(permanent.getId()); } } diff --git a/Mage.Sets/src/mage/cards/p/PullFromEternity.java b/Mage.Sets/src/mage/cards/p/PullFromEternity.java index 0692ce2ff2cf..88c9724d555e 100644 --- a/Mage.Sets/src/mage/cards/p/PullFromEternity.java +++ b/Mage.Sets/src/mage/cards/p/PullFromEternity.java @@ -12,7 +12,7 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; -import mage.filter.predicate.other.FaceDownPredicate; +import mage.filter.predicate.card.FaceDownPredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInExile; diff --git a/Mage.Sets/src/mage/cards/p/PullFromTheDeep.java b/Mage.Sets/src/mage/cards/p/PullFromTheDeep.java index 5f05df033b79..b8a8cd9712de 100644 --- a/Mage.Sets/src/mage/cards/p/PullFromTheDeep.java +++ b/Mage.Sets/src/mage/cards/p/PullFromTheDeep.java @@ -40,7 +40,7 @@ public PullFromTheDeep(UUID ownerId, CardSetInfo setInfo) { effect.setTargetPointer(new SecondTargetPointer()); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0,1,filterSorcery)); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private PullFromTheDeep(final PullFromTheDeep card) { diff --git a/Mage.Sets/src/mage/cards/p/PullingTeeth.java b/Mage.Sets/src/mage/cards/p/PullingTeeth.java index d5280ff4696d..e02487130457 100644 --- a/Mage.Sets/src/mage/cards/p/PullingTeeth.java +++ b/Mage.Sets/src/mage/cards/p/PullingTeeth.java @@ -42,7 +42,7 @@ class PullingTeethEffect extends OneShotEffect { public PullingTeethEffect() { super(Outcome.Discard); - this.staticText = "Clash with an opponent. If you win, target player discards two cards. Otherwise that player discards a card"; + this.staticText = "Clash with an opponent. If you win, target player discards two cards. Otherwise, that player discards a card"; } public PullingTeethEffect(final PullingTeethEffect effect) { diff --git a/Mage.Sets/src/mage/cards/p/PuppetMaster.java b/Mage.Sets/src/mage/cards/p/PuppetMaster.java index 99195cdcb146..831e818daf13 100644 --- a/Mage.Sets/src/mage/cards/p/PuppetMaster.java +++ b/Mage.Sets/src/mage/cards/p/PuppetMaster.java @@ -54,12 +54,13 @@ public PuppetMaster copy() { class PuppetMasterEffect extends OneShotEffect { - public PuppetMasterEffect() { + PuppetMasterEffect() { super(Outcome.ReturnToHand); - staticText = "return that card to its owner's hand. If that card is returned to its owner's hand this way, you may pay {U}{U}{U}. If you do, return {this} to its owner's hand"; + staticText = "return that card to its owner's hand. If that card is returned to its owner's hand this way, " + + "you may pay {U}{U}{U}. If you do, return {this} to its owner's hand"; } - public PuppetMasterEffect(final PuppetMasterEffect effect) { + private PuppetMasterEffect(final PuppetMasterEffect effect) { super(effect); } @@ -70,25 +71,30 @@ public PuppetMasterEffect copy() { @Override public boolean apply(Game game, Ability source) { - Object object = getValue("attachedTo"); - if (object instanceof Permanent) { - Card card = game.getCard(((Permanent) object).getId()); - if (card != null) { - if (card.moveToZone(Zone.HAND, source, game, false)) { - Cost cost = new ManaCostsImpl("{U}{U}{U}"); - Player controller = game.getPlayer(source.getControllerId()); - Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (controller != null && sourcePermanent != null) { - if (controller.chooseUse(Outcome.Neutral, "Pay " + cost.getText() + " to return " + sourcePermanent.getLogName() + " to its owner's hand?", source, game) - && cost.pay(source, game, source, controller.getId(), false, null)) { - sourcePermanent.moveToZone(Zone.HAND, source, game, false); - } - } - return true; - } - } + Player controller = game.getPlayer(source.getControllerId()); + Permanent permanent = (Permanent) getValue("attachedTo"); + if (controller == null || permanent == null) { + return false; } - return false; + Card card = permanent.getMainCard(); + if (card == null || card.getZoneChangeCounter(game) != permanent.getZoneChangeCounter(game) + 1) { + return false; + } + controller.moveCards(card, Zone.HAND, source, game); + if (game.getState().getZone(card.getId()) != Zone.HAND) { + return false; + } + card = game.getCard(source.getSourceId()); + if (card == null || card.getZoneChangeCounter(game) != source.getSourceObjectZoneChangeCounter() + 1) { + return false; + } + Cost cost = new ManaCostsImpl("{U}{U}{U}"); + if (!controller.chooseUse(Outcome.Neutral, "Pay " + cost.getText() + + " to return " + card.getLogName() + " to its owner's hand?", source, game) + || !cost.pay(source, game, source, controller.getId(), false, null)) { + return true; + } + controller.moveCards(card, Zone.HAND, source, game); + return true; } - } diff --git a/Mage.Sets/src/mage/cards/p/PureReflection.java b/Mage.Sets/src/mage/cards/p/PureReflection.java index a3f856e2d8d4..280e3bcdd200 100644 --- a/Mage.Sets/src/mage/cards/p/PureReflection.java +++ b/Mage.Sets/src/mage/cards/p/PureReflection.java @@ -44,7 +44,7 @@ private class PureReflectionEffect extends OneShotEffect { public PureReflectionEffect() { super(Outcome.Benefit); - staticText = "destroy all Reflections. Then that player creates an X/X white Reflection creature token, where X is the converted mana cost of that spell."; + staticText = "destroy all Reflections. Then that player creates an X/X white Reflection creature token, where X is the mana value of that spell."; } public PureReflectionEffect(PureReflectionEffect effect) { @@ -74,7 +74,7 @@ public boolean apply(Game game, Ability source) { game.getState().processAction(game); // Then that player creates an X/X white Reflection creature token, where X is the converted mana cost of that spell. - ReflectionPureToken token = new ReflectionPureToken(spell.getConvertedManaCost()); + ReflectionPureToken token = new ReflectionPureToken(spell.getManaValue()); token.putOntoBattlefield(1, game, source, spell.getControllerId()); return true; diff --git a/Mage.Sets/src/mage/cards/p/PuresightMerrow.java b/Mage.Sets/src/mage/cards/p/PuresightMerrow.java index 7a6bab138195..1c1e36342f93 100644 --- a/Mage.Sets/src/mage/cards/p/PuresightMerrow.java +++ b/Mage.Sets/src/mage/cards/p/PuresightMerrow.java @@ -77,7 +77,7 @@ public boolean apply(Game game, Ability source) { if (card != null) { Cards cards = new CardsImpl(card); controller.lookAtCards("Puresight Merrow", cards, game); - if (controller.chooseUse(Outcome.Removal, "Do you wish to exile the card from the top of your library?", source, game)) { + if (controller.chooseUse(Outcome.Removal, "Exile the card from the top of your library?", source, game)) { controller.moveCardToExileWithInfo(card, source.getSourceId(), sourceObject.getIdName(), source, game, Zone.LIBRARY, true); } else { game.informPlayers(controller.getLogName() + " puts the card back on top of their library."); diff --git a/Mage.Sets/src/mage/cards/p/Purgatory.java b/Mage.Sets/src/mage/cards/p/Purgatory.java index c60e0cf567d0..283230ab3440 100644 --- a/Mage.Sets/src/mage/cards/p/Purgatory.java +++ b/Mage.Sets/src/mage/cards/p/Purgatory.java @@ -89,9 +89,7 @@ public boolean checkTrigger(GameEvent event, Game game) { if (controller != null) { ZoneChangeEvent zEvent = (ZoneChangeEvent) event; Permanent permanent = zEvent.getTarget(); - if (permanent != null - && zEvent.getToZone() == Zone.GRAVEYARD - && zEvent.getFromZone() == Zone.BATTLEFIELD + if (permanent != null && zEvent.isDiesEvent() && !(permanent instanceof PermanentToken) && permanent.isCreature() && permanent.isOwnedBy(controller.getId())) { diff --git a/Mage.Sets/src/mage/cards/p/PurphorosGodOfTheForge.java b/Mage.Sets/src/mage/cards/p/PurphorosGodOfTheForge.java index db290596d0e4..03550f189276 100644 --- a/Mage.Sets/src/mage/cards/p/PurphorosGodOfTheForge.java +++ b/Mage.Sets/src/mage/cards/p/PurphorosGodOfTheForge.java @@ -15,7 +15,7 @@ import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/p/PutridCyclops.java b/Mage.Sets/src/mage/cards/p/PutridCyclops.java index 670f356d56e8..6d0f8254d9f9 100644 --- a/Mage.Sets/src/mage/cards/p/PutridCyclops.java +++ b/Mage.Sets/src/mage/cards/p/PutridCyclops.java @@ -53,7 +53,7 @@ class PutridCyclopEffect extends OneShotEffect { public PutridCyclopEffect() { super(Outcome.Detriment); - this.staticText = "scry 1, then reveal the top card of your library. {this} gets -X/-X until end of turn, where X is that card's converted mana cost" + this.staticText = "scry 1, then reveal the top card of your library. {this} gets -X/-X until end of turn, where X is that card's mana value" + " (To scry 1, look at the top card of your library, then you may put that card on the bottom of your library.)"; } @@ -75,7 +75,7 @@ public boolean apply(Game game, Ability source) { Card card = controller.getLibrary().getFromTop(game); if (card != null) { controller.revealCards(sourceObject.getIdName(), new CardsImpl(card), game); - int unboost = card.getConvertedManaCost() * -1; + int unboost = card.getManaValue() * -1; ContinuousEffect effect = new BoostSourceEffect(unboost, unboost, Duration.EndOfTurn); game.addEffect(effect, source); } diff --git a/Mage.Sets/src/mage/cards/p/PutridWarrior.java b/Mage.Sets/src/mage/cards/p/PutridWarrior.java index dbe9d123caef..7ea5af224eec 100644 --- a/Mage.Sets/src/mage/cards/p/PutridWarrior.java +++ b/Mage.Sets/src/mage/cards/p/PutridWarrior.java @@ -71,8 +71,8 @@ public PutridWarriorDealsDamageTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLAYER || event.getType() == GameEvent.EventType.DAMAGED_CREATURE - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + return event.getType() == GameEvent.EventType.DAMAGED_PLAYER + || event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/p/PyreOfHeroes.java b/Mage.Sets/src/mage/cards/p/PyreOfHeroes.java index 5af2ccc2c0e9..6ff399a88d00 100644 --- a/Mage.Sets/src/mage/cards/p/PyreOfHeroes.java +++ b/Mage.Sets/src/mage/cards/p/PyreOfHeroes.java @@ -16,8 +16,8 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.StaticFilters; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; -import mage.filter.predicate.permanent.SharesCreatureTypePredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.filter.predicate.mageobject.SharesCreatureTypePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -60,9 +60,9 @@ class PyreOfHeroesEffect extends OneShotEffect { PyreOfHeroesEffect() { super(Outcome.Benefit); - staticText = "Search your library for a creature card that shares a creature type with the sacrificed creature and has converted mana cost equal to 1 " + - "plus that creature's converted mana cost. Put that card " + - "onto the battlefield, then shuffle your library"; + staticText = "Search your library for a creature card that shares a creature type with the sacrificed creature and has mana value equal to 1 " + + "plus that creature's mana value. Put that card " + + "onto the battlefield, then shuffle"; } private PyreOfHeroesEffect(final PyreOfHeroesEffect effect) { @@ -85,9 +85,9 @@ public boolean apply(Game game, Ability source) { if (sacrificedPermanent == null || controller == null) { return false; } - int newConvertedCost = sacrificedPermanent.getConvertedManaCost() + 1; - FilterCard filter = new FilterCard("creature card with converted mana cost " + newConvertedCost); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, newConvertedCost)); + int newConvertedCost = sacrificedPermanent.getManaValue() + 1; + FilterCard filter = new FilterCard("creature card with mana value " + newConvertedCost); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, newConvertedCost)); filter.add(CardType.CREATURE.getPredicate()); filter.add(new SharesCreatureTypePredicate(sacrificedPermanent)); TargetCardInLibrary target = new TargetCardInLibrary(filter); diff --git a/Mage.Sets/src/mage/cards/p/PyromancerAscension.java b/Mage.Sets/src/mage/cards/p/PyromancerAscension.java index af5a2ee11acc..7be8d97c90fa 100644 --- a/Mage.Sets/src/mage/cards/p/PyromancerAscension.java +++ b/Mage.Sets/src/mage/cards/p/PyromancerAscension.java @@ -87,8 +87,8 @@ public boolean checkTrigger(GameEvent event, Game game) { private boolean isControlledInstantOrSorcery(Spell spell) { return spell != null && - (spell.isControlledBy(this.getControllerId())) && - (spell.isInstant() || spell.isSorcery()); + spell.isControlledBy(this.getControllerId()) && + spell.isInstantOrSorcery(); } @Override @@ -134,8 +134,8 @@ public boolean checkTrigger(GameEvent event, Game game) { private boolean isControlledInstantOrSorcery(Spell spell) { return spell != null && - (spell.isControlledBy(this.getControllerId())) && - (spell.isInstant() || spell.isSorcery()); + spell.isControlledBy(this.getControllerId()) && + spell.isInstantOrSorcery(); } @Override diff --git a/Mage.Sets/src/mage/cards/p/PyromancersGauntlet.java b/Mage.Sets/src/mage/cards/p/PyromancersGauntlet.java index 74c2d5c4651e..416d45ff5502 100644 --- a/Mage.Sets/src/mage/cards/p/PyromancersGauntlet.java +++ b/Mage.Sets/src/mage/cards/p/PyromancersGauntlet.java @@ -54,8 +54,7 @@ class PyromancersGauntletReplacementEffect extends ReplacementEffectImpl { @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.DAMAGE_PLAYER - || event.getType() == GameEvent.EventType.DAMAGE_CREATURE - || event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER; + || event.getType() == GameEvent.EventType.DAMAGE_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/p/PyromancersSwath.java b/Mage.Sets/src/mage/cards/p/PyromancersSwath.java index 78425bc1853a..3112bba39e07 100644 --- a/Mage.Sets/src/mage/cards/p/PyromancersSwath.java +++ b/Mage.Sets/src/mage/cards/p/PyromancersSwath.java @@ -56,9 +56,8 @@ class PyromancersSwathReplacementEffect extends ReplacementEffectImpl { @Override public boolean checksEventType(GameEvent event, Game game) { switch (event.getType()) { - case DAMAGE_CREATURE: + case DAMAGE_PERMANENT: case DAMAGE_PLAYER: - case DAMAGE_PLANESWALKER: return true; default: return false; @@ -69,7 +68,7 @@ public boolean checksEventType(GameEvent event, Game game) { public boolean applies(GameEvent event, Ability source, Game game) { if (source.isControlledBy(game.getControllerId(event.getSourceId()))) { MageObject object = game.getObject(event.getSourceId()); - return object != null && (object.isInstant() || object.isSorcery()); + return object != null && object.isInstantOrSorcery(); } return false; } diff --git a/Mage.Sets/src/mage/cards/p/PyrostaticPillar.java b/Mage.Sets/src/mage/cards/p/PyrostaticPillar.java index 67d42381b6b1..3517b2831e92 100644 --- a/Mage.Sets/src/mage/cards/p/PyrostaticPillar.java +++ b/Mage.Sets/src/mage/cards/p/PyrostaticPillar.java @@ -11,7 +11,6 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.stack.Spell; import mage.target.targetpointer.FixedTarget; @@ -64,7 +63,7 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getConvertedManaCost() <= 3){ + if (spell != null && spell.getManaValue() <= 3){ for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getPlayerId())); } @@ -75,6 +74,6 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { - return "Whenever a player casts a spell with converted mana cost 3 or less, {this} deals 2 damage to that player."; + return "Whenever a player casts a spell with mana value 3 or less, {this} deals 2 damage to that player."; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/q/QuandrixApprentice.java b/Mage.Sets/src/mage/cards/q/QuandrixApprentice.java new file mode 100644 index 000000000000..8c4e470d11b8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/q/QuandrixApprentice.java @@ -0,0 +1,44 @@ +package mage.cards.q; + +import mage.MageInt; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +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 java.util.UUID; + +/** + * @author TheElk801 + */ +public final class QuandrixApprentice extends CardImpl { + + public QuandrixApprentice(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, look at the top three cards of your library. You may reveal a land card from among them and put that card into your hand. Put the rest on the bottom of your library in any order. + this.addAbility(new MagecraftAbility(new LookLibraryAndPickControllerEffect( + StaticValue.get(3), false, StaticValue.get(1), StaticFilters.FILTER_CARD_LAND_A, + Zone.LIBRARY, false, true, false, Zone.HAND, true + ))); + } + + private QuandrixApprentice(final QuandrixApprentice card) { + super(card); + } + + @Override + public QuandrixApprentice copy() { + return new QuandrixApprentice(this); + } +} diff --git a/Mage.Sets/src/mage/cards/q/QuandrixCampus.java b/Mage.Sets/src/mage/cards/q/QuandrixCampus.java new file mode 100644 index 000000000000..4fff5fb64739 --- /dev/null +++ b/Mage.Sets/src/mage/cards/q/QuandrixCampus.java @@ -0,0 +1,46 @@ +package mage.cards.q; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class QuandrixCampus extends CardImpl { + + public QuandrixCampus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // Quandrix Campus enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {G} or {U}. + this.addAbility(new GreenManaAbility()); + this.addAbility(new BlueManaAbility()); + + // {4}, {T}: Scry 1. + Ability ability = new SimpleActivatedAbility(new ScryEffect(1), new GenericManaCost(4)); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private QuandrixCampus(final QuandrixCampus card) { + super(card); + } + + @Override + public QuandrixCampus copy() { + return new QuandrixCampus(this); + } +} diff --git a/Mage.Sets/src/mage/cards/q/QuandrixCommand.java b/Mage.Sets/src/mage/cards/q/QuandrixCommand.java new file mode 100644 index 000000000000..36f2dd5775b7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/q/QuandrixCommand.java @@ -0,0 +1,72 @@ +package mage.cards.q; + +import mage.abilities.Mode; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.common.TargetPlayerShufflesTargetCardsEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.filter.FilterSpell; +import mage.filter.predicate.Predicates; +import mage.target.TargetPlayer; +import mage.target.TargetSpell; +import mage.target.common.TargetCardInTargetPlayersGraveyard; +import mage.target.common.TargetCreatureOrPlaneswalker; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class QuandrixCommand extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("artifact or enchantment spell"); + + static { + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.ENCHANTMENT.getPredicate() + )); + } + + public QuandrixCommand(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}{U}"); + + // Choose two — + this.getSpellAbility().getModes().setMinModes(2); + this.getSpellAbility().getModes().setMaxModes(2); + + // • Return target creature or planeswalker to its owner's hand. + this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); + + // • Counter target artifact or enchantment spell. + Mode mode = new Mode(new CounterTargetEffect()); + mode.addTarget(new TargetSpell(filter)); + this.getSpellAbility().addMode(mode); + + // • Put two +1/+1 counters on target creature. + mode = new Mode(new AddCountersTargetEffect(CounterType.P1P1.createInstance(2))); + mode.addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addMode(mode); + + // • Target player shuffles up to three target cards from their graveyard into their library. + mode = new Mode(new TargetPlayerShufflesTargetCardsEffect()); + mode.addTarget(new TargetPlayer()); + mode.addTarget(new TargetCardInTargetPlayersGraveyard(3)); + this.getSpellAbility().addMode(mode); + } + + private QuandrixCommand(final QuandrixCommand card) { + super(card); + } + + @Override + public QuandrixCommand copy() { + return new QuandrixCommand(this); + } +} diff --git a/Mage.Sets/src/mage/cards/q/QuandrixCultivator.java b/Mage.Sets/src/mage/cards/q/QuandrixCultivator.java new file mode 100644 index 000000000000..bac765734968 --- /dev/null +++ b/Mage.Sets/src/mage/cards/q/QuandrixCultivator.java @@ -0,0 +1,54 @@ +package mage.cards.q; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class QuandrixCultivator extends CardImpl { + + private static final FilterCard filter = new FilterCard("a basic Forest or Island card"); + + static { + filter.add(SuperType.BASIC.getPredicate()); + filter.add(Predicates.or( + SubType.FOREST.getPredicate(), + SubType.ISLAND.getPredicate() + )); + } + + public QuandrixCultivator(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{G/U}{U}"); + + this.subtype.add(SubType.TURTLE); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // When Quandrix Cultivator enters the battlefield, you may search your library for a basic Forest or Island card, put it onto the battlefield, then shuffle. + this.addAbility(new EntersBattlefieldTriggeredAbility( + new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter)), true + )); + } + + private QuandrixCultivator(final QuandrixCultivator card) { + super(card); + } + + @Override + public QuandrixCultivator copy() { + return new QuandrixCultivator(this); + } +} diff --git a/Mage.Sets/src/mage/cards/q/QuandrixPledgemage.java b/Mage.Sets/src/mage/cards/q/QuandrixPledgemage.java new file mode 100644 index 000000000000..197fd530fe6d --- /dev/null +++ b/Mage.Sets/src/mage/cards/q/QuandrixPledgemage.java @@ -0,0 +1,39 @@ +package mage.cards.q; + +import mage.MageInt; +import mage.abilities.common.MagecraftAbility; +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 java.util.UUID; + +/** + * @author TheElk801 + */ +public final class QuandrixPledgemage extends CardImpl { + + public QuandrixPledgemage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G/U}{G/U}"); + + this.subtype.add(SubType.MERFOLK); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, put a +1/+1 counter on Quandrix Pledgemage. + this.addAbility(new MagecraftAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()))); + } + + private QuandrixPledgemage(final QuandrixPledgemage card) { + super(card); + } + + @Override + public QuandrixPledgemage copy() { + return new QuandrixPledgemage(this); + } +} diff --git a/Mage.Sets/src/mage/cards/q/QuestForPureFlame.java b/Mage.Sets/src/mage/cards/q/QuestForPureFlame.java index 56567bf9b365..d2891e216eb1 100644 --- a/Mage.Sets/src/mage/cards/q/QuestForPureFlame.java +++ b/Mage.Sets/src/mage/cards/q/QuestForPureFlame.java @@ -106,9 +106,8 @@ public QuestForPureFlameEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE - || event.getType() == GameEvent.EventType.DAMAGE_PLAYER - || event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER; + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT + || event.getType() == GameEvent.EventType.DAMAGE_PLAYER; } @Override diff --git a/Mage.Sets/src/mage/cards/q/QuestForTheHolyRelic.java b/Mage.Sets/src/mage/cards/q/QuestForTheHolyRelic.java index 77a24876f9c7..7b4ec91c3b23 100644 --- a/Mage.Sets/src/mage/cards/q/QuestForTheHolyRelic.java +++ b/Mage.Sets/src/mage/cards/q/QuestForTheHolyRelic.java @@ -66,7 +66,7 @@ class QuestForTheHolyRelicEffect extends OneShotEffect { QuestForTheHolyRelicEffect() { super(Outcome.PutCardInPlay); - this.staticText = "Search your library for an Equipment card, put it onto the battlefield, and attach it to a creature you control. Then shuffle your library"; + this.staticText = "Search your library for an Equipment card, put it onto the battlefield, attach it to a creature you control, then shuffle"; } private QuestForTheHolyRelicEffect(final QuestForTheHolyRelicEffect effect) { diff --git a/Mage.Sets/src/mage/cards/q/QuestForUlasTemple.java b/Mage.Sets/src/mage/cards/q/QuestForUlasTemple.java index 52bf7a08599c..c50d55078e16 100644 --- a/Mage.Sets/src/mage/cards/q/QuestForUlasTemple.java +++ b/Mage.Sets/src/mage/cards/q/QuestForUlasTemple.java @@ -79,7 +79,7 @@ public boolean apply(Game game, Ability source) { Cards cards = new CardsImpl(card); controller.lookAtCards(sourcePermanent.getName(), cards, game); if (card.isCreature()) { - if (controller.chooseUse(Outcome.DrawCard, "Do you wish to reveal the creature card at the top of the library?", source, game)) { + if (controller.chooseUse(Outcome.DrawCard, "Reveal the top card of your library?", source, game)) { controller.revealCards(sourcePermanent.getName(), cards, game); Permanent questForUlasTemple = game.getPermanent(source.getSourceId()); if (questForUlasTemple != null) { diff --git a/Mage.Sets/src/mage/cards/q/Quickling.java b/Mage.Sets/src/mage/cards/q/Quickling.java index e15b460da423..55dab052ad42 100644 --- a/Mage.Sets/src/mage/cards/q/Quickling.java +++ b/Mage.Sets/src/mage/cards/q/Quickling.java @@ -1,35 +1,27 @@ - package mage.cards.q; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.abilities.costs.common.ReturnToHandChosenControlledPermanentCost; +import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect; import mage.abilities.keyword.FlashAbility; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.TargetPermanent; +import mage.filter.StaticFilters; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class Quickling extends CardImpl { public Quickling(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.FAERIE); this.subtype.add(SubType.ROGUE); @@ -38,10 +30,16 @@ public Quickling(UUID ownerId, CardSetInfo setInfo) { // Flying this.addAbility(FlyingAbility.getInstance()); + // Flash this.addAbility(FlashAbility.getInstance()); + // When Quickling enters the battlefield, sacrifice it unless you return another creature you control to its owner's hand. - this.addAbility(new EntersBattlefieldTriggeredAbility(new QuicklingEffect())); + this.addAbility(new EntersBattlefieldTriggeredAbility(new SacrificeSourceUnlessPaysEffect( + new ReturnToHandChosenControlledPermanentCost( + new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE) + ) + ).setText("sacrifice it unless you return another creature you control to its owner's hand"))); } private Quickling(final Quickling card) { @@ -53,51 +51,3 @@ public Quickling copy() { return new Quickling(this); } } - -class QuicklingEffect extends OneShotEffect { - - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("another creature you control"); - private static final String effectText = "sacrifice it unless you return another creature you control to its owner's hand"; - - static { - filter.add(AnotherPredicate.instance); - } - - QuicklingEffect() { - super(Outcome.ReturnToHand); - staticText = effectText; - } - - QuicklingEffect(QuicklingEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - boolean targetChosen = false; - TargetPermanent target = new TargetPermanent(1, 1, filter, true); - if (target.canChoose(source.getSourceId(), controller.getId(), game) && controller.chooseUse(outcome, "Return another creature you control to its owner's hand?", source, game)) { - controller.chooseTarget(Outcome.ReturnToHand, target, source, game); - Permanent permanent = game.getPermanent(target.getFirstTarget()); - if (permanent != null) { - targetChosen = true; - permanent.moveToZone(Zone.HAND, source, game, false); - } - } - - if (!targetChosen) { - new SacrificeSourceEffect().apply(game, source); - } - return true; - } - return false; - } - - @Override - public QuicklingEffect copy() { - return new QuicklingEffect(this); - } - -} diff --git a/Mage.Sets/src/mage/cards/q/QuietSpeculation.java b/Mage.Sets/src/mage/cards/q/QuietSpeculation.java index bec1759d32c9..bffe12850f33 100644 --- a/Mage.Sets/src/mage/cards/q/QuietSpeculation.java +++ b/Mage.Sets/src/mage/cards/q/QuietSpeculation.java @@ -54,7 +54,7 @@ class SearchLibraryPutInGraveEffect extends SearchEffect { public SearchLibraryPutInGraveEffect(TargetCardInLibrary target) { super(target, Outcome.Neutral); - staticText = "Search target player's library for up to three cards with flashback and put them into that player's graveyard. Then the player shuffles their library."; + staticText = "Search target player's library for up to three cards with flashback and put them into that player's graveyard. Then the player shuffles."; } public SearchLibraryPutInGraveEffect(final SearchLibraryPutInGraveEffect effect) { diff --git a/Mage.Sets/src/mage/cards/q/QuillmaneBaku.java b/Mage.Sets/src/mage/cards/q/QuillmaneBaku.java index 3351ec79323e..ddf247111fe4 100644 --- a/Mage.Sets/src/mage/cards/q/QuillmaneBaku.java +++ b/Mage.Sets/src/mage/cards/q/QuillmaneBaku.java @@ -21,7 +21,7 @@ import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -74,8 +74,8 @@ public void adjustTargets(Ability ability, Game game) { } } ability.getTargets().clear(); - FilterCreaturePermanent newFilter = new FilterCreaturePermanent("creature with converted mana cost " + maxConvManaCost + " or less"); - newFilter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, maxConvManaCost + 1)); + FilterCreaturePermanent newFilter = new FilterCreaturePermanent("creature with mana value " + maxConvManaCost + " or less"); + newFilter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, maxConvManaCost + 1)); TargetCreaturePermanent target = new TargetCreaturePermanent(newFilter); ability.getTargets().add(target); } @@ -85,7 +85,7 @@ class QuillmaneBakuReturnEffect extends OneShotEffect { public QuillmaneBakuReturnEffect() { super(Outcome.ReturnToHand); - this.staticText = "Return target creature with converted mana cost X or less to its owner's hand"; + this.staticText = "Return target creature with mana value X or less to its owner's hand"; } public QuillmaneBakuReturnEffect(final QuillmaneBakuReturnEffect effect) { diff --git a/Mage.Sets/src/mage/cards/q/QuintoriusFieldHistorian.java b/Mage.Sets/src/mage/cards/q/QuintoriusFieldHistorian.java new file mode 100644 index 000000000000..2f1c9c83c6fd --- /dev/null +++ b/Mage.Sets/src/mage/cards/q/QuintoriusFieldHistorian.java @@ -0,0 +1,52 @@ +package mage.cards.q; + +import mage.MageInt; +import mage.abilities.common.CardsLeaveGraveyardTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.permanent.token.LoreholdToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class QuintoriusFieldHistorian extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.SPIRIT, "Spirits"); + + public QuintoriusFieldHistorian(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ELEPHANT); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Spirits you control get +1/+0. + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 1, 0, Duration.WhileOnBattlefield, filter + ))); + + // Whenever one or more cards leave your graveyard, create a 3/2 red and white Spirit creature token. + this.addAbility(new CardsLeaveGraveyardTriggeredAbility(new CreateTokenEffect(new LoreholdToken()))); + } + + private QuintoriusFieldHistorian(final QuintoriusFieldHistorian card) { + super(card); + } + + @Override + public QuintoriusFieldHistorian copy() { + return new QuintoriusFieldHistorian(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RadhaHeartOfKeld.java b/Mage.Sets/src/mage/cards/r/RadhaHeartOfKeld.java index adaecbd2615d..912e8e00233b 100644 --- a/Mage.Sets/src/mage/cards/r/RadhaHeartOfKeld.java +++ b/Mage.Sets/src/mage/cards/r/RadhaHeartOfKeld.java @@ -31,7 +31,7 @@ */ public final class RadhaHeartOfKeld extends CardImpl { - private static final FilterCard filter = new FilterLandCard("play land cards"); + private static final FilterCard filter = new FilterLandCard("play lands"); public RadhaHeartOfKeld(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{G}"); @@ -50,9 +50,9 @@ public RadhaHeartOfKeld(UUID ownerId, CardSetInfo setInfo) { // You may look at the top card of your library any time, and you may play lands from the top of your library. LookAtTopCardOfLibraryAnyTimeEffect lookEffect = new LookAtTopCardOfLibraryAnyTimeEffect(); - lookEffect.overrideRuleText("You may look at the top card of your library any time"); - PlayTheTopCardEffect playEffect = new PlayTheTopCardEffect(filter); - playEffect.overrideRuleText(", and you may play lands from the top of your library"); + lookEffect.setText("You may look at the top card of your library any time"); + PlayTheTopCardEffect playEffect = new PlayTheTopCardEffect(filter, false); + playEffect.setText(", and you may play lands from the top of your library"); SimpleStaticAbility lookAndPlayAbility = new SimpleStaticAbility(lookEffect); lookAndPlayAbility.addEffect(playEffect); @@ -61,7 +61,7 @@ public RadhaHeartOfKeld(UUID ownerId, CardSetInfo setInfo) { // 4RG: Radha gets +X/+X until end of turn, where X is the number of lands you control. DynamicValue controlledLands = new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_PERMANENT_LANDS); BoostSourceEffect bse = new BoostSourceEffect(controlledLands, controlledLands, Duration.EndOfTurn, true); - bse.overrideRuleText("Radha gets +X/+X until end of turn, where X is the number of lands you control"); + bse.setText("Radha gets +X/+X until end of turn, where X is the number of lands you control"); this.addAbility(new SimpleActivatedAbility(bse, new ManaCostsImpl("{4}{R}{G}"))); } diff --git a/Mage.Sets/src/mage/cards/r/RadiantArchangel.java b/Mage.Sets/src/mage/cards/r/RadiantArchangel.java index a40a07027e8f..0ae4877f4bf0 100644 --- a/Mage.Sets/src/mage/cards/r/RadiantArchangel.java +++ b/Mage.Sets/src/mage/cards/r/RadiantArchangel.java @@ -18,7 +18,7 @@ import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/r/RadiantPerformer.java b/Mage.Sets/src/mage/cards/r/RadiantPerformer.java new file mode 100644 index 000000000000..5321c85ef88e --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RadiantPerformer.java @@ -0,0 +1,157 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.CopySpellForEachItCouldTargetEffect; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterStackObject; +import mage.filter.StaticFilters; +import mage.filter.predicate.Predicate; +import mage.filter.predicate.mageobject.MageObjectReferencePredicate; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.game.stack.StackAbility; +import mage.game.stack.StackObject; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetStackObject; +import mage.watchers.common.CastFromHandWatcher; + +import java.util.*; + +/** + * @author TheElk801 + */ +public final class RadiantPerformer extends CardImpl { + + private static final FilterStackObject filter + = new FilterStackObject("spell or ability that targets only a single permanent or player"); + + static { + filter.add(RadiantPerformerPredicate.instance); + } + + public RadiantPerformer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // When Radiant Performer enters the battlefield, if you cast it from your hand, choose target spell or ability that targets only a single permanent or player. Copy that spell or ability for each other permanent or player the spell or ability could target. Each copy targets a different one of those permanents and players. + Ability ability = new ConditionalInterveningIfTriggeredAbility( + new EntersBattlefieldTriggeredAbility(new RadiantPerformerEffect()), + CastFromHandSourcePermanentCondition.instance, "When {this} enters the battlefield, " + + "if you cast it from your hand, choose target spell or ability that targets only " + + "a single permanent or player. Copy that spell or ability for each other permanent or player " + + "the spell or ability could target. Each copy targets a different one of those permanents and players." + ); + ability.addTarget(new TargetStackObject(filter)); + this.addAbility(ability, new CastFromHandWatcher()); + } + + private RadiantPerformer(final RadiantPerformer card) { + super(card); + } + + @Override + public RadiantPerformer copy() { + return new RadiantPerformer(this); + } +} + +enum RadiantPerformerPredicate implements Predicate { + instance; + + @Override + public boolean apply(StackObject input, Game game) { + Ability ability = input instanceof Spell ? ((Spell) input).getSpellAbility() : (StackAbility) input; + return ability != null + && ability + .getModes() + .values() + .stream() + .map(Mode::getTargets) + .flatMap(Collection::stream) + .map(Target::getTargets) + .flatMap(Collection::stream) + .distinct() + .mapToInt(uuid -> game.getPermanent(uuid) == null && game.getPlayer(uuid) == null ? 2 : 1) + .sum() == 1; + } +} + +class RadiantPerformerEffect extends CopySpellForEachItCouldTargetEffect { + + RadiantPerformerEffect() { + super(); + } + + private RadiantPerformerEffect(final RadiantPerformerEffect effect) { + super(effect); + } + + @Override + protected StackObject getStackObject(Game game, Ability source) { + return game.getStack().getStackObject(source.getFirstTarget()); + } + + @Override + protected Player getPlayer(Game game, Ability source) { + return game.getPlayer(source.getControllerId()); + } + + @Override + protected List getPossibleTargets(StackObject stackObject, Player player, Ability source, Game game) { + List predicates = new ArrayList<>(); + Ability ability = stackObject instanceof Spell ? ((Spell) stackObject).getSpellAbility() : (StackAbility) stackObject; + UUID targeted = ability == null ? null : ability + .getModes() + .values() + .stream() + .map(Mode::getTargets) + .flatMap(Collection::stream) + .map(Target::getTargets) + .flatMap(Collection::stream) + .filter(Objects::nonNull) + .findAny() + .orElse(null); + game.getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_PERMANENT, player.getId(), source.getSourceId(), game + ).stream() + .filter(Objects::nonNull) + .filter(p -> !p.equals(game.getPermanent(targeted))) + .filter(p -> stackObject.canTarget(game, p.getId())) + .map(p -> new MageObjectReference(p, game)) + .map(MageObjectReferencePredicate::new) + .forEach(predicates::add); + game.getState() + .getPlayersInRange(source.getControllerId(), game) + .stream() + .filter(uuid -> !uuid.equals(targeted)) + .filter(uuid -> stackObject.canTarget(game, uuid)) + .map(MageObjectReference::new) + .map(MageObjectReferencePredicate::new) + .forEach(predicates::add); + return predicates; + } + + @Override + public RadiantPerformerEffect copy() { + return new RadiantPerformerEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RadiantScrollwielder.java b/Mage.Sets/src/mage/cards/r/RadiantScrollwielder.java new file mode 100644 index 000000000000..bd2a6a217e2c --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RadiantScrollwielder.java @@ -0,0 +1,193 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.GainAbilitySpellsEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterObject; +import mage.filter.StaticFilters; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RadiantScrollwielder extends CardImpl { + + private static final FilterObject filter = new FilterObject("instant and sorcery spells you control"); + + static { + filter.add(Predicates.or( + CardType.INSTANT.getPredicate(), + CardType.SORCERY.getPredicate() + )); + } + + public RadiantScrollwielder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{W}"); + + this.subtype.add(SubType.DWARF); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Instant and sorcery spells you control have lifelink. + this.addAbility(new SimpleStaticAbility(new GainAbilitySpellsEffect( + LifelinkAbility.getInstance(), filter + ).setText("instant and sorcery spells you control have lifelink"))); + + // At the beginning of your upkeep, exile an instant or sorcery card at random from your graveyard. You may cast it this turn. If a spell cast this way would be put into your graveyard, exile it instead. + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new RadiantScrollwielderEffect(), TargetController.YOU, false + ), new RadiantScrollwielderWatcher()); + } + + private RadiantScrollwielder(final RadiantScrollwielder card) { + super(card); + } + + @Override + public RadiantScrollwielder copy() { + return new RadiantScrollwielder(this); + } +} + +class RadiantScrollwielderEffect extends OneShotEffect { + + RadiantScrollwielderEffect() { + super(Outcome.Benefit); + staticText = "exile an instant or sorcery card at random from your graveyard. You may cast it this turn. " + + "If a spell cast this way would be put into your graveyard, exile it instead"; + } + + private RadiantScrollwielderEffect(final RadiantScrollwielderEffect effect) { + super(effect); + } + + @Override + public RadiantScrollwielderEffect copy() { + return new RadiantScrollwielderEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null || player.getGraveyard().count(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, game) < 1) { + return false; + } + TargetCardInYourGraveyard target = new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY); + target.setNotTarget(true); + target.setRandom(true); + player.chooseTarget(outcome, target, source, game); + Card card = game.getCard(target.getFirstTarget()); + if (card == null) { + return false; + } + PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile( + game, source, card, TargetController.YOU, + Duration.EndOfTurn, false, true + ); + game.addEffect(new RadiantScrollwielderReplacementEffect(card, game), source); + return true; + } +} + +class RadiantScrollwielderReplacementEffect extends ReplacementEffectImpl { + + private final MageObjectReference mor; + + RadiantScrollwielderReplacementEffect(Card card, Game game) { + super(Duration.EndOfTurn, Outcome.Exile); + this.mor = new MageObjectReference(card, game, 1); + } + + private RadiantScrollwielderReplacementEffect(final RadiantScrollwielderReplacementEffect effect) { + super(effect); + this.mor = effect.mor; + } + + @Override + public RadiantScrollwielderReplacementEffect copy() { + return new RadiantScrollwielderReplacementEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + ((ZoneChangeEvent) event).setToZone(Zone.EXILED); + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return ((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD + && mor.refersTo(game.getCard(event.getSourceId()), game) + && RadiantScrollwielderWatcher.checkSpell(game.getCard(event.getSourceId()), source, game); + } +} + +class RadiantScrollwielderWatcher extends Watcher { + + private final Map morMap = new HashMap<>(); + + RadiantScrollwielderWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.SPELL_CAST || event.getAdditionalReference() == null) { + return; + } + morMap.put( + new MageObjectReference(event.getSourceId(), game), + event.getAdditionalReference().getApprovingMageObjectReference() + ); + } + + @Override + public void reset() { + super.reset(); + this.morMap.clear(); + } + + static boolean checkSpell(Card card, Ability source, Game game) { + if (card == null) { + return false; + } + RadiantScrollwielderWatcher watcher = game.getState().getWatcher(RadiantScrollwielderWatcher.class); + if (watcher == null) { + return false; + } + MageObjectReference mor = watcher.morMap.getOrDefault(new MageObjectReference(card, game), null); + return mor != null && mor.refersTo(source.getSourceObject(game), game); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RadiantSerraArchangel.java b/Mage.Sets/src/mage/cards/r/RadiantSerraArchangel.java index c3e4f6850237..1731130be231 100644 --- a/Mage.Sets/src/mage/cards/r/RadiantSerraArchangel.java +++ b/Mage.Sets/src/mage/cards/r/RadiantSerraArchangel.java @@ -16,7 +16,7 @@ import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TappedPredicate; import mage.target.common.TargetControlledPermanent; diff --git a/Mage.Sets/src/mage/cards/r/Radiate.java b/Mage.Sets/src/mage/cards/r/Radiate.java index 7beff0b1054a..f12b71b592f5 100644 --- a/Mage.Sets/src/mage/cards/r/Radiate.java +++ b/Mage.Sets/src/mage/cards/r/Radiate.java @@ -1,20 +1,18 @@ - package mage.cards.r; -import java.util.UUID; -import mage.MageItem; +import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.Mode; +import mage.abilities.AbilityImpl; import mage.abilities.effects.common.CopySpellForEachItCouldTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterInPlay; import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import mage.filter.common.FilterInstantOrSorcerySpell; -import mage.filter.common.FilterPermanentOrPlayer; import mage.filter.predicate.ObjectPlayer; import mage.filter.predicate.ObjectPlayerPredicate; +import mage.filter.predicate.mageobject.MageObjectReferencePredicate; import mage.game.Game; import mage.game.stack.Spell; import mage.game.stack.StackObject; @@ -23,21 +21,24 @@ import mage.target.TargetSpell; import mage.util.TargetAddress; +import java.util.*; + /** * @author duncant */ public final class Radiate extends CardImpl { - protected static final FilterSpell filter = new FilterInstantOrSorcerySpell(); + protected static final FilterSpell filter = new FilterInstantOrSorcerySpell( + "instant or sorcery spell that targets only a single permanent or player" + ); static { - filter.add(new SpellWithOnlySingleTargetPredicate()); - filter.add(new SpellWithOnlyPermanentOrPlayerTargetsPredicate()); - filter.setMessage("instant or sorcery spell that targets only a single permanent or player"); + filter.add(SpellWithOnlySingleTargetPredicate.instance); + filter.add(SpellWithOnlyPermanentOrPlayerTargetsPredicate.instance); } public Radiate(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{R}{R}"); // Choose target instant or sorcery spell that targets only a single permanent or player. Copy that spell for each other permanent or player the spell could target. Each copy targets a different one of those permanents and players. this.getSpellAbility().addEffect(new RadiateEffect()); @@ -54,7 +55,8 @@ public Radiate copy() { } } -class SpellWithOnlySingleTargetPredicate implements ObjectPlayerPredicate> { +enum SpellWithOnlySingleTargetPredicate implements ObjectPlayerPredicate> { + instance; @Override public boolean apply(ObjectPlayer input, Game game) { @@ -77,7 +79,8 @@ public boolean apply(ObjectPlayer input, Game game) { } } -class SpellWithOnlyPermanentOrPlayerTargetsPredicate implements ObjectPlayerPredicate> { +enum SpellWithOnlyPermanentOrPlayerTargetsPredicate implements ObjectPlayerPredicate> { + instance; @Override public boolean apply(ObjectPlayer input, Game game) { @@ -98,47 +101,61 @@ public boolean apply(ObjectPlayer input, Game game) { } } -class RadiateEffect extends CopySpellForEachItCouldTargetEffect { +class RadiateEffect extends CopySpellForEachItCouldTargetEffect { - public RadiateEffect() { - this(new FilterPermanentOrPlayer()); + RadiateEffect() { + super(); + staticText = "Choose target instant or sorcery spell that targets only a single permanent or player. " + + "Copy that spell for each other permanent or player the spell could target. " + + "Each copy targets a different one of those permanents and players."; } - public RadiateEffect(RadiateEffect effect) { + private RadiateEffect(RadiateEffect effect) { super(effect); } - private RadiateEffect(FilterInPlay filter) { - super(filter); - } - - @Override - public String getText(Mode mode) { - return "Choose target instant or sorcery spell that targets only a single permanent or player. Copy that spell for each other permanent or player the spell could target. Each copy targets a different one of those permanents and players."; - } - @Override protected Player getPlayer(Game game, Ability source) { return game.getPlayer(source.getControllerId()); } @Override - protected Spell getSpell(Game game, Ability source) { - StackObject ret = game.getStack().getStackObject(targetPointer.getFirst(game, source)); - if (ret instanceof Spell) { - return (Spell) ret; - } - return null; - } - - @Override - protected boolean changeTarget(Target target, Game game, Ability source) { - return true; + protected List getPossibleTargets(StackObject stackObject, Player player, Ability source, Game game) { + List predicates = new ArrayList<>(); + UUID targeted = ((Spell) stackObject) + .getSpellAbilities() + .stream() + .map(AbilityImpl::getTargets) + .flatMap(Collection::stream) + .map(Target::getTargets) + .flatMap(Collection::stream) + .filter(Objects::nonNull) + .findAny() + .orElse(null); + game.getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_PERMANENT, player.getId(), source.getSourceId(), game + ).stream() + .filter(Objects::nonNull) + .filter(p -> !p.equals(game.getPermanent(targeted))) + .filter(p -> stackObject.canTarget(game, p.getId())) + .map(p -> new MageObjectReference(p, game)) + .map(MageObjectReferencePredicate::new) + .forEach(predicates::add); + game.getState() + .getPlayersInRange(source.getControllerId(), game) + .stream() + .filter(uuid -> !uuid.equals(targeted)) + .filter(uuid -> stackObject.canTarget(game, uuid)) + .map(MageObjectReference::new) + .map(MageObjectReferencePredicate::new) + .forEach(predicates::add); + return predicates; } @Override - protected void modifyCopy(Spell copy, Game game, Ability source) { - copy.setControllerId(source.getControllerId()); + protected Spell getStackObject(Game game, Ability source) { + return game.getSpell(source.getFirstTarget()); } @Override diff --git a/Mage.Sets/src/mage/cards/r/RageExtractor.java b/Mage.Sets/src/mage/cards/r/RageExtractor.java index 365b2b5c6578..9c4d203be7d3 100644 --- a/Mage.Sets/src/mage/cards/r/RageExtractor.java +++ b/Mage.Sets/src/mage/cards/r/RageExtractor.java @@ -13,7 +13,6 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.stack.Spell; import mage.target.common.TargetAnyTarget; @@ -66,7 +65,7 @@ public boolean checkTrigger(GameEvent event, Game game) { if (spell != null) { for (ManaCost cost : spell.getCard().getManaCost()) { if (cost instanceof PhyrexianManaCost) { - ((DamageTargetEffect)getEffects().get(0)).setAmount(StaticValue.get(spell.getConvertedManaCost())); + ((DamageTargetEffect)getEffects().get(0)).setAmount(StaticValue.get(spell.getManaValue())); return true; } } @@ -77,6 +76,6 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { - return "Whenever you cast a spell with p in its mana cost, {this} deals damage equal to that spell's converted mana cost to any target."; + return "Whenever you cast a spell with {P} in its mana cost, {this} deals damage equal to that spell's mana value to any target."; } } diff --git a/Mage.Sets/src/mage/cards/r/RageForger.java b/Mage.Sets/src/mage/cards/r/RageForger.java index 3fcadde5c8e2..80e044028a53 100644 --- a/Mage.Sets/src/mage/cards/r/RageForger.java +++ b/Mage.Sets/src/mage/cards/r/RageForger.java @@ -17,7 +17,7 @@ import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/r/RagingSwordtooth.java b/Mage.Sets/src/mage/cards/r/RagingSwordtooth.java index cb25c77bc6da..720eb803f19f 100644 --- a/Mage.Sets/src/mage/cards/r/RagingSwordtooth.java +++ b/Mage.Sets/src/mage/cards/r/RagingSwordtooth.java @@ -11,7 +11,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/r/RakshasaDebaser.java b/Mage.Sets/src/mage/cards/r/RakshasaDebaser.java index 011c5f8f7094..6d0a11e603c8 100644 --- a/Mage.Sets/src/mage/cards/r/RakshasaDebaser.java +++ b/Mage.Sets/src/mage/cards/r/RakshasaDebaser.java @@ -12,7 +12,7 @@ import mage.constants.SubType; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.permanent.DefendingPlayerOwnsCardPredicate; +import mage.filter.predicate.card.DefendingPlayerOwnsCardPredicate; import mage.target.common.TargetCardInGraveyard; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/r/RalIzzetViceroy.java b/Mage.Sets/src/mage/cards/r/RalIzzetViceroy.java index 5a868270bd85..afd6b54e8119 100644 --- a/Mage.Sets/src/mage/cards/r/RalIzzetViceroy.java +++ b/Mage.Sets/src/mage/cards/r/RalIzzetViceroy.java @@ -1,6 +1,5 @@ package mage.cards.r; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; @@ -9,22 +8,29 @@ import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.GetEmblemEffect; import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; -import mage.constants.SubType; -import mage.constants.SuperType; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.command.emblems.RalIzzetViceroyEmblem; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class RalIzzetViceroy extends CardImpl { + private static final Hint hint = new ValueHint( + "Instant and sorcery cards in your exile and graveyard", InstantSorceryExileGraveyardCount.instance + ); + public RalIzzetViceroy(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{3}{U}{R}"); @@ -47,7 +53,7 @@ public RalIzzetViceroy(UUID ownerId, CardSetInfo setInfo) { + "the total number of instant and sorcery cards " + "you own in exile and in your graveyard"), -3); ability.addTarget(new TargetCreaturePermanent()); - this.addAbility(ability); + this.addAbility(ability.addHint(hint)); // -8: You get an emblem with "Whenever you cast an instant or sorcery spell, this emblem deals 4 damage to any target and you draw two cards." this.addAbility(new LoyaltyAbility( diff --git a/Mage.Sets/src/mage/cards/r/RalStormConduit.java b/Mage.Sets/src/mage/cards/r/RalStormConduit.java index ca9a9e29113e..679774d6a889 100644 --- a/Mage.Sets/src/mage/cards/r/RalStormConduit.java +++ b/Mage.Sets/src/mage/cards/r/RalStormConduit.java @@ -120,7 +120,7 @@ public boolean checkEventType(GameEvent event, Game game) { public boolean checkTrigger(GameEvent event, Game game) { if (event.getPlayerId().equals(this.getControllerId())) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && (spell.isInstant() || spell.isSorcery())) { + if (spell != null && spell.isInstantOrSorcery()) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getTargetId())); } diff --git a/Mage.Sets/src/mage/cards/r/RalZarek.java b/Mage.Sets/src/mage/cards/r/RalZarek.java index f3a3e887780c..edfebbc8438a 100644 --- a/Mage.Sets/src/mage/cards/r/RalZarek.java +++ b/Mage.Sets/src/mage/cards/r/RalZarek.java @@ -17,7 +17,7 @@ import mage.constants.Outcome; import mage.constants.SuperType; import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.turn.TurnMod; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/r/RallyTheAncestors.java b/Mage.Sets/src/mage/cards/r/RallyTheAncestors.java index 8a880a9e044d..a2880193badb 100644 --- a/Mage.Sets/src/mage/cards/r/RallyTheAncestors.java +++ b/Mage.Sets/src/mage/cards/r/RallyTheAncestors.java @@ -16,7 +16,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -39,7 +39,7 @@ public RallyTheAncestors(UUID ownerId, CardSetInfo setInfo) { // Return each creature card with converted mana cost X or less from your graveyard to the battlefield. // Exile those creatures at the beginning of your next upkeep. Exile Rally the Ancestors. this.getSpellAbility().addEffect(new RallyTheAncestorsEffect()); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private RallyTheAncestors(final RallyTheAncestors card) { @@ -56,7 +56,7 @@ class RallyTheAncestorsEffect extends OneShotEffect { RallyTheAncestorsEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "Return each creature card with converted mana cost X or less from your graveyard to the battlefield. Exile those creatures at the beginning of your next upkeep"; + this.staticText = "Return each creature card with mana value X or less from your graveyard to the battlefield. Exile those creatures at the beginning of your next upkeep"; } RallyTheAncestorsEffect(final RallyTheAncestorsEffect effect) { @@ -74,7 +74,7 @@ public boolean apply(Game game, Ability source) { if (player != null) { int xValue = source.getManaCostsToPay().getX(); FilterCreatureCard filter = new FilterCreatureCard(); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, xValue + 1)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); Set cards = player.getGraveyard().getCards(filter, game); player.moveCards(cards, Zone.BATTLEFIELD, source, game); List toExile = new ArrayList<>(cards.size()); diff --git a/Mage.Sets/src/mage/cards/r/RamThrough.java b/Mage.Sets/src/mage/cards/r/RamThrough.java index ee1eddbab7c2..0dbccc0a8421 100644 --- a/Mage.Sets/src/mage/cards/r/RamThrough.java +++ b/Mage.Sets/src/mage/cards/r/RamThrough.java @@ -2,7 +2,6 @@ import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.abilities.keyword.DeathtouchAbility; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -17,8 +16,6 @@ import java.util.UUID; -import static mage.game.combat.CombatGroup.getLethalDamage; - /** * @author TheElk801 */ @@ -79,10 +76,7 @@ public boolean apply(Game game, Ability source) { if (!myPermanent.getAbilities().containsKey(TrampleAbility.getInstance().getId())) { return anotherPermanent.damage(power, myPermanent.getId(), source, game, false, true) > 0; } - int lethal = getLethalDamage(anotherPermanent, game); - if (myPermanent.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) { - lethal = Math.min(lethal, 1); - } + int lethal = anotherPermanent.getLethalDamage(myPermanent.getId(), game); lethal = Math.min(lethal, power); anotherPermanent.damage(lethal, myPermanent.getId(), source, game); Player player = game.getPlayer(anotherPermanent.getControllerId()); diff --git a/Mage.Sets/src/mage/cards/r/RamosDragonEngine.java b/Mage.Sets/src/mage/cards/r/RamosDragonEngine.java index e5e8bdcb760e..e2d1afc36017 100644 --- a/Mage.Sets/src/mage/cards/r/RamosDragonEngine.java +++ b/Mage.Sets/src/mage/cards/r/RamosDragonEngine.java @@ -20,6 +20,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.Spell; @@ -42,7 +43,7 @@ public RamosDragonEngine(UUID ownerId, CardSetInfo setInfo) { // Flying this.addAbility(FlyingAbility.getInstance()); // Whenever you cast a spell, put a +1/+1 counter on Ramos, Dragon Engine for each of that spell's colors. - this.addAbility(new SpellCastControllerTriggeredAbility(new RamosDragonEngineAddCountersEffect(), new FilterSpell("a spell"), false, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new RamosDragonEngineAddCountersEffect(), StaticFilters.FILTER_SPELL_A, false, true)); // Remove five +1/+1 counters from Ramos: Add {W}{W}{U}{U}{B}{B}{R}{R}{G}{G}. Activate this ability only once each turn. Ability ability = new ActivateOncePerTurnManaAbility(Zone.BATTLEFIELD, new BasicManaEffect(new Mana(2, 2, 2, 2, 2, 0, 0, 0)), new RemoveCountersSourceCost(CounterType.P1P1.createInstance(5))); diff --git a/Mage.Sets/src/mage/cards/r/RamosianCaptain.java b/Mage.Sets/src/mage/cards/r/RamosianCaptain.java index 4b870e2839f2..ab8a8957fb9f 100644 --- a/Mage.Sets/src/mage/cards/r/RamosianCaptain.java +++ b/Mage.Sets/src/mage/cards/r/RamosianCaptain.java @@ -17,7 +17,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; /** @@ -26,11 +26,11 @@ */ public final class RamosianCaptain extends CardImpl { - private static final FilterPermanentCard filter = new FilterPermanentCard("Rebel permanent card with converted mana cost 4 or less"); + private static final FilterPermanentCard filter = new FilterPermanentCard("Rebel permanent card with mana value 4 or less"); static { filter.add(SubType.REBEL.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 5)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 5)); } public RamosianCaptain(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/r/RamosianCommander.java b/Mage.Sets/src/mage/cards/r/RamosianCommander.java index c18585e7770f..e4aedc29a538 100644 --- a/Mage.Sets/src/mage/cards/r/RamosianCommander.java +++ b/Mage.Sets/src/mage/cards/r/RamosianCommander.java @@ -16,7 +16,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; /** @@ -25,11 +25,11 @@ */ public final class RamosianCommander extends CardImpl { - private static final FilterPermanentCard filter = new FilterPermanentCard("Rebel permanent card with converted mana cost 5 or less"); + private static final FilterPermanentCard filter = new FilterPermanentCard("Rebel permanent card with mana value 5 or less"); static { filter.add(SubType.REBEL.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 6)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 6)); } public RamosianCommander(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/r/RamosianLieutenant.java b/Mage.Sets/src/mage/cards/r/RamosianLieutenant.java index 24df05f87b8d..c61ecd3474a6 100644 --- a/Mage.Sets/src/mage/cards/r/RamosianLieutenant.java +++ b/Mage.Sets/src/mage/cards/r/RamosianLieutenant.java @@ -16,7 +16,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; /** @@ -25,11 +25,11 @@ */ public final class RamosianLieutenant extends CardImpl { - private static final FilterPermanentCard filter = new FilterPermanentCard("Rebel permanent card with converted mana cost 3 or less"); + private static final FilterPermanentCard filter = new FilterPermanentCard("Rebel permanent card with mana value 3 or less"); static { filter.add(SubType.REBEL.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public RamosianLieutenant(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/r/RamosianRevivalist.java b/Mage.Sets/src/mage/cards/r/RamosianRevivalist.java index 51ddfddc55a0..bfda4bedcbc7 100644 --- a/Mage.Sets/src/mage/cards/r/RamosianRevivalist.java +++ b/Mage.Sets/src/mage/cards/r/RamosianRevivalist.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInYourGraveyard; /** @@ -24,11 +24,11 @@ */ public final class RamosianRevivalist extends CardImpl { - private static final FilterPermanentCard filter = new FilterPermanentCard("Rebel permanent card with converted mana cost 5 or less from your graveyard"); + private static final FilterPermanentCard filter = new FilterPermanentCard("Rebel permanent card with mana value 5 or less from your graveyard"); static { filter.add(SubType.REBEL.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 6)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 6)); } public RamosianRevivalist(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/r/RamosianSergeant.java b/Mage.Sets/src/mage/cards/r/RamosianSergeant.java index 2429bbd3b2a3..09d3e449dec0 100644 --- a/Mage.Sets/src/mage/cards/r/RamosianSergeant.java +++ b/Mage.Sets/src/mage/cards/r/RamosianSergeant.java @@ -16,7 +16,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; /** @@ -24,11 +24,11 @@ */ public final class RamosianSergeant extends CardImpl { - private static final FilterPermanentCard filter = new FilterPermanentCard("Rebel permanent card with converted mana cost 2 or less"); + private static final FilterPermanentCard filter = new FilterPermanentCard("Rebel permanent card with mana value 2 or less"); static { filter.add(SubType.REBEL.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); } public RamosianSergeant(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/r/RamosianSkyMarshal.java b/Mage.Sets/src/mage/cards/r/RamosianSkyMarshal.java index 0e4dceff84ab..8031972d07ec 100644 --- a/Mage.Sets/src/mage/cards/r/RamosianSkyMarshal.java +++ b/Mage.Sets/src/mage/cards/r/RamosianSkyMarshal.java @@ -16,7 +16,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; /** @@ -25,11 +25,11 @@ */ public final class RamosianSkyMarshal extends CardImpl { - private static final FilterPermanentCard filter = new FilterPermanentCard("Rebel permanent card with converted mana cost 6 or less"); + private static final FilterPermanentCard filter = new FilterPermanentCard("Rebel permanent card with mana value 6 or less"); static { filter.add(SubType.REBEL.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 7)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 7)); } public RamosianSkyMarshal(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/r/RampagingFerocidon.java b/Mage.Sets/src/mage/cards/r/RampagingFerocidon.java index 97b700131dbd..1704036e3e96 100644 --- a/Mage.Sets/src/mage/cards/r/RampagingFerocidon.java +++ b/Mage.Sets/src/mage/cards/r/RampagingFerocidon.java @@ -20,7 +20,7 @@ import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/r/RampagingWerewolf.java b/Mage.Sets/src/mage/cards/r/RampagingWerewolf.java index 0ceff6db82ec..93cbf1d62ad4 100644 --- a/Mage.Sets/src/mage/cards/r/RampagingWerewolf.java +++ b/Mage.Sets/src/mage/cards/r/RampagingWerewolf.java @@ -1,19 +1,13 @@ - package mage.cards.r; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; -import mage.abilities.keyword.TransformAbility; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** * @author nantuko @@ -21,7 +15,7 @@ public final class RampagingWerewolf extends CardImpl { public RampagingWerewolf(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.WEREWOLF); this.color.setRed(true); @@ -33,9 +27,7 @@ public RampagingWerewolf(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(4); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Rampaging Werewolf. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); - + this.addAbility(new WerewolfBackTriggeredAbility()); } private RampagingWerewolf(final RampagingWerewolf card) { diff --git a/Mage.Sets/src/mage/cards/r/Ramroller.java b/Mage.Sets/src/mage/cards/r/Ramroller.java index 2797b6ecb771..5aa1b8b6cd7f 100644 --- a/Mage.Sets/src/mage/cards/r/Ramroller.java +++ b/Mage.Sets/src/mage/cards/r/Ramroller.java @@ -15,7 +15,7 @@ import mage.constants.Duration; import mage.constants.Zone; import mage.filter.common.FilterControlledArtifactPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/r/RamunapExcavator.java b/Mage.Sets/src/mage/cards/r/RamunapExcavator.java index 2de1ddf71e16..26096ba53c5b 100644 --- a/Mage.Sets/src/mage/cards/r/RamunapExcavator.java +++ b/Mage.Sets/src/mage/cards/r/RamunapExcavator.java @@ -24,7 +24,7 @@ public RamunapExcavator(UUID ownerId, CardSetInfo setInfo) { this.power = new MageInt(2); this.toughness = new MageInt(3); - // You may play land cards from your graveyard. + // You may play lands from your graveyard. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayLandsFromGraveyardControllerEffect())); } diff --git a/Mage.Sets/src/mage/cards/r/RanarTheEverWatchful.java b/Mage.Sets/src/mage/cards/r/RanarTheEverWatchful.java index 292e46a11055..792e5a376d6f 100644 --- a/Mage.Sets/src/mage/cards/r/RanarTheEverWatchful.java +++ b/Mage.Sets/src/mage/cards/r/RanarTheEverWatchful.java @@ -121,6 +121,7 @@ public boolean checkTrigger(GameEvent event, Game game) { if (zEvent != null && Zone.HAND == zEvent.getFromZone() && Zone.EXILED == zEvent.getToZone() + && zEvent.getPlayerId() == controllerId && zEvent.getCards() != null) { for (Card card : zEvent.getCards()) { if (card != null) { @@ -135,6 +136,7 @@ public boolean checkTrigger(GameEvent event, Game game) { if (zEvent != null && Zone.BATTLEFIELD == zEvent.getFromZone() && Zone.EXILED == zEvent.getToZone() + && zEvent.getPlayerId() == controllerId && zEvent.getCards() != null) { return true; diff --git a/Mage.Sets/src/mage/cards/r/RangerCaptainOfEos.java b/Mage.Sets/src/mage/cards/r/RangerCaptainOfEos.java index a124319640d3..60736bcc15cb 100644 --- a/Mage.Sets/src/mage/cards/r/RangerCaptainOfEos.java +++ b/Mage.Sets/src/mage/cards/r/RangerCaptainOfEos.java @@ -14,7 +14,7 @@ import mage.constants.*; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; @@ -28,10 +28,10 @@ public final class RangerCaptainOfEos extends CardImpl { private static final FilterCard filter - = new FilterCreatureCard("a creature card with converted mana cost 1 or less"); + = new FilterCreatureCard("a creature card with mana value 1 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 2)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 2)); } public RangerCaptainOfEos(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/r/RangerOfEos.java b/Mage.Sets/src/mage/cards/r/RangerOfEos.java index 276a189738ec..c1cf9ebc0c8e 100644 --- a/Mage.Sets/src/mage/cards/r/RangerOfEos.java +++ b/Mage.Sets/src/mage/cards/r/RangerOfEos.java @@ -12,7 +12,7 @@ import mage.constants.SubType; import mage.constants.ComparisonType; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; /** @@ -21,11 +21,11 @@ */ public final class RangerOfEos extends CardImpl { - private static final FilterCard filter = new FilterCard("creature cards with converted mana cost 1 or less"); + private static final FilterCard filter = new FilterCard("creature cards with mana value 1 or less"); static { filter.add(CardType.CREATURE.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 2)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 2)); } public RangerOfEos(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/r/RashmiEternitiesCrafter.java b/Mage.Sets/src/mage/cards/r/RashmiEternitiesCrafter.java index 3a27e87027db..4e99b475a9ad 100644 --- a/Mage.Sets/src/mage/cards/r/RashmiEternitiesCrafter.java +++ b/Mage.Sets/src/mage/cards/r/RashmiEternitiesCrafter.java @@ -78,7 +78,7 @@ public boolean checkTrigger(GameEvent event, Game game) { Spell spell = game.getStack().getSpell(event.getTargetId()); if (spell != null) { for (Effect effect : getEffects()) { - effect.setValue("RashmiEternitiesCrafterCMC", spell.getConvertedManaCost()); + effect.setValue("RashmiEternitiesCrafterCMC", spell.getManaValue()); } return true; } @@ -91,8 +91,8 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { return "Whenever you cast your first spell each turn, reveal the top card " - + "of your library. If it's a nonland card with converted mana " - + "cost less than that spell's, you may cast it without paying " + + "of your library. If it's a nonland card with mana value " + + "less than that spell's, you may cast it without paying " + "its mana cost. If you don't cast the revealed card, put it into your hand."; } } @@ -102,7 +102,7 @@ class RashmiEternitiesCrafterEffect extends OneShotEffect { RashmiEternitiesCrafterEffect() { super(Outcome.PlayForFree); this.staticText = "reveal the top card of your library. If it's a nonland" - + " card with converted mana cost less than that spell's, you may " + + " card with mana value less than that spell's, you may " + "cast it without paying its mana cost. If you don't cast the " + "revealed card, put it into your hand"; } @@ -130,7 +130,7 @@ public boolean apply(Game game, Ability source) { } Object cmcObject = this.getValue("RashmiEternitiesCrafterCMC"); if (cmcObject != null - && card.getConvertedManaCost() < (int) cmcObject + && card.getManaValue() < (int) cmcObject && controller.chooseUse(Outcome.PlayForFree, "Cast " + card.getName() + " without paying its mana cost?", source, game)) { game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); diff --git a/Mage.Sets/src/mage/cards/r/RatColony.java b/Mage.Sets/src/mage/cards/r/RatColony.java index 1a9a71c8b3ec..6292595d2949 100644 --- a/Mage.Sets/src/mage/cards/r/RatColony.java +++ b/Mage.Sets/src/mage/cards/r/RatColony.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/r/RatchetBomb.java b/Mage.Sets/src/mage/cards/r/RatchetBomb.java index ae3598d49790..43efd7acff57 100644 --- a/Mage.Sets/src/mage/cards/r/RatchetBomb.java +++ b/Mage.Sets/src/mage/cards/r/RatchetBomb.java @@ -49,7 +49,7 @@ class RatchetBombEffect extends OneShotEffect { public RatchetBombEffect() { super(Outcome.DestroyPermanent); - staticText = "Destroy each nonland permanent with converted mana cost equal to the number of charge counters on {this}"; + staticText = "Destroy each nonland permanent with mana value equal to the number of charge counters on {this}"; } public RatchetBombEffect(final RatchetBombEffect effect) { @@ -68,7 +68,7 @@ public boolean apply(Game game, Ability source) { int count = p.getCounters(game).getCount(CounterType.CHARGE); for (Permanent perm: game.getBattlefield().getAllActivePermanents()) { - if (perm.getConvertedManaCost() == count && !(perm.isLand())) { + if (perm.getManaValue() == count && !(perm.isLand())) { perm.destroy(source, game, false); } } diff --git a/Mage.Sets/src/mage/cards/r/RathiAssassin.java b/Mage.Sets/src/mage/cards/r/RathiAssassin.java index a3d75739a194..266d6c554689 100644 --- a/Mage.Sets/src/mage/cards/r/RathiAssassin.java +++ b/Mage.Sets/src/mage/cards/r/RathiAssassin.java @@ -21,7 +21,7 @@ import mage.filter.common.FilterPermanentCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.filter.predicate.permanent.TappedPredicate; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetCreaturePermanent; @@ -32,12 +32,12 @@ */ public final class RathiAssassin extends CardImpl { - private static final FilterPermanentCard filter = new FilterPermanentCard("Mercenary permanent card with converted mana cost 3 or less"); + private static final FilterPermanentCard filter = new FilterPermanentCard("Mercenary permanent card with mana value 3 or less"); private static final FilterCreaturePermanent destroyFilter = new FilterCreaturePermanent("tapped nonblack creature"); static { filter.add(SubType.MERCENARY.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); destroyFilter.add(TappedPredicate.instance); destroyFilter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); } diff --git a/Mage.Sets/src/mage/cards/r/RathiFiend.java b/Mage.Sets/src/mage/cards/r/RathiFiend.java index b5fda654ce45..288abb93a72c 100644 --- a/Mage.Sets/src/mage/cards/r/RathiFiend.java +++ b/Mage.Sets/src/mage/cards/r/RathiFiend.java @@ -17,7 +17,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; /** @@ -26,11 +26,11 @@ */ public final class RathiFiend extends CardImpl { - private static final FilterPermanentCard filter = new FilterPermanentCard("Mercenary permanent card with converted mana cost 3 or less"); + private static final FilterPermanentCard filter = new FilterPermanentCard("Mercenary permanent card with mana value 3 or less"); static { filter.add(SubType.MERCENARY.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public RathiFiend(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/r/RathiIntimidator.java b/Mage.Sets/src/mage/cards/r/RathiIntimidator.java index 58a2286725b0..b5c282d24fcb 100644 --- a/Mage.Sets/src/mage/cards/r/RathiIntimidator.java +++ b/Mage.Sets/src/mage/cards/r/RathiIntimidator.java @@ -16,7 +16,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; /** @@ -25,11 +25,11 @@ */ public final class RathiIntimidator extends CardImpl { - private static final FilterPermanentCard filter = new FilterPermanentCard("Mercenary permanent card with converted mana cost 2 or less"); + private static final FilterPermanentCard filter = new FilterPermanentCard("Mercenary permanent card with mana value 2 or less"); static { filter.add(SubType.MERCENARY.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); } public RathiIntimidator(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/r/RavagerOfTheFells.java b/Mage.Sets/src/mage/cards/r/RavagerOfTheFells.java index 008c74711efa..8b4c020a10f8 100644 --- a/Mage.Sets/src/mage/cards/r/RavagerOfTheFells.java +++ b/Mage.Sets/src/mage/cards/r/RavagerOfTheFells.java @@ -1,27 +1,17 @@ - package mage.cards.r; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.keyword.TrampleAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; -import mage.constants.TargetController; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; @@ -33,8 +23,11 @@ import mage.target.TargetPermanent; import mage.target.common.TargetOpponentOrPlaneswalker; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author BetaSteward */ public final class RavagerOfTheFells extends CardImpl { @@ -58,12 +51,7 @@ public RavagerOfTheFells(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new RavagerOfTheFellsAbility()); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Ravager of the Fells. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - ability, - TwoOrMoreSpellsWereCastLastTurnCondition.instance, - TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE - )); + this.addAbility(new WerewolfBackTriggeredAbility()); } private RavagerOfTheFells(final RavagerOfTheFells card) { @@ -78,14 +66,14 @@ public RavagerOfTheFells copy() { class RavagerOfTheFellsAbility extends TriggeredAbilityImpl { - public RavagerOfTheFellsAbility() { + RavagerOfTheFellsAbility() { super(Zone.BATTLEFIELD, new RavagerOfTheFellsEffect(), false); Target target1 = new TargetOpponentOrPlaneswalker(); this.addTarget(target1); this.addTarget(new RavagerOfTheFellsTarget()); } - public RavagerOfTheFellsAbility(final RavagerOfTheFellsAbility ability) { + private RavagerOfTheFellsAbility(final RavagerOfTheFellsAbility ability) { super(ability); } @@ -121,11 +109,11 @@ public String getRule() { class RavagerOfTheFellsEffect extends OneShotEffect { - public RavagerOfTheFellsEffect() { + RavagerOfTheFellsEffect() { super(Outcome.Damage); } - public RavagerOfTheFellsEffect(final RavagerOfTheFellsEffect effect) { + private RavagerOfTheFellsEffect(final RavagerOfTheFellsEffect effect) { super(effect); } @@ -148,11 +136,11 @@ public boolean apply(Game game, Ability source) { class RavagerOfTheFellsTarget extends TargetPermanent { - public RavagerOfTheFellsTarget() { + RavagerOfTheFellsTarget() { super(0, 1, StaticFilters.FILTER_PERMANENT_CREATURE, false); } - public RavagerOfTheFellsTarget(final RavagerOfTheFellsTarget target) { + private RavagerOfTheFellsTarget(final RavagerOfTheFellsTarget target) { super(target); } diff --git a/Mage.Sets/src/mage/cards/r/RaziaBorosArchangel.java b/Mage.Sets/src/mage/cards/r/RaziaBorosArchangel.java index 97c41551afc4..a1a15feb12fb 100644 --- a/Mage.Sets/src/mage/cards/r/RaziaBorosArchangel.java +++ b/Mage.Sets/src/mage/cards/r/RaziaBorosArchangel.java @@ -20,7 +20,7 @@ import mage.constants.SuperType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.target.Target; diff --git a/Mage.Sets/src/mage/cards/r/RazorHippogriff.java b/Mage.Sets/src/mage/cards/r/RazorHippogriff.java index 4974aefeb334..c4f2c1dc1190 100644 --- a/Mage.Sets/src/mage/cards/r/RazorHippogriff.java +++ b/Mage.Sets/src/mage/cards/r/RazorHippogriff.java @@ -7,6 +7,7 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.Card; @@ -37,7 +38,7 @@ public RazorHippogriff (UUID ownerId, CardSetInfo setInfo) { this.addAbility(FlyingAbility.getInstance()); - Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect()); + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect()); TargetCard target = new TargetCardInYourGraveyard(new FilterArtifactCard("artifact card from your graveyard")); ability.addTarget(target); ability.addEffect(new RazorHippogriffGainLifeEffect()); @@ -58,7 +59,7 @@ public final class RazorHippogriffGainLifeEffect extends OneShotEffect { public RazorHippogriffGainLifeEffect() { super(Outcome.GainLife); - staticText = "you gain life equal to that card's converted mana cost."; + staticText = "you gain life equal to that card's mana value."; } public RazorHippogriffGainLifeEffect(final RazorHippogriffGainLifeEffect effect) { @@ -79,7 +80,7 @@ public boolean apply(Game game, Ability source) { card = (Card)game.getLastKnownInformation(source.getFirstTarget(), Zone.GRAVEYARD); } if (card != null) { - player.gainLife(card.getConvertedManaCost(), game, source); + player.gainLife(card.getManaValue(), game, source); } } return true; diff --git a/Mage.Sets/src/mage/cards/r/ReadTheBones.java b/Mage.Sets/src/mage/cards/r/ReadTheBones.java index bf09130a2bd3..eadf6c573713 100644 --- a/Mage.Sets/src/mage/cards/r/ReadTheBones.java +++ b/Mage.Sets/src/mage/cards/r/ReadTheBones.java @@ -1,7 +1,5 @@ - package mage.cards.r; -import java.util.UUID; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.LoseLifeSourceControllerEffect; @@ -10,20 +8,20 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class ReadTheBones extends CardImpl { public ReadTheBones(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}"); // Scry 2, then draw two cards. You lose 2 life. - this.getSpellAbility().addEffect(new ScryEffect(2)); + this.getSpellAbility().addEffect(new ScryEffect(2, false)); Effect effect = new DrawCardSourceControllerEffect(2); - effect.setText("then draw two cards"); + effect.setText(", then draw two cards"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addEffect(new LoseLifeSourceControllerEffect(2)); } diff --git a/Mage.Sets/src/mage/cards/r/RealityStrobe.java b/Mage.Sets/src/mage/cards/r/RealityStrobe.java index b375ce3d9b7d..2e4ae802a410 100644 --- a/Mage.Sets/src/mage/cards/r/RealityStrobe.java +++ b/Mage.Sets/src/mage/cards/r/RealityStrobe.java @@ -28,7 +28,7 @@ public RealityStrobe(UUID ownerId, CardSetInfo setInfo) { // Return target permanent to its owner's hand. this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); // Exile Reality Strobe - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); // with three time counters on it. Effect effect = new AddCountersSourceEffect(CounterType.TIME.createInstance(), StaticValue.get(3), false, true); effect.setText("with 3 time counters on it"); diff --git a/Mage.Sets/src/mage/cards/r/RealmsUncharted.java b/Mage.Sets/src/mage/cards/r/RealmsUncharted.java index 218d103dd7d6..0a1699fa1af1 100644 --- a/Mage.Sets/src/mage/cards/r/RealmsUncharted.java +++ b/Mage.Sets/src/mage/cards/r/RealmsUncharted.java @@ -45,7 +45,7 @@ class RealmsUnchartedEffect extends OneShotEffect { public RealmsUnchartedEffect() { super(Outcome.DrawCard); - this.staticText = "Search your library for four land cards with different names and reveal them. An opponent chooses two of those cards. Put the chosen cards into your graveyard and the rest into your hand. Then shuffle your library"; + this.staticText = "Search your library for four land cards with different names and reveal them. An opponent chooses two of those cards. Put the chosen cards into your graveyard and the rest into your hand. Then shuffle"; } public RealmsUnchartedEffect(final RealmsUnchartedEffect effect) { diff --git a/Mage.Sets/src/mage/cards/r/Realmwalker.java b/Mage.Sets/src/mage/cards/r/Realmwalker.java index b08dc1ac26db..6f57551358f5 100644 --- a/Mage.Sets/src/mage/cards/r/Realmwalker.java +++ b/Mage.Sets/src/mage/cards/r/Realmwalker.java @@ -1,6 +1,5 @@ package mage.cards.r; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; @@ -16,13 +15,15 @@ import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.mageobject.ChosenSubtypePredicate; +import java.util.UUID; + /** - * * @author weirddan455 */ public final class Realmwalker extends CardImpl { private static final FilterCreatureCard filter = new FilterCreatureCard("cast creature spells of the chosen type"); + static { filter.add(ChosenSubtypePredicate.TRUE); } @@ -44,7 +45,7 @@ public Realmwalker(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect())); // You may cast creature spells of the chosen type from the top of your library. - this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter))); + this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false))); } private Realmwalker(final Realmwalker card) { diff --git a/Mage.Sets/src/mage/cards/r/Reanimate.java b/Mage.Sets/src/mage/cards/r/Reanimate.java index 551035fafc3d..c4bc8ba2eee5 100644 --- a/Mage.Sets/src/mage/cards/r/Reanimate.java +++ b/Mage.Sets/src/mage/cards/r/Reanimate.java @@ -1,33 +1,32 @@ - package mage.cards.r; -import java.util.UUID; -import mage.abilities.dynamicvalue.common.TargetConvertedManaCost; +import mage.abilities.dynamicvalue.common.TargetManaValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.LoseLifeSourceControllerEffect; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInGraveyard; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class Reanimate extends CardImpl { public Reanimate(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{B}"); // Put target creature card from a graveyard onto the battlefield under your control. You lose life equal to its converted mana cost. - getSpellAbility().addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card from a graveyard"))); - getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); - Effect effect = new LoseLifeSourceControllerEffect(TargetConvertedManaCost.instance); - effect.setText("You lose life equal to its converted mana cost"); - getSpellAbility().addEffect(effect); + getSpellAbility().addTarget(new TargetCardInGraveyard(StaticFilters.FILTER_CARD_CREATURE)); + getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect() + .setText("put target creature card from a graveyard onto the battlefield under your control")); + Effect effect = new LoseLifeSourceControllerEffect(TargetManaValue.instance); + effect.setText("You lose life equal to its mana value"); + getSpellAbility().addEffect(effect); } private Reanimate(final Reanimate card) { diff --git a/Mage.Sets/src/mage/cards/r/ReapIntellect.java b/Mage.Sets/src/mage/cards/r/ReapIntellect.java index 00e4639f0e33..0d5fde0fd591 100644 --- a/Mage.Sets/src/mage/cards/r/ReapIntellect.java +++ b/Mage.Sets/src/mage/cards/r/ReapIntellect.java @@ -63,7 +63,7 @@ public ReapIntellectEffect() { + "nonland cards from it and exile them. For each card exiled " + "this way, search that player's graveyard, hand, and library " + "for any number of cards with the same name as that card and " - + "exile them. Then that player shuffles their library"; + + "exile them. Then that player shuffles"; } public ReapIntellectEffect(final ReapIntellectEffect effect) { diff --git a/Mage.Sets/src/mage/cards/r/ReapThePast.java b/Mage.Sets/src/mage/cards/r/ReapThePast.java index d10ed9f27e0a..f43604279d2b 100644 --- a/Mage.Sets/src/mage/cards/r/ReapThePast.java +++ b/Mage.Sets/src/mage/cards/r/ReapThePast.java @@ -65,6 +65,6 @@ public boolean apply(Game game, Ability source) { cards.remove(cards.getRandom(game)); } player.moveCards(cards, Zone.HAND, source, game); - return ExileSpellEffect.getInstance().apply(game, source); + return new ExileSpellEffect().apply(game, source); } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/r/ReaperKing.java b/Mage.Sets/src/mage/cards/r/ReaperKing.java index bf94ca1f3140..7de37168157a 100644 --- a/Mage.Sets/src/mage/cards/r/ReaperKing.java +++ b/Mage.Sets/src/mage/cards/r/ReaperKing.java @@ -12,7 +12,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; /** diff --git a/Mage.Sets/src/mage/cards/r/ReaperOfFlightMoonsilver.java b/Mage.Sets/src/mage/cards/r/ReaperOfFlightMoonsilver.java index cf93894871d1..39b0888966fe 100644 --- a/Mage.Sets/src/mage/cards/r/ReaperOfFlightMoonsilver.java +++ b/Mage.Sets/src/mage/cards/r/ReaperOfFlightMoonsilver.java @@ -41,7 +41,7 @@ public ReaperOfFlightMoonsilver(UUID ownerId, CardSetInfo setInfo) { new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT)), DeliriumCondition.instance, "Delirium — Sacrifice another creature: Reaper of Flight Moonsilver gets +2/+1 until end of turn. " - + "Activate this ability only if there are four or more card types among cards in your graveyard") + + "Activate only if there are four or more card types among cards in your graveyard.") .addHint(DeliriumHint.instance)); } diff --git a/Mage.Sets/src/mage/cards/r/ReaperOfSheoldred.java b/Mage.Sets/src/mage/cards/r/ReaperOfSheoldred.java index 6892df8335b6..c83145062668 100644 --- a/Mage.Sets/src/mage/cards/r/ReaperOfSheoldred.java +++ b/Mage.Sets/src/mage/cards/r/ReaperOfSheoldred.java @@ -66,7 +66,7 @@ public ReaperOfSheoldredTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/r/ReaverDrone.java b/Mage.Sets/src/mage/cards/r/ReaverDrone.java index 5de394f7bc09..91699d555c0a 100644 --- a/Mage.Sets/src/mage/cards/r/ReaverDrone.java +++ b/Mage.Sets/src/mage/cards/r/ReaverDrone.java @@ -17,7 +17,7 @@ import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorlessPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/r/RebbecArchitectOfAscension.java b/Mage.Sets/src/mage/cards/r/RebbecArchitectOfAscension.java index dbbb495ff286..1720c48f8c32 100644 --- a/Mage.Sets/src/mage/cards/r/RebbecArchitectOfAscension.java +++ b/Mage.Sets/src/mage/cards/r/RebbecArchitectOfAscension.java @@ -27,7 +27,7 @@ public final class RebbecArchitectOfAscension extends CardImpl { private static final FilterCard filter - = new FilterCard("each converted mana cost among artifacts you control"); + = new FilterCard("each mana value among artifacts you control"); static { filter.add(RebbecArchitectOfAscensionPredicate.instance); @@ -73,7 +73,7 @@ public boolean apply(ObjectSourcePlayer input, Game game) { game.getControllerId(input.getSourceId()), input.getSourceId(), game ).stream() .filter(Objects::nonNull) - .mapToInt(MageObject::getConvertedManaCost) - .anyMatch(n -> input.getObject().getConvertedManaCost() == n); + .mapToInt(MageObject::getManaValue) + .anyMatch(n -> input.getObject().getManaValue() == n); } } diff --git a/Mage.Sets/src/mage/cards/r/RebellionOfTheFlamekin.java b/Mage.Sets/src/mage/cards/r/RebellionOfTheFlamekin.java index adb082711d0d..62b6d4247517 100644 --- a/Mage.Sets/src/mage/cards/r/RebellionOfTheFlamekin.java +++ b/Mage.Sets/src/mage/cards/r/RebellionOfTheFlamekin.java @@ -1,39 +1,34 @@ - package mage.cards.r; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.Effect; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.permanent.token.ElementalShamanToken; -import mage.players.Player; -import mage.target.targetpointer.FixedTarget; +import mage.game.permanent.token.Token; +import mage.target.targetpointer.FixedTargets; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; /** - * * @author Styxo */ public final class RebellionOfTheFlamekin extends CardImpl { public RebellionOfTheFlamekin(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.TRIBAL,CardType.ENCHANTMENT},"{3}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.TRIBAL, CardType.ENCHANTMENT}, "{3}{R}"); this.subtype.add(SubType.ELEMENTAL); // Whenever you clash, you may pay {1}. If you do create a 3/1 Red Elemental Shaman creature token in play. If you won that token gains haste @@ -52,11 +47,11 @@ public RebellionOfTheFlamekin copy() { class RebellionOfTheFlamekinTriggeredAbility extends TriggeredAbilityImpl { - public RebellionOfTheFlamekinTriggeredAbility() { - super(Zone.BATTLEFIELD, new RebellionOfTheFlamekinEffect()); + RebellionOfTheFlamekinTriggeredAbility() { + super(Zone.BATTLEFIELD, new DoIfCostPaid(new RebellionOfTheFlamekinEffect(), new GenericManaCost(1))); } - public RebellionOfTheFlamekinTriggeredAbility(final RebellionOfTheFlamekinTriggeredAbility ability) { + private RebellionOfTheFlamekinTriggeredAbility(final RebellionOfTheFlamekinTriggeredAbility ability) { super(ability); } @@ -72,32 +67,29 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { - boolean youWonTheClash = false; - if (event.getData().equals("controller") && event.getPlayerId().equals(getControllerId()) - || event.getData().equals("opponent") && event.getTargetId().equals(getControllerId())) { - youWonTheClash = true; - } - for (Effect effect : getEffects()) { - if (effect instanceof RebellionOfTheFlamekinEffect) { - effect.setValue("clash", youWonTheClash); - } + if (!isControlledBy(event.getPlayerId())) { + return false; } + this.getEffects().setValue("clash", event.getFlag()); return true; } @Override public String getRule() { - return "Whenever you clash, you may pay {1}. If you do create a 3/1 Red Elemental Shaman creature token in play. If you won that token gains haste until end of turn"; + return "Whenever you clash, you may pay {1}. If you do, " + + "create a 3/1 red Elemental Shaman creature token. " + + "If you won, that token gains haste until end of turn. " + + "(This ability triggers after the clash ends.)"; } } class RebellionOfTheFlamekinEffect extends OneShotEffect { - public RebellionOfTheFlamekinEffect() { + RebellionOfTheFlamekinEffect() { super(Outcome.PutCreatureInPlay); } - public RebellionOfTheFlamekinEffect(final RebellionOfTheFlamekinEffect effect) { + private RebellionOfTheFlamekinEffect(final RebellionOfTheFlamekinEffect effect) { super(effect); } @@ -108,21 +100,20 @@ public RebellionOfTheFlamekinEffect copy() { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - CreateTokenEffect createTokenEffect = new CreateTokenEffect(new ElementalShamanToken("LRW")); - DoIfCostPaid doIfCostPaid = new DoIfCostPaid(createTokenEffect, new ManaCostsImpl("{1}")); - doIfCostPaid.apply(game, source); - Permanent token = game.getPermanent(createTokenEffect.getLastAddedTokenId()); - if (token != null && (boolean) (this.getValue("clash"))) { - ContinuousEffect continuousEffect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); - continuousEffect.setTargetPointer(new FixedTarget(createTokenEffect.getLastAddedTokenId())); - game.addEffect(continuousEffect, source); - } - return true; - + Token token = new ElementalShamanToken("LRW"); + token.putOntoBattlefield(1, game, source, source.getControllerId()); + + List permanents = token + .getLastAddedTokenIds() + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + if (!permanents.isEmpty() && (boolean) this.getValue("clash")) { + game.addEffect(new GainAbilityTargetEffect( + HasteAbility.getInstance(), Duration.EndOfTurn + ).setTargetPointer(new FixedTargets(permanents, game)), source); } - return false; - + return true; } } diff --git a/Mage.Sets/src/mage/cards/r/Rebound.java b/Mage.Sets/src/mage/cards/r/Rebound.java index cbd0a1acdeb9..eae18ec190e0 100644 --- a/Mage.Sets/src/mage/cards/r/Rebound.java +++ b/Mage.Sets/src/mage/cards/r/Rebound.java @@ -8,7 +8,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.filter.FilterSpell; -import mage.filter.predicate.other.TargetsOnlyOnePlayerPredicate; +import mage.filter.predicate.mageobject.TargetsOnlyOnePlayerPredicate; import mage.game.Game; import mage.game.stack.Spell; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/r/RebuffTheWicked.java b/Mage.Sets/src/mage/cards/r/RebuffTheWicked.java index 1874e28791d7..23992560c047 100644 --- a/Mage.Sets/src/mage/cards/r/RebuffTheWicked.java +++ b/Mage.Sets/src/mage/cards/r/RebuffTheWicked.java @@ -8,7 +8,7 @@ import mage.constants.CardType; import mage.filter.FilterSpell; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.other.TargetsPermanentPredicate; +import mage.filter.predicate.mageobject.TargetsPermanentPredicate; import mage.target.TargetSpell; /** diff --git a/Mage.Sets/src/mage/cards/r/Recall.java b/Mage.Sets/src/mage/cards/r/Recall.java index 507fca512167..974f8ba02856 100644 --- a/Mage.Sets/src/mage/cards/r/Recall.java +++ b/Mage.Sets/src/mage/cards/r/Recall.java @@ -29,7 +29,7 @@ public Recall(UUID ownerId, CardSetInfo setInfo) { // Discard X cards, then return a card from your graveyard to your hand for each card discarded this way. this.getSpellAbility().addEffect(new RecallEffect()); // Exile Recall. - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private Recall(final Recall card) { diff --git a/Mage.Sets/src/mage/cards/r/RecklessAmplimancer.java b/Mage.Sets/src/mage/cards/r/RecklessAmplimancer.java new file mode 100644 index 000000000000..5cc9bf280742 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RecklessAmplimancer.java @@ -0,0 +1,47 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount; +import mage.abilities.dynamicvalue.common.SourcePermanentToughnessValue; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RecklessAmplimancer extends CardImpl { + + private static final DynamicValue sourcePower = new SourcePermanentPowerCount(); + + public RecklessAmplimancer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.ELF); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // {4}{G}: Double Reckless Amplimancer's power and toughness until end of turn. + this.addAbility(new SimpleActivatedAbility(new BoostSourceEffect( + sourcePower, SourcePermanentToughnessValue.getInstance(), Duration.EndOfTurn, true + ).setText("double {this}'s power and toughness until end of turn"), new ManaCostsImpl("{4}{G}"))); + } + + private RecklessAmplimancer(final RecklessAmplimancer card) { + super(card); + } + + @Override + public RecklessAmplimancer copy() { + return new RecklessAmplimancer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RecklessBushwhacker.java b/Mage.Sets/src/mage/cards/r/RecklessBushwhacker.java index 2c871f2a9043..5d8dc12d4fb7 100644 --- a/Mage.Sets/src/mage/cards/r/RecklessBushwhacker.java +++ b/Mage.Sets/src/mage/cards/r/RecklessBushwhacker.java @@ -16,7 +16,7 @@ import mage.constants.SubType; import mage.constants.Duration; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/r/RecklessCohort.java b/Mage.Sets/src/mage/cards/r/RecklessCohort.java index 1ac4cef07951..b2bddd031832 100644 --- a/Mage.Sets/src/mage/cards/r/RecklessCohort.java +++ b/Mage.Sets/src/mage/cards/r/RecklessCohort.java @@ -12,7 +12,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/r/RecklessWaif.java b/Mage.Sets/src/mage/cards/r/RecklessWaif.java index 42f428f7602c..848d23ac6232 100644 --- a/Mage.Sets/src/mage/cards/r/RecklessWaif.java +++ b/Mage.Sets/src/mage/cards/r/RecklessWaif.java @@ -1,20 +1,14 @@ - package mage.cards.r; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** * @author nantuko @@ -35,8 +29,7 @@ public RecklessWaif(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each upkeep, if no spells were cast last turn, transform Reckless Waif. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private RecklessWaif(final RecklessWaif card) { diff --git a/Mage.Sets/src/mage/cards/r/ReclaimTheWastes.java b/Mage.Sets/src/mage/cards/r/ReclaimTheWastes.java index bc6b5694e6b5..aa753520141a 100644 --- a/Mage.Sets/src/mage/cards/r/ReclaimTheWastes.java +++ b/Mage.Sets/src/mage/cards/r/ReclaimTheWastes.java @@ -33,7 +33,7 @@ public ReclaimTheWastes(UUID ownerId, CardSetInfo setInfo) { 0, 1, StaticFilters.FILTER_CARD_BASIC_LAND ), true), KickedCondition.instance, "search your library for a basic land card, " + - "reveal it, put it into your hand, then shuffle your library. If this spell was kicked, " + + "reveal it, put it into your hand, then shuffle. If this spell was kicked, " + "search your library for two basic land cards instead of one" )); } diff --git a/Mage.Sets/src/mage/cards/r/ReclusiveWight.java b/Mage.Sets/src/mage/cards/r/ReclusiveWight.java index 4c49464a9349..11ccd3cd406e 100644 --- a/Mage.Sets/src/mage/cards/r/ReclusiveWight.java +++ b/Mage.Sets/src/mage/cards/r/ReclusiveWight.java @@ -14,7 +14,7 @@ import mage.constants.TargetController; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/r/ReconstructHistory.java b/Mage.Sets/src/mage/cards/r/ReconstructHistory.java new file mode 100644 index 000000000000..e0cc3077f95a --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ReconstructHistory.java @@ -0,0 +1,69 @@ +package mage.cards.r; + +import mage.abilities.effects.common.ExileSpellEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterCard; +import mage.filter.common.FilterArtifactCard; +import mage.filter.common.FilterEnchantmentCard; +import mage.filter.common.FilterPlaneswalkerCard; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ReconstructHistory extends CardImpl { + + private static final FilterCard filterArtifact = new FilterArtifactCard(); + private static final FilterCard filterEnchantment = new FilterEnchantmentCard(); + private static final FilterCard filterInstant = new FilterCard("instant card"); + private static final FilterCard filterSorcery = new FilterCard("sorcery card"); + private static final FilterCard filterPlaneswalker = new FilterPlaneswalkerCard(); + private static final String rule = "return up to one target artifact card, " + + "up to one target enchantment card, up to one target instant card, " + + "up to one target sorcery card, and up to one target planeswalker " + + "card from your graveyard to your hand"; + + static { + filterInstant.add(CardType.INSTANT.getPredicate()); + filterSorcery.add(CardType.SORCERY.getPredicate()); + } + + public ReconstructHistory(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}{W}"); + + // Return up to one target artifact card, up to one target enchantment card, up to one target instant card, up to one target sorcery card, and up to one target planeswalker card from your graveyard to your hand. + this.getSpellAbility().addEffect(new ReturnToHandTargetEffect().setText(rule)); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard( + 0, 1, filterArtifact + )); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard( + 0, 1, filterEnchantment + )); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard( + 0, 1, filterInstant + )); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard( + 0, 1, filterSorcery + )); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard( + 0, 1, filterPlaneswalker + )); + + // Exile Reconstruct History. + this.getSpellAbility().addEffect(new ExileSpellEffect().concatBy("
")); + } + + private ReconstructHistory(final ReconstructHistory card) { + super(card); + } + + @Override + public ReconstructHistory copy() { + return new ReconstructHistory(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RecurringNightmare.java b/Mage.Sets/src/mage/cards/r/RecurringNightmare.java index b262f6c2d9f4..b0ff7867537a 100644 --- a/Mage.Sets/src/mage/cards/r/RecurringNightmare.java +++ b/Mage.Sets/src/mage/cards/r/RecurringNightmare.java @@ -27,7 +27,7 @@ public RecurringNightmare(UUID ownerId, CardSetInfo setInfo) { Ability ability = new ActivateAsSorceryActivatedAbility( Zone.BATTLEFIELD, new ReturnFromGraveyardToBattlefieldTargetEffect(), - new SacrificeTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE)) + new SacrificeTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT)) ); ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); ability.addCost(new ReturnToHandFromBattlefieldSourceCost()); diff --git a/Mage.Sets/src/mage/cards/r/RedeemTheLost.java b/Mage.Sets/src/mage/cards/r/RedeemTheLost.java index 7ccd344db147..16b5a15516eb 100644 --- a/Mage.Sets/src/mage/cards/r/RedeemTheLost.java +++ b/Mage.Sets/src/mage/cards/r/RedeemTheLost.java @@ -8,6 +8,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; +import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; /** @@ -21,7 +22,7 @@ public RedeemTheLost(UUID ownerId, CardSetInfo setInfo) { // Target creature you control gains protection from the color of your choice until end of turn. this.getSpellAbility().addEffect(new GainProtectionFromColorTargetEffect(Duration.EndOfTurn)); - this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); // Clash with an opponent. If you win, return Redeem the Lost to its owner's hand. this.getSpellAbility().addEffect(ClashWinReturnToHandSpellEffect.getInstance()); } diff --git a/Mage.Sets/src/mage/cards/r/ReduceToMemory.java b/Mage.Sets/src/mage/cards/r/ReduceToMemory.java new file mode 100644 index 000000000000..443e93b14d9b --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ReduceToMemory.java @@ -0,0 +1,71 @@ +package mage.cards.r; + +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.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.LoreholdToken; +import mage.players.Player; +import mage.target.common.TargetNonlandPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ReduceToMemory extends CardImpl { + + public ReduceToMemory(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{W}{W}"); + + this.subtype.add(SubType.LESSON); + + // Exile target nonland permanent. Its controller creates a 3/2 red and white spirit creature token. + this.getSpellAbility().addEffect(new ReduceToMemoryEffect()); + this.getSpellAbility().addTarget(new TargetNonlandPermanent()); + } + + private ReduceToMemory(final ReduceToMemory card) { + super(card); + } + + @Override + public ReduceToMemory copy() { + return new ReduceToMemory(this); + } +} + +class ReduceToMemoryEffect extends OneShotEffect { + + ReduceToMemoryEffect() { + super(Outcome.Benefit); + staticText = "exile target nonland permanent. Its controller creates a 3/2 red and white Spirit creature token"; + } + + private ReduceToMemoryEffect(final ReduceToMemoryEffect effect) { + super(effect); + } + + @Override + public ReduceToMemoryEffect copy() { + return new ReduceToMemoryEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + Player player = game.getPlayer(permanent.getControllerId()); + player.moveCards(permanent, Zone.EXILED, source, game); + new LoreholdToken().putOntoBattlefield(1, game, source, player.getId()); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/r/ReflectiveGolem.java b/Mage.Sets/src/mage/cards/r/ReflectiveGolem.java new file mode 100644 index 000000000000..e1653f08b3cb --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ReflectiveGolem.java @@ -0,0 +1,105 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.AbilityImpl; +import mage.abilities.Mode; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CopyTargetSpellEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.target.Target; +import mage.target.targetpointer.FixedTarget; + +import java.util.Collection; +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ReflectiveGolem extends CardImpl { + + public ReflectiveGolem(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); + + this.subtype.add(SubType.GOLEM); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Whenever you cast an instant or sorcery spell that targets only Reflective Golem, you may pay {2}. If you do, copy that spell. You may choose new targets for the copy. + this.addAbility(new ReflectiveGolemTriggeredAbility()); + } + + private ReflectiveGolem(final ReflectiveGolem card) { + super(card); + } + + @Override + public ReflectiveGolem copy() { + return new ReflectiveGolem(this); + } +} + +class ReflectiveGolemTriggeredAbility extends TriggeredAbilityImpl { + + ReflectiveGolemTriggeredAbility() { + super(Zone.BATTLEFIELD, new DoIfCostPaid(new CopyTargetSpellEffect(), new GenericManaCost(2)), false); + } + + private ReflectiveGolemTriggeredAbility(final ReflectiveGolemTriggeredAbility ability) { + super(ability); + } + + @Override + public ReflectiveGolemTriggeredAbility copy() { + return new ReflectiveGolemTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!isControlledBy(event.getPlayerId())) { + return false; + } + Spell spell = game.getSpellOrLKIStack(event.getTargetId()); + if (spell == null || !spell.isInstantOrSorcery()) { + return false; + } + if (spell.getSpellAbilities() + .stream() + .map(AbilityImpl::getModes) + .flatMap(m -> m.getSelectedModes().stream().map(m::get)) + .filter(Objects::nonNull) + .map(Mode::getTargets) + .flatMap(Collection::stream) + .filter(t -> !t.isNotTarget()) + .map(Target::getTargets) + .flatMap(Collection::stream) + .filter(Objects::nonNull) + .distinct() + .filter(getSourceId()::equals) + .count() != 1) { + return false; + } + this.getEffects().setTargetPointer(new FixedTarget(spell, game)); + return true; + } + + @Override + public String getRule() { + return "Whenever you cast an instant or sorcery spell that targets only {this}, " + + "you may pay {2}. If you do, copy that spell. You may choose new targets for the copy."; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RefuseCooperate.java b/Mage.Sets/src/mage/cards/r/RefuseCooperate.java index a1022fdaae04..92c6de21674d 100644 --- a/Mage.Sets/src/mage/cards/r/RefuseCooperate.java +++ b/Mage.Sets/src/mage/cards/r/RefuseCooperate.java @@ -52,7 +52,7 @@ class RefuseEffect extends OneShotEffect { public RefuseEffect() { super(Outcome.Damage); - staticText = "Refuse deals damage to target spell's controller equal to that spell's converted mana cost"; + staticText = "Refuse deals damage to target spell's controller equal to that spell's mana value"; } public RefuseEffect(final RefuseEffect effect) { @@ -72,7 +72,7 @@ public boolean apply(Game game, Ability source) { if (spell != null) { Player spellController = game.getPlayer(spell.getControllerId()); if (spellController != null) { - spellController.damage(spell.getConvertedManaCost(), source.getSourceId(), source, game); + spellController.damage(spell.getManaValue(), source.getSourceId(), source, game); return true; } } diff --git a/Mage.Sets/src/mage/cards/r/RegalBloodlord.java b/Mage.Sets/src/mage/cards/r/RegalBloodlord.java index 0be1200cb447..505126bfce2e 100644 --- a/Mage.Sets/src/mage/cards/r/RegalBloodlord.java +++ b/Mage.Sets/src/mage/cards/r/RegalBloodlord.java @@ -1,23 +1,24 @@ package mage.cards.r; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.condition.common.YouGainedLifeCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.dynamicvalue.common.ControllerGotLifeCount; import mage.abilities.effects.common.CreateTokenEffect; -import mage.constants.SubType; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.ComparisonType; +import mage.constants.SubType; import mage.constants.TargetController; import mage.game.permanent.token.BatToken; import mage.watchers.common.PlayerGainedLifeWatcher; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class RegalBloodlord extends CardImpl { @@ -41,9 +42,9 @@ public RegalBloodlord(UUID ownerId, CardSetInfo setInfo) { ), new YouGainedLifeCondition(ComparisonType.MORE_THAN, 0), "At the beginning of each end step, " - + "if you gained life this turn, " - + "create a 1/1 black Bat creature token with flying." - ), new PlayerGainedLifeWatcher()); + + "if you gained life this turn, " + + "create a 1/1 black Bat creature token with flying." + ).addHint(ControllerGotLifeCount.getHint()), new PlayerGainedLifeWatcher()); } private RegalBloodlord(final RegalBloodlord card) { diff --git a/Mage.Sets/src/mage/cards/r/Regrowth.java b/Mage.Sets/src/mage/cards/r/Regrowth.java index 96d13a42200a..4336b82d6222 100644 --- a/Mage.Sets/src/mage/cards/r/Regrowth.java +++ b/Mage.Sets/src/mage/cards/r/Regrowth.java @@ -1,26 +1,23 @@ - package mage.cards.r; -import java.util.UUID; -import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.target.common.TargetCardInYourGraveyard; +import java.util.UUID; + /** - * * @author KholdFuzion - */ public final class Regrowth extends CardImpl { public Regrowth(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{G}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}"); // Return target card from your graveyard to your hand. - this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); + this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard()); } diff --git a/Mage.Sets/src/mage/cards/r/ReidaneGodOfTheWorthy.java b/Mage.Sets/src/mage/cards/r/ReidaneGodOfTheWorthy.java index 4b717659189c..a8d2bd400ec9 100644 --- a/Mage.Sets/src/mage/cards/r/ReidaneGodOfTheWorthy.java +++ b/Mage.Sets/src/mage/cards/r/ReidaneGodOfTheWorthy.java @@ -121,7 +121,7 @@ class ReidaneGodOfTheWorthyCostEffect extends CostModificationEffectImpl { ReidaneGodOfTheWorthyCostEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST); - staticText = "Noncreature spells your opponents cast with converted mana cost 4 or greater cost {2} more to cast"; + staticText = "Noncreature spells your opponents cast with mana value 4 or greater cost {2} more to cast"; } private ReidaneGodOfTheWorthyCostEffect(ReidaneGodOfTheWorthyCostEffect effect) { @@ -142,7 +142,7 @@ public boolean applies(Ability abilityToModify, Ability source, Game game) { return false; } Card spellCard = ((SpellAbility) abilityToModify).getCharacteristics(game); - return spellCard != null && !spellCard.isCreature() && spellCard.getConvertedManaCost() >= 4; + return spellCard != null && !spellCard.isCreature() && spellCard.getManaValue() >= 4; } @Override @@ -175,8 +175,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { return isOpponent && source.isControlledBy(event.getTargetId()) && super.applies(event, source, game); - case DAMAGE_CREATURE: - case DAMAGE_PLANESWALKER: + case DAMAGE_PERMANENT: isOpponent = game.getOpponents(game.getControllerId(event.getSourceId())).contains(source.getControllerId()); Permanent permanent = game.getPermanent(event.getTargetId()); return isOpponent diff --git a/Mage.Sets/src/mage/cards/r/Reincarnation.java b/Mage.Sets/src/mage/cards/r/Reincarnation.java index 05dbb8b9570b..81250560bdae 100644 --- a/Mage.Sets/src/mage/cards/r/Reincarnation.java +++ b/Mage.Sets/src/mage/cards/r/Reincarnation.java @@ -13,10 +13,9 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/r/ReinsOfTheVinesteed.java b/Mage.Sets/src/mage/cards/r/ReinsOfTheVinesteed.java index 5e3dee608579..f97d8b80521d 100644 --- a/Mage.Sets/src/mage/cards/r/ReinsOfTheVinesteed.java +++ b/Mage.Sets/src/mage/cards/r/ReinsOfTheVinesteed.java @@ -13,7 +13,7 @@ import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.SharesCreatureTypePredicate; +import mage.filter.predicate.mageobject.SharesCreatureTypePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/r/Reinterpret.java b/Mage.Sets/src/mage/cards/r/Reinterpret.java new file mode 100644 index 000000000000..3f8276acb85b --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/Reinterpret.java @@ -0,0 +1,67 @@ +package mage.cards.r; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.cost.CastWithoutPayingManaCostEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.target.TargetSpell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Reinterpret extends CardImpl { + + public Reinterpret(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}{R}"); + + // Counter target spell. You may cast a spell with an equal or lesser mana value from your hand without paying its mana cost. + this.getSpellAbility().addEffect(new ReinterpretEffect()); + this.getSpellAbility().addTarget(new TargetSpell()); + } + + private Reinterpret(final Reinterpret card) { + super(card); + } + + @Override + public Reinterpret copy() { + return new Reinterpret(this); + } +} + +class ReinterpretEffect extends OneShotEffect { + + ReinterpretEffect() { + super(Outcome.Benefit); + staticText = "counter target spell. You may cast a spell with an " + + "equal or lesser mana value from your hand without paying its mana cost"; + } + + private ReinterpretEffect(final ReinterpretEffect effect) { + super(effect); + } + + @Override + public ReinterpretEffect copy() { + return new ReinterpretEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Spell spell = game.getSpell(source.getFirstTarget()); + if (spell == null) { + return false; + } + int manaValue = spell.getManaValue(); + spell.counter(source, game); + new CastWithoutPayingManaCostEffect(manaValue).apply(game, source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/r/Reject.java b/Mage.Sets/src/mage/cards/r/Reject.java new file mode 100644 index 000000000000..afcb870c5acc --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/Reject.java @@ -0,0 +1,44 @@ +package mage.cards.r; + +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CounterUnlessPaysEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterSpell; +import mage.filter.predicate.Predicates; +import mage.target.TargetSpell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Reject extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("creature or planeswalker spell"); + + static { + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + CardType.PLANESWALKER.getPredicate() + )); + } + + public Reject(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); + + // Counter target creature or planeswalker spell unless its controller pays {3}. If that spell is countered this way, exile it instead of putting into its owner's graveyard. + this.getSpellAbility().addEffect(new CounterUnlessPaysEffect(new GenericManaCost(3), true)); + this.getSpellAbility().addTarget(new TargetSpell(filter)); + } + + private Reject(final Reject card) { + super(card); + } + + @Override + public Reject copy() { + return new Reject(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RelentlessDead.java b/Mage.Sets/src/mage/cards/r/RelentlessDead.java index f08c7b57a8de..f0c19dddf28a 100644 --- a/Mage.Sets/src/mage/cards/r/RelentlessDead.java +++ b/Mage.Sets/src/mage/cards/r/RelentlessDead.java @@ -14,7 +14,7 @@ import mage.constants.*; import mage.filter.FilterCard; import mage.filter.predicate.mageobject.AnotherCardPredicate; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; @@ -37,7 +37,7 @@ public RelentlessDead(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new MenaceAbility()); // When Relentless Dead dies, you may pay {B}. If you do, return it to its owner's hand. - this.addAbility(new DiesSourceTriggeredAbility(new DoIfCostPaid(new ReturnToHandSourceEffect(), new ManaCostsImpl("{B}")))); + this.addAbility(new DiesSourceTriggeredAbility(new DoIfCostPaid(new ReturnToHandSourceEffect().setText("return it to its owner's hand"), new ManaCostsImpl("{B}")))); // When Relentless Dead dies, you may pay {X}. If you do, return another target Zombie creature card with converted mana cost X from your graveyard to the battlefield. this.addAbility(new DiesSourceTriggeredAbility(new RelentlessDeadEffect())); @@ -57,7 +57,7 @@ class RelentlessDeadEffect extends OneShotEffect { public RelentlessDeadEffect() { super(Outcome.PutCardInPlay); - this.staticText = "you may pay {X}. If you do, return another target Zombie creature card with converted mana cost X from your graveyard to the battlefield"; + this.staticText = "you may pay {X}. If you do, return another target Zombie creature card with mana value X from your graveyard to the battlefield"; } public RelentlessDeadEffect(final RelentlessDeadEffect effect) { @@ -76,9 +76,9 @@ public boolean apply(Game game, Ability source) { if (controller.chooseUse(Outcome.Benefit, "Do you want to pay {X} to return zombie?", source, game)) { int payCount = ManaUtil.playerPaysXGenericMana(true, "Relentless Dead", controller, source, game); // can be 0 - FilterCard filter = new FilterCard("Another target Zombie card with converted mana cost {" + payCount + "}"); + FilterCard filter = new FilterCard("Another target Zombie card with mana value {" + payCount + "}"); filter.add(SubType.ZOMBIE.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, payCount)); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, payCount)); filter.add(new AnotherCardPredicate()); TargetCardInYourGraveyard target = new TargetCardInYourGraveyard(filter); if (controller.chooseTarget(outcome, target, source, game)) { diff --git a/Mage.Sets/src/mage/cards/r/RelicOfProgenitus.java b/Mage.Sets/src/mage/cards/r/RelicOfProgenitus.java index c7c1a7e6f3de..9998bdd1d2e6 100644 --- a/Mage.Sets/src/mage/cards/r/RelicOfProgenitus.java +++ b/Mage.Sets/src/mage/cards/r/RelicOfProgenitus.java @@ -17,7 +17,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; diff --git a/Mage.Sets/src/mage/cards/r/RelicSeeker.java b/Mage.Sets/src/mage/cards/r/RelicSeeker.java index 9c542affb7ee..67fd2daef886 100644 --- a/Mage.Sets/src/mage/cards/r/RelicSeeker.java +++ b/Mage.Sets/src/mage/cards/r/RelicSeeker.java @@ -19,7 +19,7 @@ */ public final class RelicSeeker extends CardImpl { - private static final FilterCard filter = new FilterCard("Equipment card"); + private static final FilterCard filter = new FilterCard("an Equipment card"); static { filter.add(CardType.ARTIFACT.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/r/RelicSloth.java b/Mage.Sets/src/mage/cards/r/RelicSloth.java new file mode 100644 index 000000000000..3c47b0e36304 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RelicSloth.java @@ -0,0 +1,40 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.keyword.MenaceAbility; +import mage.abilities.keyword.VigilanceAbility; +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 RelicSloth extends CardImpl { + + public RelicSloth(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{W}"); + + this.subtype.add(SubType.BEAST); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Menace + this.addAbility(new MenaceAbility()); + } + + private RelicSloth(final RelicSloth card) { + super(card); + } + + @Override + public RelicSloth copy() { + return new RelicSloth(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/Remembrance.java b/Mage.Sets/src/mage/cards/r/Remembrance.java index 2a559585c856..f957d3b46a57 100644 --- a/Mage.Sets/src/mage/cards/r/Remembrance.java +++ b/Mage.Sets/src/mage/cards/r/Remembrance.java @@ -91,6 +91,6 @@ public boolean checkTrigger(GameEvent event, Game game) { public String getRule() { return "Whenever a nontoken creature you control dies, " + "you may search your library for a card with the same name as that creature, " + - "reveal it, and put it into your hand. If you do, shuffle your library."; + "reveal it, put it into your hand, then shuffle."; } } diff --git a/Mage.Sets/src/mage/cards/r/RendingVines.java b/Mage.Sets/src/mage/cards/r/RendingVines.java index f11e269b710d..20bd980a0783 100644 --- a/Mage.Sets/src/mage/cards/r/RendingVines.java +++ b/Mage.Sets/src/mage/cards/r/RendingVines.java @@ -32,7 +32,7 @@ public RendingVines(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); // Draw a card. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); } private RendingVines(final RendingVines card) { @@ -49,7 +49,7 @@ class RendingVinesEffect extends OneShotEffect { public RendingVinesEffect() { super(Outcome.DestroyPermanent); - this.staticText = "Destroy target artifact or enchantment if its converted mana cost is less than or equal to the number of cards in your hand"; + this.staticText = "Destroy target artifact or enchantment if its mana value is less than or equal to the number of cards in your hand"; } public RendingVinesEffect(final RendingVinesEffect effect) { @@ -66,7 +66,7 @@ public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (controller != null) { - if (permanent.getConvertedManaCost() <= controller.getHand().size()) { + if (permanent.getManaValue() <= controller.getHand().size()) { return permanent.destroy(source, game, false); } } diff --git a/Mage.Sets/src/mage/cards/r/RenegadeKrasis.java b/Mage.Sets/src/mage/cards/r/RenegadeKrasis.java index 817e585bbb26..bf60d72cca16 100644 --- a/Mage.Sets/src/mage/cards/r/RenegadeKrasis.java +++ b/Mage.Sets/src/mage/cards/r/RenegadeKrasis.java @@ -13,10 +13,9 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; public final class RenegadeKrasis extends CardImpl { diff --git a/Mage.Sets/src/mage/cards/r/RenegadeRallier.java b/Mage.Sets/src/mage/cards/r/RenegadeRallier.java index 6fb75d765a71..f3a0bfc3595a 100644 --- a/Mage.Sets/src/mage/cards/r/RenegadeRallier.java +++ b/Mage.Sets/src/mage/cards/r/RenegadeRallier.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.constants.ComparisonType; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInYourGraveyard; import mage.watchers.common.RevoltWatcher; @@ -25,10 +25,10 @@ */ public final class RenegadeRallier extends CardImpl { - private static final FilterPermanentCard filter = new FilterPermanentCard("permanent card with converted mana cost 2 or less from your graveyard"); + private static final FilterPermanentCard filter = new FilterPermanentCard("permanent card with mana value 2 or less from your graveyard"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); } public RenegadeRallier(UUID ownerId, CardSetInfo setInfo) { @@ -44,7 +44,7 @@ public RenegadeRallier(UUID ownerId, CardSetInfo setInfo) { Ability ability = new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility( new ReturnFromGraveyardToBattlefieldTargetEffect(), false), RevoltCondition.instance, "Revolt — When {this} enters the battlefield, if a permanent you controlled left" - + " the battlefield this turn, return target permanent card with converted mana cost 2 or less from your graveyard to your battlefield."); + + " the battlefield this turn, return target permanent card with mana value 2 or less from your graveyard to the battlefield."); ability.setAbilityWord(AbilityWord.REVOLT); ability.addTarget(new TargetCardInYourGraveyard(filter)); ability.addWatcher(new RevoltWatcher()); diff --git a/Mage.Sets/src/mage/cards/r/Renewal.java b/Mage.Sets/src/mage/cards/r/Renewal.java index 495a2e36a52e..a0900a49d8dc 100644 --- a/Mage.Sets/src/mage/cards/r/Renewal.java +++ b/Mage.Sets/src/mage/cards/r/Renewal.java @@ -32,7 +32,7 @@ public Renewal(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND))); // Draw a card at the beginning of the next turn's upkeep. - this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextUpkeepDelayedTriggeredAbility(new DrawCardSourceControllerEffect(1)), false)); + this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextUpkeepDelayedTriggeredAbility(new DrawCardSourceControllerEffect(1)), false).concatBy("
")); } private Renewal(final Renewal card) { diff --git a/Mage.Sets/src/mage/cards/r/RenownedWeaponsmith.java b/Mage.Sets/src/mage/cards/r/RenownedWeaponsmith.java index cfbdbf5330d9..1a7ef3fd54dd 100644 --- a/Mage.Sets/src/mage/cards/r/RenownedWeaponsmith.java +++ b/Mage.Sets/src/mage/cards/r/RenownedWeaponsmith.java @@ -100,7 +100,7 @@ class RenownedWeaponsmithEffect extends OneShotEffect { public RenownedWeaponsmithEffect() { super(Outcome.DrawCard); - staticText = "Search your library for a card named Heart-Piercer Bow or Vial of Dragonfire, reveal it, put it into your hand, then shuffle your library"; + staticText = "Search your library for a card named Heart-Piercer Bow or Vial of Dragonfire, reveal it, put it into your hand, then shuffle"; } public RenownedWeaponsmithEffect(final RenownedWeaponsmithEffect effect) { diff --git a/Mage.Sets/src/mage/cards/r/Repeal.java b/Mage.Sets/src/mage/cards/r/Repeal.java index 097bf7c2c167..341689d768df 100644 --- a/Mage.Sets/src/mage/cards/r/Repeal.java +++ b/Mage.Sets/src/mage/cards/r/Repeal.java @@ -9,7 +9,7 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.filter.common.FilterNonlandPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.target.TargetPermanent; import mage.target.common.TargetNonlandPermanent; @@ -26,11 +26,11 @@ public Repeal(UUID ownerId, CardSetInfo setInfo) { // Return target nonland permanent with converted mana cost X to its owner's hand. this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(new FilterNonlandPermanent("nonland permanent with converted mana cost X"))); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterNonlandPermanent("nonland permanent with mana value X"))); this.getSpellAbility().setTargetAdjuster(RepealAdjuster.instance); // Draw a card. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); } private Repeal(final Repeal card) { @@ -50,8 +50,8 @@ enum RepealAdjuster implements TargetAdjuster { public void adjustTargets(Ability ability, Game game) { ability.getTargets().clear(); int xValue = ability.getManaCostsToPay().getX(); - FilterNonlandPermanent filter = new FilterNonlandPermanent("nonland permanent with converted mana cost " + xValue); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, xValue)); + FilterNonlandPermanent filter = new FilterNonlandPermanent("nonland permanent with mana value " + xValue); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, xValue)); ability.addTarget(new TargetNonlandPermanent(filter)); } } diff --git a/Mage.Sets/src/mage/cards/r/RepeatedReverberation.java b/Mage.Sets/src/mage/cards/r/RepeatedReverberation.java index fe7f5b469ef1..12c0ea3a1db8 100644 --- a/Mage.Sets/src/mage/cards/r/RepeatedReverberation.java +++ b/Mage.Sets/src/mage/cards/r/RepeatedReverberation.java @@ -132,7 +132,6 @@ public boolean apply(Game game, Ability source) { return false; } stackAbility.createCopyOnStack(game, source, source.getControllerId(), true, 2); - game.informPlayers(sourcePermanent.getIdName() + ": " + controller.getLogName() + " copied loyalty ability twice"); return true; } diff --git a/Mage.Sets/src/mage/cards/r/Repercussion.java b/Mage.Sets/src/mage/cards/r/Repercussion.java index dbb49d2798ee..1f3eff928d86 100644 --- a/Mage.Sets/src/mage/cards/r/Repercussion.java +++ b/Mage.Sets/src/mage/cards/r/Repercussion.java @@ -55,15 +55,17 @@ public RepercussionTriggeredAbility(final RepercussionTriggeredAbility ability) @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { - for (Effect effect : getEffects()) { - effect.setValue(PLAYER_DAMAGE_AMOUNT_KEY, event.getAmount()); - effect.setValue(TRIGGERING_CREATURE_KEY, new MageObjectReference(event.getTargetId(), game)); + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent == null || !permanent.isCreature()) { + return false; } + getEffects().setValue(PLAYER_DAMAGE_AMOUNT_KEY, event.getAmount()); + getEffects().setValue(TRIGGERING_CREATURE_KEY, new MageObjectReference(event.getTargetId(), game)); return true; } diff --git a/Mage.Sets/src/mage/cards/r/ReplicationTechnique.java b/Mage.Sets/src/mage/cards/r/ReplicationTechnique.java new file mode 100644 index 000000000000..c5ccc8a1d8fc --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ReplicationTechnique.java @@ -0,0 +1,36 @@ +package mage.cards.r; + +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.keyword.DemonstrateAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ReplicationTechnique extends CardImpl { + + public ReplicationTechnique(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{U}"); + + // Demonstrate + this.addAbility(new DemonstrateAbility()); + + // Create a token that's a copy of target permanent you control. + this.getSpellAbility().addEffect(new CreateTokenCopyTargetEffect()); + this.getSpellAbility().addTarget(new TargetControlledPermanent()); + } + + private ReplicationTechnique(final ReplicationTechnique card) { + super(card); + } + + @Override + public ReplicationTechnique copy() { + return new ReplicationTechnique(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RequiemAngel.java b/Mage.Sets/src/mage/cards/r/RequiemAngel.java index 788cbb9cfb4c..825c031b53c9 100644 --- a/Mage.Sets/src/mage/cards/r/RequiemAngel.java +++ b/Mage.Sets/src/mage/cards/r/RequiemAngel.java @@ -11,7 +11,7 @@ import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.SpiritWhiteToken; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/r/Reroute.java b/Mage.Sets/src/mage/cards/r/Reroute.java index 12dda9cbed2e..64d76719cb91 100644 --- a/Mage.Sets/src/mage/cards/r/Reroute.java +++ b/Mage.Sets/src/mage/cards/r/Reroute.java @@ -8,7 +8,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.FilterStackObject; -import mage.filter.predicate.mageobject.NumberOfTargetsPredicate; +import mage.filter.predicate.other.NumberOfTargetsPredicate; import mage.target.common.TargetActivatedAbility; /** diff --git a/Mage.Sets/src/mage/cards/r/RescueFromTheUnderworld.java b/Mage.Sets/src/mage/cards/r/RescueFromTheUnderworld.java index beb6d5ba2eef..a225bec80ed8 100644 --- a/Mage.Sets/src/mage/cards/r/RescueFromTheUnderworld.java +++ b/Mage.Sets/src/mage/cards/r/RescueFromTheUnderworld.java @@ -64,7 +64,7 @@ public RescueFromTheUnderworld(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new RescueFromTheUnderworldCreateDelayedTriggeredAbilityEffect(new RescueFromTheUnderworldDelayedTriggeredAbility())); Target target = new TargetCardInYourGraveyard(new FilterCreatureCard("creature card in your graveyard")); this.getSpellAbility().addTarget(target); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private RescueFromTheUnderworld(final RescueFromTheUnderworld card) { diff --git a/Mage.Sets/src/mage/cards/r/Resculpt.java b/Mage.Sets/src/mage/cards/r/Resculpt.java new file mode 100644 index 000000000000..c1c90092184e --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/Resculpt.java @@ -0,0 +1,67 @@ +package mage.cards.r; + +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.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.PrismariToken; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Resculpt extends CardImpl { + + public Resculpt(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); + + // Exile target artifact or creature. Its controller creates a 4/4 blue and red Elemental creature token. + this.getSpellAbility().addEffect(new ResculptEffect()); + } + + private Resculpt(final Resculpt card) { + super(card); + } + + @Override + public Resculpt copy() { + return new Resculpt(this); + } +} + +class ResculptEffect extends OneShotEffect { + + ResculptEffect() { + super(Outcome.PutCreatureInPlay); + staticText = "Exile target artifact or creature. " + + "Its controller creates a 4/4 blue and red Elemental creature token"; + } + + private ResculptEffect(final ResculptEffect effect) { + super(effect); + } + + @Override + public ResculptEffect copy() { + return new ResculptEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + Player player = game.getPlayer(permanent.getControllerId()); + player.moveCards(permanent, Zone.EXILED, source, game); + new PrismariToken().putOntoBattlefield(1, game, source, player.getId()); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/r/ResplendentAngel.java b/Mage.Sets/src/mage/cards/r/ResplendentAngel.java index 03622c8c2e24..eef76d1ec910 100644 --- a/Mage.Sets/src/mage/cards/r/ResplendentAngel.java +++ b/Mage.Sets/src/mage/cards/r/ResplendentAngel.java @@ -1,30 +1,26 @@ package mage.cards.r; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.condition.common.YouGainedLifeCondition; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.ControllerGotLifeCount; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; -import mage.constants.SubType; 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.Duration; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.constants.*; import mage.game.permanent.token.AngelVigilanceToken; import mage.watchers.common.PlayerGainedLifeWatcher; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class ResplendentAngel extends CardImpl { @@ -41,23 +37,16 @@ public ResplendentAngel(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each end step, if you gained 5 or more life this turn, create a 4/4 white Angel creature token with flying and vigilance. this.addAbility(new BeginningOfEndStepTriggeredAbility( - Zone.BATTLEFIELD, - new CreateTokenEffect(new AngelVigilanceToken()), - TargetController.ANY, - new YouGainedLifeCondition(ComparisonType.MORE_THAN, 4), - false - ), new PlayerGainedLifeWatcher()); + Zone.BATTLEFIELD, new CreateTokenEffect(new AngelVigilanceToken()), + TargetController.ANY, new YouGainedLifeCondition(ComparisonType.MORE_THAN, 4), false + ).addHint(ControllerGotLifeCount.getHint()), new PlayerGainedLifeWatcher()); // {3}{W}{W}{W}: Until end of turn, Resplendent Angel gets +2/+2 and gains lifelink. - Ability ability = new SimpleActivatedAbility( - new BoostSourceEffect( - 2, 2, Duration.EndOfTurn - ).setText("until end of turn, {this} gets +2/+2"), - new ManaCostsImpl("{3}{W}{W}{W}") - ); + Ability ability = new SimpleActivatedAbility(new BoostSourceEffect( + 2, 2, Duration.EndOfTurn + ).setText("until end of turn, {this} gets +2/+2"), new ManaCostsImpl("{3}{W}{W}{W}")); ability.addEffect(new GainAbilitySourceEffect( - LifelinkAbility.getInstance(), - Duration.EndOfTurn + LifelinkAbility.getInstance(), Duration.EndOfTurn ).setText("and gains lifelink")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/ResplendentMarshal.java b/Mage.Sets/src/mage/cards/r/ResplendentMarshal.java index a025fee1a8af..29b5272f92c4 100644 --- a/Mage.Sets/src/mage/cards/r/ResplendentMarshal.java +++ b/Mage.Sets/src/mage/cards/r/ResplendentMarshal.java @@ -19,7 +19,7 @@ import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/r/Restock.java b/Mage.Sets/src/mage/cards/r/Restock.java index 52ce844d28c6..ff61bd7043e1 100644 --- a/Mage.Sets/src/mage/cards/r/Restock.java +++ b/Mage.Sets/src/mage/cards/r/Restock.java @@ -25,7 +25,7 @@ public Restock(UUID ownerId, CardSetInfo setInfo) { effect.setText("Return two target cards from your graveyard to your hand"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(2, StaticFilters.FILTER_CARD_FROM_YOUR_GRAVEYARD)); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private Restock(final Restock card) { diff --git a/Mage.Sets/src/mage/cards/r/RestoreThePeace.java b/Mage.Sets/src/mage/cards/r/RestoreThePeace.java index f5da33b94771..051ed9835c7d 100644 --- a/Mage.Sets/src/mage/cards/r/RestoreThePeace.java +++ b/Mage.Sets/src/mage/cards/r/RestoreThePeace.java @@ -1,31 +1,36 @@ - package mage.cards.r; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ReturnToHandFromBattlefieldAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.watchers.common.SourceDidDamageWatcher; +import java.util.UUID; + /** * @author LevelX2 */ public final class RestoreThePeace extends CardImpl { + private static final FilterPermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(RestoreThePeacePredicate.instance); + } + public RestoreThePeace(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}{U}"); - // Return each creature that dealt damage this turn to its owner's hand. - this.getSpellAbility().addEffect(new RestoreThePeaceEffect()); + this.getSpellAbility().addEffect(new ReturnToHandFromBattlefieldAllEffect(filter) + .setText("return each creature that dealt damage this turn to its owner's hand")); this.getSpellAbility().addWatcher(new SourceDidDamageWatcher()); - } private RestoreThePeace(final RestoreThePeace card) { @@ -38,34 +43,12 @@ public RestoreThePeace copy() { } } -class RestoreThePeaceEffect extends OneShotEffect { - - public RestoreThePeaceEffect() { - super(Outcome.ReturnToHand); - this.staticText = "Return each creature that dealt damage this turn to its owner's hand"; - } - - public RestoreThePeaceEffect(final RestoreThePeaceEffect effect) { - super(effect); - } - - @Override - public RestoreThePeaceEffect copy() { - return new RestoreThePeaceEffect(this); - } +enum RestoreThePeacePredicate implements Predicate { + instance; @Override - public boolean apply(Game game, Ability source) { + public boolean apply(Permanent input, Game game) { SourceDidDamageWatcher watcher = game.getState().getWatcher(SourceDidDamageWatcher.class); - if (watcher != null) { - for (UUID permId : watcher.damageSources) { - Permanent perm = game.getPermanent(permId); - if (perm != null) { - perm.moveToZone(Zone.HAND, source, game, true); - } - } - return true; - } - return false; + return watcher != null && watcher.checkSource(input, game); } } diff --git a/Mage.Sets/src/mage/cards/r/Retaliate.java b/Mage.Sets/src/mage/cards/r/Retaliate.java index 9ac9c79b18f9..6f574bfb0310 100644 --- a/Mage.Sets/src/mage/cards/r/Retaliate.java +++ b/Mage.Sets/src/mage/cards/r/Retaliate.java @@ -7,7 +7,7 @@ import mage.constants.CardType; import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.DamagedPlayerThisTurnPredicate; +import mage.filter.predicate.other.DamagedPlayerThisTurnPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/r/Rethink.java b/Mage.Sets/src/mage/cards/r/Rethink.java index c1cb49241f44..a750eda58f16 100644 --- a/Mage.Sets/src/mage/cards/r/Rethink.java +++ b/Mage.Sets/src/mage/cards/r/Rethink.java @@ -42,7 +42,7 @@ class RethinkEffect extends OneShotEffect { RethinkEffect() { super(Outcome.Detriment); - this.staticText = "Counter target spell unless that player pays {X}, where X is its converted mana cost"; + this.staticText = "Counter target spell unless its controller pays {X}, where X is its mana value"; } RethinkEffect(final RethinkEffect effect) { @@ -60,7 +60,7 @@ public boolean apply(Game game, Ability source) { if (spell != null) { Player player = game.getPlayer(spell.getControllerId()); if (player != null) { - Cost cost = ManaUtil.createManaCost(spell.getConvertedManaCost(), true); + Cost cost = ManaUtil.createManaCost(spell.getManaValue(), true); if (!cost.pay(source, game, source, player.getId(), false)) { game.getStack().counter(spell.getId(), source, game); } diff --git a/Mage.Sets/src/mage/cards/r/RetrieverPhoenix.java b/Mage.Sets/src/mage/cards/r/RetrieverPhoenix.java new file mode 100644 index 000000000000..518e2e28397f --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RetrieverPhoenix.java @@ -0,0 +1,98 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.CastFromEverywhereSourceCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.LearnEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.HasteAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RetrieverPhoenix extends CardImpl { + + public RetrieverPhoenix(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.PHOENIX); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // When Retriever Phoenix enters the battlefield, if you cast it, learn. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new EntersBattlefieldTriggeredAbility(new LearnEffect()), CastFromEverywhereSourceCondition.instance, + "When {this} enters the battlefield, if you cast it, " + LearnEffect.getDefaultText() + )); + + // As long as Retriever Phoenix is in your graveyard, if you would learn, you may instead return Retriever Phoenix to the battlefield. + this.addAbility(new SimpleStaticAbility(Zone.GRAVEYARD, new RetrieverPhoenixEffect())); + } + + private RetrieverPhoenix(final RetrieverPhoenix card) { + super(card); + } + + @Override + public RetrieverPhoenix copy() { + return new RetrieverPhoenix(this); + } +} + +class RetrieverPhoenixEffect extends ReplacementEffectImpl { + + RetrieverPhoenixEffect() { + super(Duration.WhileInGraveyard, Outcome.PutCreatureInPlay); + staticText = "as long as {this} is in your graveyard, if you would learn, " + + "you may instead return {this} to the battlefield"; + } + + private RetrieverPhoenixEffect(final RetrieverPhoenixEffect effect) { + super(effect); + } + + @Override + public RetrieverPhoenixEffect copy() { + return new RetrieverPhoenixEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + MageObject sourceObject = source.getSourceObjectIfItStillExists(game); + Player player = game.getPlayer(source.getControllerId()); + return sourceObject instanceof Card + && player != null + && player.chooseUse(outcome, "Return " + sourceObject.getName() + " to the battlefield instead of learning?", source, game) + && player.moveCards((Card) sourceObject, Zone.BATTLEFIELD, source, game); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.LEARN; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.isControlledBy(event.getPlayerId()); + } +} diff --git a/Mage.Sets/src/mage/cards/r/ReturnToTheRanks.java b/Mage.Sets/src/mage/cards/r/ReturnToTheRanks.java index 90802fa7a79e..a2189dbc05dd 100644 --- a/Mage.Sets/src/mage/cards/r/ReturnToTheRanks.java +++ b/Mage.Sets/src/mage/cards/r/ReturnToTheRanks.java @@ -10,7 +10,7 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetadjustment.TargetAdjuster; @@ -30,7 +30,7 @@ public ReturnToTheRanks(UUID ownerId, CardSetInfo setInfo) { // Return X target creature cards with converted mana cost 2 or less from your graveyard to the battlefield. Effect effect = new ReturnFromGraveyardToBattlefieldTargetEffect(); - effect.setText("Return X target creature cards with converted mana cost 2 or less from your graveyard to the battlefield"); + effect.setText("Return X target creature cards with mana value 2 or less from your graveyard to the battlefield"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().setTargetAdjuster(ReturnToTheRanksAdjuster.instance); } @@ -47,10 +47,10 @@ public ReturnToTheRanks copy() { enum ReturnToTheRanksAdjuster implements TargetAdjuster { instance; - private static final FilterCreatureCard filter = new FilterCreatureCard("creature cards with converted mana cost 2 or less from your graveyard"); + private static final FilterCreatureCard filter = new FilterCreatureCard("creature cards with mana value 2 or less from your graveyard"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); } @Override diff --git a/Mage.Sets/src/mage/cards/r/ReturnedPastcaller.java b/Mage.Sets/src/mage/cards/r/ReturnedPastcaller.java new file mode 100644 index 000000000000..d95a6303fd05 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ReturnedPastcaller.java @@ -0,0 +1,58 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ReturnedPastcaller extends CardImpl { + + private static final FilterCard filter = new FilterCard("Spirit, instant, or sorcery card from your graveyard"); + + static { + filter.add(Predicates.or( + SubType.SPIRIT.getPredicate(), + CardType.INSTANT.getPredicate(), + CardType.SORCERY.getPredicate() + )); + } + + public ReturnedPastcaller(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R/W}{W}"); + + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(4); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Returned Pastcaller enters the battlefield, return target Spirit, instant, or sorcery card from your graveyard to your hand. + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect()); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + } + + private ReturnedPastcaller(final ReturnedPastcaller card) { + super(card); + } + + @Override + public ReturnedPastcaller copy() { + return new ReturnedPastcaller(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RevealingWind.java b/Mage.Sets/src/mage/cards/r/RevealingWind.java index 3707801030da..a8be10d36c38 100644 --- a/Mage.Sets/src/mage/cards/r/RevealingWind.java +++ b/Mage.Sets/src/mage/cards/r/RevealingWind.java @@ -16,7 +16,7 @@ import mage.constants.Outcome; import mage.filter.common.FilterAttackingOrBlockingCreature; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.other.FaceDownPredicate; +import mage.filter.predicate.card.FaceDownPredicate; import mage.game.Game; import mage.players.Player; import mage.target.Target; diff --git a/Mage.Sets/src/mage/cards/r/Reveillark.java b/Mage.Sets/src/mage/cards/r/Reveillark.java index 373a8a7b629c..e7dc662a6d64 100644 --- a/Mage.Sets/src/mage/cards/r/Reveillark.java +++ b/Mage.Sets/src/mage/cards/r/Reveillark.java @@ -38,7 +38,7 @@ public Reveillark(UUID ownerId, CardSetInfo setInfo) { // Flying this.addAbility(FlyingAbility.getInstance()); // When Reveillark leaves the battlefield, return up to two target creature cards with power 2 or less from your graveyard to the battlefield. - Ability ability = new LeavesBattlefieldTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(false, false), false); + Ability ability = new LeavesBattlefieldTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(), false); ability.addTarget(new TargetCardInYourGraveyard(0,2,filter)); this.addAbility(ability); // Evoke {5}{W} diff --git a/Mage.Sets/src/mage/cards/r/Reverberation.java b/Mage.Sets/src/mage/cards/r/Reverberation.java index ce74ee4079d0..4a59ccffda91 100644 --- a/Mage.Sets/src/mage/cards/r/Reverberation.java +++ b/Mage.Sets/src/mage/cards/r/Reverberation.java @@ -64,8 +64,7 @@ public ReverberationEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE || - event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER || + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT || event.getType() == GameEvent.EventType.DAMAGE_PLAYER; } diff --git a/Mage.Sets/src/mage/cards/r/Revitalize.java b/Mage.Sets/src/mage/cards/r/Revitalize.java index 03e60a1dae41..85fd139324ea 100644 --- a/Mage.Sets/src/mage/cards/r/Revitalize.java +++ b/Mage.Sets/src/mage/cards/r/Revitalize.java @@ -20,7 +20,7 @@ public Revitalize(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new GainLifeEffect(3)); // Draw a card. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); } private Revitalize(final Revitalize card) { diff --git a/Mage.Sets/src/mage/cards/r/RevivalExperiment.java b/Mage.Sets/src/mage/cards/r/RevivalExperiment.java new file mode 100644 index 000000000000..86c35f7b9ca8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RevivalExperiment.java @@ -0,0 +1,126 @@ +package mage.cards.r; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.common.CardTypeAssignment; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileSpellEffect; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.Arrays; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RevivalExperiment extends CardImpl { + + public RevivalExperiment(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}{G}"); + + // For each permanent type, return up to one card of that type from your graveyard to the battlefield. You lose 3 life for each card returned this way. Exile Revival Experiment. + this.getSpellAbility().addEffect(new RevivalExperimentEffect()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); + } + + private RevivalExperiment(final RevivalExperiment card) { + super(card); + } + + @Override + public RevivalExperiment copy() { + return new RevivalExperiment(this); + } +} + +class RevivalExperimentEffect extends OneShotEffect { + + RevivalExperimentEffect() { + super(Outcome.PutCreatureInPlay); + staticText = "for each permanent type, return up to one card of that type from your graveyard " + + "to the battlefield. You lose 3 life for each card returned this way."; + } + + private RevivalExperimentEffect(final RevivalExperimentEffect effect) { + super(effect); + } + + @Override + public RevivalExperimentEffect copy() { + return new RevivalExperimentEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + RevivalExperimentTarget target = new RevivalExperimentTarget(); + player.choose(outcome, target, source.getSourceId(), game); + Cards cards = new CardsImpl(target.getTargets()); + player.moveCards(cards, Zone.BATTLEFIELD, source, game); + int toBattlefield = cards + .stream() + .map(game.getState()::getZone) + .filter(Zone.EXILED::equals) + .mapToInt(x -> 1) + .sum(); + player.loseLife(3 * toBattlefield, game, source, false); + return true; + } +} + +class RevivalExperimentTarget extends TargetCardInYourGraveyard { + + private static final CardTypeAssignment cardTypeAssigner = new CardTypeAssignment( + Arrays.stream(CardType.values()) + .filter(CardType::isPermanentType) + .toArray(CardType[]::new) + ); + + RevivalExperimentTarget() { + super(0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD_PERMANENT, true); + } + + private RevivalExperimentTarget(final RevivalExperimentTarget target) { + super(target); + } + + @Override + public RevivalExperimentTarget copy() { + return new RevivalExperimentTarget(this); + } + + @Override + public boolean canTarget(UUID playerId, UUID id, Ability ability, Game game) { + if (!super.canTarget(playerId, id, ability, game)) { + return false; + } + Card card = game.getCard(id); + if (card == null) { + return false; + } + if (this.getTargets().isEmpty()) { + return true; + } + Cards cards = new CardsImpl(this.getTargets()); + cards.add(card); + return cardTypeAssigner.getRoleCount(cards, game) >= cards.size(); + } + + + @Override + public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { + Set possibleTargets = super.possibleTargets(sourceId, sourceControllerId, game); + possibleTargets.removeIf(uuid -> !this.canTarget(sourceControllerId, uuid, null, game)); + return possibleTargets; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RevivalRevenge.java b/Mage.Sets/src/mage/cards/r/RevivalRevenge.java index b2c530e8cc0e..c17ae5534cac 100644 --- a/Mage.Sets/src/mage/cards/r/RevivalRevenge.java +++ b/Mage.Sets/src/mage/cards/r/RevivalRevenge.java @@ -11,7 +11,7 @@ import mage.constants.SpellAbilityType; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; @@ -25,10 +25,10 @@ public final class RevivalRevenge extends SplitCard { private static final FilterCard filter - = new FilterCreatureCard("creature card with converted mana cost 3 or less from your graveyard"); + = new FilterCreatureCard("creature card with mana value 3 or less from your graveyard"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public RevivalRevenge(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/r/ReviveTheFallen.java b/Mage.Sets/src/mage/cards/r/ReviveTheFallen.java index 3fff388aedc7..3e90df00711c 100644 --- a/Mage.Sets/src/mage/cards/r/ReviveTheFallen.java +++ b/Mage.Sets/src/mage/cards/r/ReviveTheFallen.java @@ -21,7 +21,7 @@ public ReviveTheFallen(UUID ownerId, CardSetInfo setInfo) { // Return target creature card from a graveyard to its owner's hand. this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); - this.getSpellAbility().addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card in a graveyard"))); + this.getSpellAbility().addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card from a graveyard"))); // Clash with an opponent. If you win, return Revive the Fallen to its owner's hand. this.getSpellAbility().addEffect(ClashWinReturnToHandSpellEffect.getInstance()); } diff --git a/Mage.Sets/src/mage/cards/r/RevivingVapors.java b/Mage.Sets/src/mage/cards/r/RevivingVapors.java index 112fac99b409..8acd1dcc2645 100644 --- a/Mage.Sets/src/mage/cards/r/RevivingVapors.java +++ b/Mage.Sets/src/mage/cards/r/RevivingVapors.java @@ -40,7 +40,7 @@ class RevivingVaporsEffect extends OneShotEffect { public RevivingVaporsEffect() { super(Outcome.Benefit); - staticText = "Reveal the top three cards of your library and put one of them into your hand. You gain life equal to that card's converted mana cost. Put all other cards revealed this way into your graveyard"; + staticText = "Reveal the top three cards of your library and put one of them into your hand. You gain life equal to that card's mana value. Put all other cards revealed this way into your graveyard"; } public RevivingVaporsEffect(final RevivingVaporsEffect effect) { @@ -75,7 +75,7 @@ public boolean apply(Game game, Ability source) { controller.moveCards(card, Zone.HAND, source, game); // You gain life equal to that card's converted mana cost - controller.gainLife(card.getConvertedManaCost(), game, source); + controller.gainLife(card.getManaValue(), game, source); } // Put all other cards revealed this way into your graveyard diff --git a/Mage.Sets/src/mage/cards/r/RewardTheFaithful.java b/Mage.Sets/src/mage/cards/r/RewardTheFaithful.java index c067c1c85671..dd925af74140 100644 --- a/Mage.Sets/src/mage/cards/r/RewardTheFaithful.java +++ b/Mage.Sets/src/mage/cards/r/RewardTheFaithful.java @@ -2,7 +2,7 @@ package mage.cards.r; import java.util.UUID; -import mage.abilities.dynamicvalue.common.HighestConvertedManaCostValue; +import mage.abilities.dynamicvalue.common.HighestManaValueCount; import mage.abilities.effects.common.GainLifeTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -19,8 +19,8 @@ public RewardTheFaithful(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}"); // Any number of target players each gain life equal to the highest converted mana cost among permanents you control. - this.getSpellAbility().addEffect(new GainLifeTargetEffect(new HighestConvertedManaCostValue()) - .setText("Any number of target players each gain life equal to the highest converted mana cost among permanents you control.")); + this.getSpellAbility().addEffect(new GainLifeTargetEffect(new HighestManaValueCount()) + .setText("Any number of target players each gain life equal to the highest mana value among permanents you control.")); this.getSpellAbility().addTarget(new TargetPlayer(0, Integer.MAX_VALUE, false)); } diff --git a/Mage.Sets/src/mage/cards/r/Reweave.java b/Mage.Sets/src/mage/cards/r/Reweave.java index be0cf54ec1c2..9ee0a5262179 100644 --- a/Mage.Sets/src/mage/cards/r/Reweave.java +++ b/Mage.Sets/src/mage/cards/r/Reweave.java @@ -54,7 +54,7 @@ class ReweaveEffect extends OneShotEffect { public ReweaveEffect() { super(Outcome.Detriment); - this.staticText = "Target permanent's controller sacrifices it. If they do, that player reveals cards from the top of their library until they reveal a permanent card that shares a card type with the sacrificed permanent, puts that card onto the battlefield, then shuffles their library"; + this.staticText = "Target permanent's controller sacrifices it. If the player does, they reveal cards from the top of their library until they reveal a permanent card that shares a card type with the sacrificed permanent, put that card onto the battlefield, then shuffle"; } public ReweaveEffect(final ReweaveEffect effect) { diff --git a/Mage.Sets/src/mage/cards/r/Rey.java b/Mage.Sets/src/mage/cards/r/Rey.java index 19b9183566b4..33a70ca64635 100644 --- a/Mage.Sets/src/mage/cards/r/Rey.java +++ b/Mage.Sets/src/mage/cards/r/Rey.java @@ -60,7 +60,7 @@ class ReyEffect extends OneShotEffect { public ReyEffect() { super(Outcome.Detriment); - staticText = "reveal the top card of target player's library. You gain life equal to that card's converted mana cost"; + staticText = "reveal the top card of target player's library. You gain life equal to that card's mana value"; } public ReyEffect(final ReyEffect effect) { @@ -86,7 +86,7 @@ public boolean apply(Game game, Ability source) { // You gain life equal to that card's converted mana cost. if (topCard != null) { - controller.gainLife(topCard.getConvertedManaCost(), game, source); + controller.gainLife(topCard.getManaValue(), game, source); } } diff --git a/Mage.Sets/src/mage/cards/r/RhonasTheIndomitable.java b/Mage.Sets/src/mage/cards/r/RhonasTheIndomitable.java index 6fb88e17f85b..a815713ed79d 100644 --- a/Mage.Sets/src/mage/cards/r/RhonasTheIndomitable.java +++ b/Mage.Sets/src/mage/cards/r/RhonasTheIndomitable.java @@ -18,7 +18,7 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/r/RhysticCave.java b/Mage.Sets/src/mage/cards/r/RhysticCave.java index 670904d5f7d4..ea347ae91b44 100644 --- a/Mage.Sets/src/mage/cards/r/RhysticCave.java +++ b/Mage.Sets/src/mage/cards/r/RhysticCave.java @@ -77,7 +77,7 @@ public RhysticCaveManaAbility copy() { @Override public String getRule() { - return super.getRule() + " Activate this ability only any time you could cast an instant."; + return super.getRule() + " Activate only as an instant."; } } @@ -85,7 +85,7 @@ class RhysticCaveManaEffect extends ManaEffect { public RhysticCaveManaEffect() { super(); - this.staticText = "Choose a color. Add one mana of that color "; + this.staticText = "Choose a color. Add one mana of that color"; } public RhysticCaveManaEffect(final RhysticCaveManaEffect effect) { diff --git a/Mage.Sets/src/mage/cards/r/RhysticStudy.java b/Mage.Sets/src/mage/cards/r/RhysticStudy.java index bb75974a2b14..53cea2e790d5 100644 --- a/Mage.Sets/src/mage/cards/r/RhysticStudy.java +++ b/Mage.Sets/src/mage/cards/r/RhysticStudy.java @@ -64,8 +64,7 @@ public boolean apply(Game game, Ability source) { if (controller != null && opponent != null && sourceObject != null) { if (controller.chooseUse(Outcome.DrawCard, "Draw a card (" + sourceObject.getLogName() + ')', source, game)) { Cost cost = ManaUtil.createManaCost(1, false); - String message = "Would you like to pay {1} to prevent the opponent to draw a card?"; - if (opponent.chooseUse(Outcome.Benefit, message, source, game) + if (opponent.chooseUse(Outcome.Benefit, "Pay {1}?", source, game) && cost.pay(source, game, source, opponent.getId(), false, null)) { return true; } diff --git a/Mage.Sets/src/mage/cards/r/RhysticTutor.java b/Mage.Sets/src/mage/cards/r/RhysticTutor.java index 2c1d6be87124..b755fccd3c4b 100644 --- a/Mage.Sets/src/mage/cards/r/RhysticTutor.java +++ b/Mage.Sets/src/mage/cards/r/RhysticTutor.java @@ -22,7 +22,7 @@ public RhysticTutor(UUID ownerId, CardSetInfo setInfo) { // Unless any player pays {2}, search your library for a card, put that card into your hand, then shuffle your library. Effect effect = new DoUnlessAnyPlayerPaysEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary()), new ManaCostsImpl("{2}")); - effect.setText("Unless any player pays {2}, search your library for a card, put that card into your hand, then shuffle your library"); + effect.setText("Unless any player pays {2}, search your library for a card, put that card into your hand, then shuffle"); this.getSpellAbility().addEffect(effect); } diff --git a/Mage.Sets/src/mage/cards/r/RhythmicWaterVortex.java b/Mage.Sets/src/mage/cards/r/RhythmicWaterVortex.java index 8b9ead0b1a0b..18c722bc35b7 100644 --- a/Mage.Sets/src/mage/cards/r/RhythmicWaterVortex.java +++ b/Mage.Sets/src/mage/cards/r/RhythmicWaterVortex.java @@ -30,7 +30,7 @@ public RhythmicWaterVortex(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 2)); // Search your library and/or graveyard for a card named Mu Yanling, reveal it, and put it into your hand. If you searched your library this way, shuffle it. - this.getSpellAbility().addEffect(new SearchLibraryGraveyardPutInHandEffect(filter)); + this.getSpellAbility().addEffect(new SearchLibraryGraveyardPutInHandEffect(filter).concatBy("
")); } private RhythmicWaterVortex(final RhythmicWaterVortex card) { diff --git a/Mage.Sets/src/mage/cards/r/RickSteadfastLeader.java b/Mage.Sets/src/mage/cards/r/RickSteadfastLeader.java index fa222678d4b0..f7ae7f7d5331 100644 --- a/Mage.Sets/src/mage/cards/r/RickSteadfastLeader.java +++ b/Mage.Sets/src/mage/cards/r/RickSteadfastLeader.java @@ -36,10 +36,10 @@ */ public final class RickSteadfastLeader extends CardImpl { - private static final FilterPermanent filter - = new FilterControlledPermanent(SubType.HUMAN, ""); - private static final FilterCreaturePermanent filter2 + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.HUMAN, ""); + private static final FilterPermanent filter2 + = new FilterControlledPermanent(SubType.HUMAN, ""); private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter2, ComparisonType.MORE_THAN, 3); @@ -60,7 +60,7 @@ public RickSteadfastLeader(UUID ownerId, CardSetInfo setInfo) { // As long as you control four or more Humans, Humans you control get +2/+2. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( - new BoostControlledEffect(2, 2, Duration.WhileOnBattlefield), + new BoostControlledEffect(2, 2, Duration.WhileOnBattlefield, filter), condition, "as long as you control four or more Humans, Humans you control get +2/+2" ))); } diff --git a/Mage.Sets/src/mage/cards/r/Ricochet.java b/Mage.Sets/src/mage/cards/r/Ricochet.java index bba107c53163..d34043ae2885 100644 --- a/Mage.Sets/src/mage/cards/r/Ricochet.java +++ b/Mage.Sets/src/mage/cards/r/Ricochet.java @@ -19,7 +19,7 @@ import mage.filter.FilterSpell; import mage.filter.predicate.ObjectPlayer; import mage.filter.predicate.ObjectPlayerPredicate; -import mage.filter.predicate.mageobject.NumberOfTargetsPredicate; +import mage.filter.predicate.other.NumberOfTargetsPredicate; import mage.game.Game; import mage.game.stack.Spell; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/r/RicochetTrap.java b/Mage.Sets/src/mage/cards/r/RicochetTrap.java index d5abfea9fd4a..91781688ea27 100644 --- a/Mage.Sets/src/mage/cards/r/RicochetTrap.java +++ b/Mage.Sets/src/mage/cards/r/RicochetTrap.java @@ -10,7 +10,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.NumberOfTargetsPredicate; +import mage.filter.predicate.other.NumberOfTargetsPredicate; import mage.game.Game; import mage.game.stack.Spell; import mage.target.TargetSpell; diff --git a/Mage.Sets/src/mage/cards/r/RiddleOfLightning.java b/Mage.Sets/src/mage/cards/r/RiddleOfLightning.java index 43964e4b2467..04e0288b4ec9 100644 --- a/Mage.Sets/src/mage/cards/r/RiddleOfLightning.java +++ b/Mage.Sets/src/mage/cards/r/RiddleOfLightning.java @@ -47,7 +47,7 @@ class RiddleOfLightningEffect extends OneShotEffect { public RiddleOfLightningEffect() { super(Outcome.Damage); - this.staticText = ", then reveal the top card of your library. {this} deals damage equal to that card's converted mana cost to that permanent or player"; + this.staticText = ", then reveal the top card of your library. {this} deals damage equal to that card's mana value to that permanent or player"; } public RiddleOfLightningEffect(final RiddleOfLightningEffect effect) { @@ -69,12 +69,12 @@ public boolean apply(Game game, Ability source) { controller.revealCards(sourceCard.getName(), new CardsImpl(card), game); Permanent targetCreature = game.getPermanent(this.getTargetPointer().getFirst(game, source)); if (targetCreature != null) { - targetCreature.damage(card.getConvertedManaCost(), source.getSourceId(), source, game, false, true); + targetCreature.damage(card.getManaValue(), source.getSourceId(), source, game, false, true); return true; } Player targetPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source)); if (targetPlayer != null) { - targetPlayer.damage(card.getConvertedManaCost(), source.getSourceId(), source, game); + targetPlayer.damage(card.getManaValue(), source.getSourceId(), source, game); return true; } } diff --git a/Mage.Sets/src/mage/cards/r/RidgescaleTusker.java b/Mage.Sets/src/mage/cards/r/RidgescaleTusker.java index fc69ce92f7da..edd78420772b 100644 --- a/Mage.Sets/src/mage/cards/r/RidgescaleTusker.java +++ b/Mage.Sets/src/mage/cards/r/RidgescaleTusker.java @@ -11,7 +11,7 @@ import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/r/RienneAngelOfRebirth.java b/Mage.Sets/src/mage/cards/r/RienneAngelOfRebirth.java index 345da504cf5a..3f69785df43e 100644 --- a/Mage.Sets/src/mage/cards/r/RienneAngelOfRebirth.java +++ b/Mage.Sets/src/mage/cards/r/RienneAngelOfRebirth.java @@ -94,9 +94,7 @@ public boolean checkTrigger(GameEvent event, Game game) { } Permanent permanent = game.getPermanentOrLKIBattlefield(zEvent.getTarget().getId()); - if (permanent != null - && zEvent.getToZone() == Zone.GRAVEYARD - && zEvent.getFromZone() == Zone.BATTLEFIELD + if (permanent != null && zEvent.isDiesEvent() && permanent.isCreature() && permanent.getColor(game).isMulticolored() && permanent.isControlledBy(this.controllerId)) { diff --git a/Mage.Sets/src/mage/cards/r/RiftmarkedKnight.java b/Mage.Sets/src/mage/cards/r/RiftmarkedKnight.java index 0908cc998ee7..d25418fb1555 100644 --- a/Mage.Sets/src/mage/cards/r/RiftmarkedKnight.java +++ b/Mage.Sets/src/mage/cards/r/RiftmarkedKnight.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.UUID; @@ -39,8 +38,10 @@ public RiftmarkedKnight(UUID ownerId, CardSetInfo setInfo) { // Protection from black; flanking this.addAbility(ProtectionAbility.from(ObjectColor.BLACK)); this.addAbility(new FlankingAbility()); + // Suspend 3-{1}{W}{W} this.addAbility(new SuspendAbility(3, new ManaCostsImpl("{1}{W}{W}"), this)); + // When the last time counter is removed from Riftmarked Knight while it's exiled, put a 2/2 black Knight creature token with flanking, protection from white, and haste onto the battlefield. this.addAbility(new RiftmarkedKnightTriggeredAbility()); } diff --git a/Mage.Sets/src/mage/cards/r/Riftsweeper.java b/Mage.Sets/src/mage/cards/r/Riftsweeper.java index e0262b772507..1526ee73a501 100644 --- a/Mage.Sets/src/mage/cards/r/Riftsweeper.java +++ b/Mage.Sets/src/mage/cards/r/Riftsweeper.java @@ -13,7 +13,7 @@ import mage.constants.Outcome; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; -import mage.filter.predicate.other.FaceDownPredicate; +import mage.filter.predicate.card.FaceDownPredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInExile; diff --git a/Mage.Sets/src/mage/cards/r/RighteousValkyrie.java b/Mage.Sets/src/mage/cards/r/RighteousValkyrie.java index 2e190be49ef6..4b3cb6782e1b 100644 --- a/Mage.Sets/src/mage/cards/r/RighteousValkyrie.java +++ b/Mage.Sets/src/mage/cards/r/RighteousValkyrie.java @@ -18,7 +18,7 @@ import mage.constants.CardType; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/r/RikuOfTwoReflections.java b/Mage.Sets/src/mage/cards/r/RikuOfTwoReflections.java index dacf3385606e..f40932215dea 100644 --- a/Mage.Sets/src/mage/cards/r/RikuOfTwoReflections.java +++ b/Mage.Sets/src/mage/cards/r/RikuOfTwoReflections.java @@ -14,7 +14,7 @@ import mage.filter.FilterSpell; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/r/RimeTender.java b/Mage.Sets/src/mage/cards/r/RimeTender.java index cf8c213c6ca0..a9f5304388bc 100644 --- a/Mage.Sets/src/mage/cards/r/RimeTender.java +++ b/Mage.Sets/src/mage/cards/r/RimeTender.java @@ -11,7 +11,7 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/r/RimehornAurochs.java b/Mage.Sets/src/mage/cards/r/RimehornAurochs.java index 5d3da3bd8723..f878e9c1d864 100644 --- a/Mage.Sets/src/mage/cards/r/RimehornAurochs.java +++ b/Mage.Sets/src/mage/cards/r/RimehornAurochs.java @@ -16,7 +16,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.AttackingPredicate; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/r/RingOfImmortals.java b/Mage.Sets/src/mage/cards/r/RingOfImmortals.java index 37728eb9fa29..4dea052699f9 100644 --- a/Mage.Sets/src/mage/cards/r/RingOfImmortals.java +++ b/Mage.Sets/src/mage/cards/r/RingOfImmortals.java @@ -15,7 +15,7 @@ import mage.filter.FilterSpell; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.other.TargetsPermanentPredicate; +import mage.filter.predicate.mageobject.TargetsPermanentPredicate; import mage.target.TargetSpell; /** diff --git a/Mage.Sets/src/mage/cards/r/RingsOfBrighthearth.java b/Mage.Sets/src/mage/cards/r/RingsOfBrighthearth.java index bb1382cfa9e2..38b19028b035 100644 --- a/Mage.Sets/src/mage/cards/r/RingsOfBrighthearth.java +++ b/Mage.Sets/src/mage/cards/r/RingsOfBrighthearth.java @@ -1,21 +1,17 @@ package mage.cards.r; -import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CopyStackAbilityEffect; +import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; import mage.game.stack.StackAbility; -import mage.players.Player; import java.util.UUID; @@ -44,10 +40,10 @@ public RingsOfBrighthearth copy() { class RingsOfBrighthearthTriggeredAbility extends TriggeredAbilityImpl { RingsOfBrighthearthTriggeredAbility() { - super(Zone.BATTLEFIELD, new RingsOfBrighthearthEffect(), false); + super(Zone.BATTLEFIELD, new DoIfCostPaid(new CopyStackAbilityEffect(), new GenericManaCost(2))); } - RingsOfBrighthearthTriggeredAbility(final RingsOfBrighthearthTriggeredAbility ability) { + private RingsOfBrighthearthTriggeredAbility(final RingsOfBrighthearthTriggeredAbility ability) { super(ability); } @@ -63,60 +59,20 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getPlayerId().equals(getControllerId())) { - StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); - if (stackAbility != null && !(stackAbility.getStackAbility() instanceof ActivatedManaAbilityImpl)) { - Effect effect = this.getEffects().get(0); - effect.setValue("stackAbility", stackAbility); - return true; - } + if (!event.getPlayerId().equals(getControllerId())) { + return false; } - return false; + StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); + if (stackAbility == null || stackAbility.getStackAbility() instanceof ActivatedManaAbilityImpl) { + return false; + } + this.getEffects().setValue("stackAbility", stackAbility); + return true; } @Override public String getRule() { - return "Whenever you activate an ability, if it isn't a mana ability, you may pay {2}. If you do, copy that ability. You may choose new targets for the copy."; - } -} - -class RingsOfBrighthearthEffect extends OneShotEffect { - - RingsOfBrighthearthEffect() { - super(Outcome.Benefit); - this.staticText = ", you may pay {2}. If you do, copy that ability. You may choose new targets for the copy."; - } - - RingsOfBrighthearthEffect(final RingsOfBrighthearthEffect effect) { - super(effect); - } - - @Override - public RingsOfBrighthearthEffect copy() { - return new RingsOfBrighthearthEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - ManaCostsImpl cost = new ManaCostsImpl("{2}"); - if (player != null) { - if (cost.canPay(source, source, player.getId(), game) - && player.chooseUse(Outcome.Benefit, "Pay " + cost.getText() + "? If you do, copy that ability. You may choose new targets for the copy.", source, game)) { - if (cost.pay(source, game, source, source.getControllerId(), false, null)) { - StackAbility ability = (StackAbility) getValue("stackAbility"); - Player controller = game.getPlayer(source.getControllerId()); - Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (ability != null && controller != null && sourcePermanent != null) { - ability.createCopyOnStack(game, source, source.getControllerId(), true); - game.informPlayers(sourcePermanent.getIdName() + ": " + controller.getLogName() + " copied activated ability"); - return true; - } - return false; - } - } - return true; - } - return false; + return "Whenever you activate an ability, if it isn't a mana ability, you may pay {2}. " + + "If you do, copy that ability. You may choose new targets for the copy."; } } diff --git a/Mage.Sets/src/mage/cards/r/Ringskipper.java b/Mage.Sets/src/mage/cards/r/Ringskipper.java index ac7541c74eb2..0d33a2bcc29f 100644 --- a/Mage.Sets/src/mage/cards/r/Ringskipper.java +++ b/Mage.Sets/src/mage/cards/r/Ringskipper.java @@ -1,6 +1,7 @@ package mage.cards.r; import mage.MageInt; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.PutIntoGraveFromBattlefieldSourceTriggeredAbility; import mage.abilities.effects.common.DoIfClashWonEffect; import mage.abilities.effects.common.ReturnToHandSourceEffect; @@ -29,7 +30,7 @@ public Ringskipper(UUID ownerId, CardSetInfo setInfo) { this.addAbility(FlyingAbility.getInstance()); // When {this} is put into graveyard from play, clash with an opponent. If you win return {this} to its owner's hand - this.addAbility(new PutIntoGraveFromBattlefieldSourceTriggeredAbility(new DoIfClashWonEffect(new ReturnToHandSourceEffect()))); + this.addAbility(new DiesSourceTriggeredAbility(new DoIfClashWonEffect(new ReturnToHandSourceEffect(false,true)))); } private Ringskipper(final Ringskipper card) { diff --git a/Mage.Sets/src/mage/cards/r/RionyaFireDancer.java b/Mage.Sets/src/mage/cards/r/RionyaFireDancer.java new file mode 100644 index 000000000000..a9992efb0b8f --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RionyaFireDancer.java @@ -0,0 +1,138 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.hint.Hint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.target.TargetPermanent; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RionyaFireDancer extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("another creature you control"); + + static { + filter.add(AnotherPredicate.instance); + } + + public RionyaFireDancer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // At the beginning of combat on your turn, create X tokens that are copies of another target creature you control, where X is one plus the number of instant and sorcery spells you've cast this turn. They gain haste. Exile them at the beginning of the next end step. + Ability ability = new BeginningOfCombatTriggeredAbility( + new RionyaFireDancerEffect(), TargetController.YOU, false + ); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability.addHint(RionyaFireDancerHint.instance), new RionyaFireDancerWatcher()); + } + + private RionyaFireDancer(final RionyaFireDancer card) { + super(card); + } + + @Override + public RionyaFireDancer copy() { + return new RionyaFireDancer(this); + } +} + +enum RionyaFireDancerHint implements Hint { + instance; + + @Override + public String getText(Game game, Ability ability) { + return "Instants and sorceries you've cast this turn: " + + RionyaFireDancerWatcher.getValue(ability.getControllerId(), game); + } + + @Override + public RionyaFireDancerHint copy() { + return instance; + } +} + +class RionyaFireDancerEffect extends OneShotEffect { + + RionyaFireDancerEffect() { + super(Outcome.Benefit); + staticText = "create X tokens that are copies of another target creature you control, " + + "where X is one plus the number of instant and sorcery spells you've cast this turn. " + + "They gain haste. Exile them at the beginning of the next end step"; + } + + private RionyaFireDancerEffect(final RionyaFireDancerEffect effect) { + super(effect); + } + + @Override + public RionyaFireDancerEffect copy() { + return new RionyaFireDancerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect( + source.getControllerId(), null, true, + RionyaFireDancerWatcher.getValue(source.getControllerId(), game) + 1 + ); + effect.apply(game, source); + effect.exileTokensCreatedAtNextEndStep(game, source); + return true; + } +} + +class RionyaFireDancerWatcher extends Watcher { + + private final Map playerMap = new HashMap<>(); + + RionyaFireDancerWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.SPELL_CAST) { + return; + } + Spell spell = game.getSpell(event.getTargetId()); + if (spell != null && spell.isInstantOrSorcery()) { + playerMap.compute(spell.getControllerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + } + } + + @Override + public void reset() { + super.reset(); + playerMap.clear(); + } + + static int getValue(UUID playerId, Game game) { + RionyaFireDancerWatcher watcher = game.getState().getWatcher(RionyaFireDancerWatcher.class); + return watcher == null ? 0 : watcher.playerMap.getOrDefault(playerId, 0); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RipApart.java b/Mage.Sets/src/mage/cards/r/RipApart.java new file mode 100644 index 000000000000..b4cf2fcba6e4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RipApart.java @@ -0,0 +1,42 @@ +package mage.cards.r; + +import mage.abilities.Mode; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreatureOrPlaneswalker; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RipApart extends CardImpl { + + public RipApart(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}{W}"); + + // Choose one — + // • Rip Apart deals 3 damage to target creature or planeswalker. + this.getSpellAbility().addEffect(new DamageTargetEffect(3)); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); + + // • Destroy target artifact or enchantment. + Mode mode = new Mode(new DestroyTargetEffect()); + mode.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); + this.getSpellAbility().addMode(mode); + } + + private RipApart(final RipApart card) { + super(card); + } + + @Override + public RipApart copy() { + return new RipApart(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RiseAgain.java b/Mage.Sets/src/mage/cards/r/RiseAgain.java index 11e7c1e6a8b1..a87bc5739620 100644 --- a/Mage.Sets/src/mage/cards/r/RiseAgain.java +++ b/Mage.Sets/src/mage/cards/r/RiseAgain.java @@ -18,7 +18,7 @@ public RiseAgain(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}"); // Return target creature card from your graveyard to the battlefield. - this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect(false, false)); + this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); } diff --git a/Mage.Sets/src/mage/cards/r/RiseOfExtus.java b/Mage.Sets/src/mage/cards/r/RiseOfExtus.java new file mode 100644 index 000000000000..e3de6d030ea7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RiseOfExtus.java @@ -0,0 +1,42 @@ +package mage.cards.r; + +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.LearnEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInGraveyard; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RiseOfExtus extends CardImpl { + + public RiseOfExtus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{W/B}{W/B}"); + + // Exile target creature. Exile up to one target instant or sorcery card from a graveyard. + this.getSpellAbility().addEffect(new ExileTargetEffect("Exile target creature. " + + "Exile up to one target instant or sorcery card from a graveyard.", true)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addTarget(new TargetCardInGraveyard( + 0, 1, StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY + )); + + // Learn. + this.getSpellAbility().addEffect(new LearnEffect().concatBy("
")); + } + + private RiseOfExtus(final RiseOfExtus card) { + super(card); + } + + @Override + public RiseOfExtus copy() { + return new RiseOfExtus(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RisingPopulace.java b/Mage.Sets/src/mage/cards/r/RisingPopulace.java index 81622751329f..cc2d7c0f86f4 100644 --- a/Mage.Sets/src/mage/cards/r/RisingPopulace.java +++ b/Mage.Sets/src/mage/cards/r/RisingPopulace.java @@ -11,7 +11,7 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/r/RiteOfPassage.java b/Mage.Sets/src/mage/cards/r/RiteOfPassage.java index b342e0627738..03adcc80c189 100644 --- a/Mage.Sets/src/mage/cards/r/RiteOfPassage.java +++ b/Mage.Sets/src/mage/cards/r/RiteOfPassage.java @@ -62,7 +62,7 @@ public RiteOfPassageTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override @@ -70,10 +70,8 @@ public boolean checkTrigger(GameEvent event, Game game) { UUID targetId = event.getTargetId(); Permanent permanent = game.getPermanent(targetId); if (permanent != null && filter.match(permanent, getSourceId(), getControllerId(), game)) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); - return true; - } + getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game)); + return true; } return false; } diff --git a/Mage.Sets/src/mage/cards/r/RiteOfReplication.java b/Mage.Sets/src/mage/cards/r/RiteOfReplication.java index 26c370a93d39..c3015a4c13d6 100644 --- a/Mage.Sets/src/mage/cards/r/RiteOfReplication.java +++ b/Mage.Sets/src/mage/cards/r/RiteOfReplication.java @@ -27,7 +27,7 @@ public RiteOfReplication(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new CreateTokenCopyTargetEffect(null, null, false, 5), new CreateTokenCopyTargetEffect(), KickedCondition.instance, - "Create a token that's a copy of target creature. if this spell was kicked, create five of those tokens instead")); + "Create a token that's a copy of target creature. If this spell was kicked, create five of those tokens instead")); } private RiteOfReplication(final RiteOfReplication card) { diff --git a/Mage.Sets/src/mage/cards/r/RiteOfUndoing.java b/Mage.Sets/src/mage/cards/r/RiteOfUndoing.java index 2ad6d2278546..afdc5c0fc382 100644 --- a/Mage.Sets/src/mage/cards/r/RiteOfUndoing.java +++ b/Mage.Sets/src/mage/cards/r/RiteOfUndoing.java @@ -1,27 +1,21 @@ - package mage.cards.r; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.keyword.DelveAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.TargetController; -import mage.constants.Zone; import mage.filter.common.FilterNonlandPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.common.TargetNonlandPermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class RiteOfUndoing extends CardImpl { - + private static final FilterNonlandPermanent filterControlled = new FilterNonlandPermanent("nonland permanent you control"); private static final FilterNonlandPermanent filterNotControlled = new FilterNonlandPermanent("nonland permanent you don't control"); @@ -31,13 +25,14 @@ public final class RiteOfUndoing extends CardImpl { } public RiteOfUndoing(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{4}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{U}"); // Delve this.addAbility(new DelveAbility()); - + // Return target nonland permanent you control and target nonland permanent you don't control to their owners' hands. - this.getSpellAbility().addEffect(new RiteOfUndoingEffect()); + this.getSpellAbility().addEffect(new ReturnToHandTargetEffect(true) + .setText("return target nonland permanent you control and target nonland permanent you don't control to their owners' hands")); this.getSpellAbility().addTarget(new TargetNonlandPermanent(filterControlled)); this.getSpellAbility().addTarget(new TargetNonlandPermanent(filterNotControlled)); } @@ -51,36 +46,3 @@ public RiteOfUndoing copy() { return new RiteOfUndoing(this); } } - -class RiteOfUndoingEffect extends OneShotEffect { - - public RiteOfUndoingEffect() { - super(Outcome.ReturnToHand); - this.staticText = "Return target nonland permanent you control and target nonland permanent you don't control to their owners' hands"; - } - - public RiteOfUndoingEffect(final RiteOfUndoingEffect effect) { - super(effect); - } - - @Override - public RiteOfUndoingEffect copy() { - return new RiteOfUndoingEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - boolean result = false; - - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent != null) { - result |= permanent.moveToZone(Zone.HAND, source, game, false); - } - permanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if (permanent != null) { - result |= permanent.moveToZone(Zone.HAND, source, game, false); - } - - return result; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/r/RitesOfReaping.java b/Mage.Sets/src/mage/cards/r/RitesOfReaping.java index 106cbbb3d841..faf070f2fe20 100644 --- a/Mage.Sets/src/mage/cards/r/RitesOfReaping.java +++ b/Mage.Sets/src/mage/cards/r/RitesOfReaping.java @@ -12,7 +12,7 @@ import mage.constants.Outcome; import mage.constants.SubLayer; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/r/RitesOfRefusal.java b/Mage.Sets/src/mage/cards/r/RitesOfRefusal.java index 653da6bce61e..401d311ecd83 100644 --- a/Mage.Sets/src/mage/cards/r/RitesOfRefusal.java +++ b/Mage.Sets/src/mage/cards/r/RitesOfRefusal.java @@ -1,18 +1,16 @@ package mage.cards.r; import mage.abilities.Ability; -import mage.abilities.costs.Cost; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.Cards; import mage.constants.CardType; import mage.constants.Outcome; import mage.game.Game; -import mage.game.stack.Spell; import mage.players.Player; import mage.target.TargetSpell; -import mage.util.ManaUtil; import java.util.UUID; @@ -28,7 +26,6 @@ public RitesOfRefusal(UUID ownerId, CardSetInfo setInfo) { // controller pays {3} for each card discarded this way. this.getSpellAbility().addEffect(new RitesOfRefusalEffect()); this.getSpellAbility().addTarget(new TargetSpell()); - } private RitesOfRefusal(final RitesOfRefusal card) { @@ -49,7 +46,7 @@ class RitesOfRefusalEffect extends OneShotEffect { + "spell unless its controller pays {3} for each card discarded this way"; } - RitesOfRefusalEffect(final RitesOfRefusalEffect effect) { + private RitesOfRefusalEffect(final RitesOfRefusalEffect effect) { super(effect); } @@ -61,31 +58,10 @@ public RitesOfRefusalEffect copy() { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Spell targetSpell = game.getStack().getSpell(source.getFirstTarget()); - if (targetSpell != null) { - Player controllerOfTargetedSpell = game.getPlayer(targetSpell.getControllerId()); - if (controller != null && controllerOfTargetedSpell != null) { - int numToDiscard = controller.getAmount(0, - controller.getHand().size(), "How many cards do you want to discard?", game); - Cards discardedCards = controller.discard(numToDiscard, false, false, source, game); - int actualNumberDiscarded = discardedCards.size(); - if (actualNumberDiscarded > 0) { - Cost cost = ManaUtil.createManaCost(actualNumberDiscarded * 3, false); - if (controllerOfTargetedSpell.chooseUse(Outcome.Benefit, - "Do you want to pay " - + cost.getText() - + " to prevent " - + targetSpell.getName() - + " from gettting countered?", source, game) - && cost.pay(source, game, source, - controllerOfTargetedSpell.getId(), false)) { - return true; - } - game.getStack().counter(targetSpell.getId(), source, game); - return true; - } - } + if (controller == null) { + return false; } - return false; + int count = controller.discard(0, Integer.MAX_VALUE, false, source, game).size(); + return new CounterUnlessPaysEffect(new GenericManaCost(3 * count)).apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/r/RitesOfSpring.java b/Mage.Sets/src/mage/cards/r/RitesOfSpring.java index 2f04978334f1..b8c67042d4cc 100644 --- a/Mage.Sets/src/mage/cards/r/RitesOfSpring.java +++ b/Mage.Sets/src/mage/cards/r/RitesOfSpring.java @@ -1,20 +1,21 @@ package mage.cards.r; +import mage.MageItem; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; -import mage.target.TargetCard; import mage.target.common.TargetCardInLibrary; -import mage.target.common.TargetDiscard; +import java.util.Objects; import java.util.UUID; /** @@ -44,7 +45,7 @@ class RitesOfSpringEffect extends OneShotEffect { RitesOfSpringEffect() { super(Outcome.DrawCard); this.staticText = "Discard any number of cards. Search your library for up to that many basic land cards, " + - "reveal those cards, and put them into your hand. Then shuffle your library."; + "reveal those cards, and put them into your hand. Then shuffle."; } private RitesOfSpringEffect(final RitesOfSpringEffect effect) { @@ -62,12 +63,23 @@ public boolean apply(Game game, Ability source) { if (controller == null) { return false; } - TargetCard target = new TargetDiscard(0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD, controller.getId()); - controller.choose(Outcome.AIDontUseIt, controller.getHand(), target, game); - int numDiscarded = controller.discard(new CardsImpl(target.getTargets()), false, source, game).size(); - new SearchLibraryPutInHandEffect(new TargetCardInLibrary( + int numDiscarded = controller.discard(0, Integer.MAX_VALUE, false, source, game).size(); + TargetCardInLibrary target = new TargetCardInLibrary( 0, numDiscarded, StaticFilters.FILTER_CARD_BASIC_LAND - ), true, true).apply(game, source); + ); + controller.searchLibrary(target, source, game); + Cards cards = new CardsImpl(); + controller + .getLibrary() + .getCards(game) + .stream() + .filter(Objects::nonNull) + .map(MageItem::getId) + .filter(target.getTargets()::contains) + .forEach(cards::add); + controller.revealCards(source, cards, game); + controller.moveCards(cards, Zone.HAND, source, game); + controller.shuffleLibrary(source, game); return true; } } diff --git a/Mage.Sets/src/mage/cards/r/RitualOfSoot.java b/Mage.Sets/src/mage/cards/r/RitualOfSoot.java index 0a2b9d209df5..143d34e08411 100644 --- a/Mage.Sets/src/mage/cards/r/RitualOfSoot.java +++ b/Mage.Sets/src/mage/cards/r/RitualOfSoot.java @@ -8,7 +8,7 @@ import mage.constants.ComparisonType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; /** * @@ -16,10 +16,10 @@ */ public final class RitualOfSoot extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent("creatures with converted mana cost 3 or less"); + private static final FilterPermanent filter = new FilterCreaturePermanent("creatures with mana value 3 or less"); static { - filter.add(new ConvertedManaCostPredicate( + filter.add(new ManaValuePredicate( ComparisonType.FEWER_THAN, 4 )); } diff --git a/Mage.Sets/src/mage/cards/r/RiverSneak.java b/Mage.Sets/src/mage/cards/r/RiverSneak.java index 1a185cacf595..d0dd5602a949 100644 --- a/Mage.Sets/src/mage/cards/r/RiverSneak.java +++ b/Mage.Sets/src/mage/cards/r/RiverSneak.java @@ -14,7 +14,7 @@ import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/r/RiversRebuke.java b/Mage.Sets/src/mage/cards/r/RiversRebuke.java index c9afc755b714..bbce02913d59 100644 --- a/Mage.Sets/src/mage/cards/r/RiversRebuke.java +++ b/Mage.Sets/src/mage/cards/r/RiversRebuke.java @@ -1,22 +1,22 @@ - package mage.cards.r; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.common.FilterNonlandPermanent; -import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class RiversRebuke extends CardImpl { @@ -41,26 +41,27 @@ public RiversRebuke copy() { class RiversRebukeReturnToHandEffect extends OneShotEffect { - public RiversRebukeReturnToHandEffect() { + RiversRebukeReturnToHandEffect() { super(Outcome.ReturnToHand); staticText = "Return all nonland permanents target player controls to their owner's hand"; } - public RiversRebukeReturnToHandEffect(final RiversRebukeReturnToHandEffect effect) { + private RiversRebukeReturnToHandEffect(final RiversRebukeReturnToHandEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { - if (targetPointer.getFirst(game, source) != null) { - FilterNonlandPermanent filter = new FilterNonlandPermanent(); - filter.add(new ControllerIdPredicate(targetPointer.getFirst(game, source))); - for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - permanent.moveToZone(Zone.HAND, source, game, true); - } - return true; + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; } - return false; + Cards cards = new CardsImpl(); + game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_CONTROLLED_PERMANENT_NON_LAND, + source.getFirstTarget(), source.getSourceId(), game + ).stream().forEach(cards::add); + return player.moveCards(cards, Zone.HAND, source, game); } @Override diff --git a/Mage.Sets/src/mage/cards/r/RoaleskApexHybrid.java b/Mage.Sets/src/mage/cards/r/RoaleskApexHybrid.java index 29cd3f704aed..38dd36c20606 100644 --- a/Mage.Sets/src/mage/cards/r/RoaleskApexHybrid.java +++ b/Mage.Sets/src/mage/cards/r/RoaleskApexHybrid.java @@ -16,7 +16,7 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/r/RobberOfTheRich.java b/Mage.Sets/src/mage/cards/r/RobberOfTheRich.java index e00f89878c58..08592b2ce275 100644 --- a/Mage.Sets/src/mage/cards/r/RobberOfTheRich.java +++ b/Mage.Sets/src/mage/cards/r/RobberOfTheRich.java @@ -1,13 +1,10 @@ package mage.cards.r; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.AsThoughManaEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.hint.ConditionHint; import mage.abilities.keyword.HasteAbility; @@ -16,20 +13,13 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.game.ExileZone; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; -import mage.players.ManaPoolItem; import mage.players.Player; -import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; -import mage.watchers.Watcher; import mage.watchers.common.AttackedThisTurnWatcher; -import java.util.HashSet; import java.util.Objects; -import java.util.Set; import java.util.UUID; /** @@ -53,17 +43,14 @@ public RobberOfTheRich(UUID ownerId, CardSetInfo setInfo) { this.addAbility(HasteAbility.getInstance()); // Whenever Robber of the Rich attacks, if defending player has more cards in hand than you, exile the top card of their library. During any turn you attacked with a Rogue, you may cast that card and you may spend mana as though it were mana of any color to cast that spell. - Ability ability = new ConditionalInterveningIfTriggeredAbility( + this.addAbility(new ConditionalInterveningIfTriggeredAbility( new AttacksTriggeredAbility( new RobberOfTheRichEffect(), false, "", SetTargetPointer.PLAYER ), RobberOfTheRichAttacksCondition.instance, "Whenever {this} attacks, " + "if defending player has more cards in hand than you, exile the top card of their library. " + "During any turn you attacked with a Rogue, you may cast that card and " + - "you may spend mana as though it were mana of any color to cast that spell."); - ability.addWatcher(new AttackedThisTurnWatcher()); - ability.addHint(new ConditionHint(RobberOfTheRichAnyTurnAttackedCondition.instance)); - - this.addAbility(ability); + "you may spend mana as though it were mana of any color to cast that spell." + ).addHint(new ConditionHint(RobberOfTheRichAnyTurnAttackedCondition.instance)), new AttackedThisTurnWatcher()); } private RobberOfTheRich(final RobberOfTheRich card) { @@ -133,19 +120,18 @@ public boolean apply(Game game, Ability source) { if (controller == null || damagedPlayer == null) { return false; } - MageObject sourceObject = game.getObject(source.getSourceId()); - UUID exileId = CardUtil.getCardExileZoneId(game, source); + Permanent sourceObject = source.getSourcePermanentIfItStillExists(game); Card card = damagedPlayer.getLibrary().getFromTop(game); if (card == null || sourceObject == null) { - return true; + return false; } // move card to exile - controller.moveCardToExileWithInfo(card, exileId, sourceObject.getIdName(), source, game, Zone.LIBRARY, true); + controller.moveCardsToExile(card, source, game, true, CardUtil.getExileZoneId(game, source), sourceObject.getIdName()); // Add effects only if the card has a spellAbility (e.g. not for lands). if (card.getSpellAbility() != null) { // allow to cast the card // and you may spend mana as though it were mana of any color to cast it - CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.Custom, RobberOfTheRichAnyTurnAttackedCondition.instance); + CardUtil.makeCardPlayable(game, source, card, Duration.Custom, true, RobberOfTheRichAnyTurnAttackedCondition.instance); } return true; } diff --git a/Mage.Sets/src/mage/cards/r/RockyTarPit.java b/Mage.Sets/src/mage/cards/r/RockyTarPit.java index 4f3e26bb9d72..0cc8671a8a0b 100644 --- a/Mage.Sets/src/mage/cards/r/RockyTarPit.java +++ b/Mage.Sets/src/mage/cards/r/RockyTarPit.java @@ -22,7 +22,7 @@ public RockyTarPit(UUID ownerId, CardSetInfo setInfo) { // Rocky Tar Pit enters the battlefield tapped. this.addAbility(new EntersBattlefieldTappedAbility()); // {tap}, Sacrifice Rocky Tar Pit: Search your library for a Swamp or Mountain card and put it onto the battlefield. Then shuffle your library. - this.addAbility(new FetchLandActivatedAbility(false, EnumSet.of(SubType.SWAMP, SubType.MOUNTAIN))); + this.addAbility(new FetchLandActivatedAbility(false, SubType.SWAMP, SubType.MOUNTAIN)); } private RockyTarPit(final RockyTarPit card) { diff --git a/Mage.Sets/src/mage/cards/r/RookieMistake.java b/Mage.Sets/src/mage/cards/r/RookieMistake.java index 8f9f16512e8f..b844915920c7 100644 --- a/Mage.Sets/src/mage/cards/r/RookieMistake.java +++ b/Mage.Sets/src/mage/cards/r/RookieMistake.java @@ -11,7 +11,7 @@ import mage.constants.Outcome; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; diff --git a/Mage.Sets/src/mage/cards/r/RoonOfTheHiddenRealm.java b/Mage.Sets/src/mage/cards/r/RoonOfTheHiddenRealm.java index f3e1c941db4c..f8625e03d393 100644 --- a/Mage.Sets/src/mage/cards/r/RoonOfTheHiddenRealm.java +++ b/Mage.Sets/src/mage/cards/r/RoonOfTheHiddenRealm.java @@ -16,7 +16,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/r/RoothaMercurialArtist.java b/Mage.Sets/src/mage/cards/r/RoothaMercurialArtist.java new file mode 100644 index 000000000000..28b0a9cf09ef --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RoothaMercurialArtist.java @@ -0,0 +1,57 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.ReturnToHandFromBattlefieldSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CopyTargetSpellEffect; +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.FilterSpell; +import mage.filter.common.FilterInstantOrSorcerySpell; +import mage.target.TargetSpell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RoothaMercurialArtist extends CardImpl { + + private static final FilterSpell filter + = new FilterInstantOrSorcerySpell("instant or sorcery spell you control"); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + } + + public RoothaMercurialArtist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ORC); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // {2}, Return Rootha, Mercurial Artist to its owner's hand: Copy target instant or sorcery spell you control. You may choose new targets for the copy. + Ability ability = new SimpleActivatedAbility(new CopyTargetSpellEffect(), new GenericManaCost(2)); + ability.addCost(new ReturnToHandFromBattlefieldSourceCost()); + ability.addTarget(new TargetSpell(filter)); + this.addAbility(ability); + } + + private RoothaMercurialArtist(final RoothaMercurialArtist card) { + super(card); + } + + @Override + public RoothaMercurialArtist copy() { + return new RoothaMercurialArtist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RootsOfWisdom.java b/Mage.Sets/src/mage/cards/r/RootsOfWisdom.java index 433a7086d177..45eb9ab37e61 100644 --- a/Mage.Sets/src/mage/cards/r/RootsOfWisdom.java +++ b/Mage.Sets/src/mage/cards/r/RootsOfWisdom.java @@ -45,10 +45,10 @@ class RootsOfWisdomEffect extends OneShotEffect { private static final FilterCard filter = new FilterCard("land card or Elf card"); static { - Predicates.or( + filter.add(Predicates.or( CardType.LAND.getPredicate(), SubType.ELF.getPredicate() - ); + )); } RootsOfWisdomEffect() { @@ -74,6 +74,7 @@ public boolean apply(Game game, Ability source) { } player.millCards(3, source, game); TargetCard targetCard = new TargetCardInYourGraveyard(filter); + targetCard.setNotTarget(true); if (targetCard.canChoose(source.getSourceId(), source.getControllerId(), game) && player.choose(outcome, targetCard, source.getSourceId(), game)) { Card card = player.getGraveyard().get(targetCard.getFirstTarget(), game); diff --git a/Mage.Sets/src/mage/cards/r/RootwaterThief.java b/Mage.Sets/src/mage/cards/r/RootwaterThief.java index 425e6069fec9..824b4ed0be39 100644 --- a/Mage.Sets/src/mage/cards/r/RootwaterThief.java +++ b/Mage.Sets/src/mage/cards/r/RootwaterThief.java @@ -57,7 +57,7 @@ class RootwaterThiefEffect extends OneShotEffect { RootwaterThiefEffect() { super(Outcome.Exile); - staticText = "you may pay {2}. If you do, search that player's library for a card and exile it, then the player shuffles their library."; + staticText = "you may pay {2}. If you do, search that player's library for a card and exile it, then the player shuffles."; } RootwaterThiefEffect(final RootwaterThiefEffect effect) { diff --git a/Mage.Sets/src/mage/cards/r/RootweaverDruid.java b/Mage.Sets/src/mage/cards/r/RootweaverDruid.java index 909615f82198..21a7a62aa768 100644 --- a/Mage.Sets/src/mage/cards/r/RootweaverDruid.java +++ b/Mage.Sets/src/mage/cards/r/RootweaverDruid.java @@ -53,7 +53,7 @@ class RootweaverDruidEffect extends OneShotEffect { staticText = "each opponent may search their library for up to three basic land cards. " + "They each put one of those cards onto the battlefield tapped under your control " + "and the rest onto the battlefield tapped under their control. " + - "Then each player who searched their library this way shuffles it"; + "Then each player who searched their library this way shuffles"; } private RootweaverDruidEffect(final RootweaverDruidEffect effect) { diff --git a/Mage.Sets/src/mage/cards/r/RotFarmSkeleton.java b/Mage.Sets/src/mage/cards/r/RotFarmSkeleton.java index 031ffa3cb84e..07cee3eb9768 100644 --- a/Mage.Sets/src/mage/cards/r/RotFarmSkeleton.java +++ b/Mage.Sets/src/mage/cards/r/RotFarmSkeleton.java @@ -35,7 +35,7 @@ public RotFarmSkeleton (UUID ownerId, CardSetInfo setInfo) { this.addAbility(new CantBlockAbility()); // 2{B}{G}, Put the top four cards of your library into your graveyard: Return Rot Farm Skeleton from your graveyard to the battlefield. Activate this ability only any time you could cast a sorcery. - Ability ability = new ActivateAsSorceryActivatedAbility(Zone.GRAVEYARD, new ReturnSourceFromGraveyardToBattlefieldEffect(), new ManaCostsImpl("{2}{B}{G}")); + Ability ability = new ActivateAsSorceryActivatedAbility(Zone.GRAVEYARD, new ReturnSourceFromGraveyardToBattlefieldEffect(false,false), new ManaCostsImpl("{2}{B}{G}")); ability.addCost(new MillCardsCost(4)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/r/RotHulk.java b/Mage.Sets/src/mage/cards/r/RotHulk.java index 38326524d6db..79479b205f90 100644 --- a/Mage.Sets/src/mage/cards/r/RotHulk.java +++ b/Mage.Sets/src/mage/cards/r/RotHulk.java @@ -3,8 +3,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.dynamicvalue.common.OpponentsCount; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.abilities.keyword.MenaceAbility; import mage.cards.CardImpl; @@ -14,6 +12,7 @@ import mage.filter.FilterCard; import mage.game.Game; import mage.target.common.TargetCardInYourGraveyard; +import mage.target.targetadjustment.TargetAdjuster; import java.util.UUID; @@ -22,14 +21,6 @@ */ public final class RotHulk extends CardImpl { - private static final FilterCard filterZombie = new FilterCard("Zombie cards from your graveyard"); - - static { - filterZombie.add(SubType.ZOMBIE.getPredicate()); - } - - private final UUID entersBattlefieldAbilityID; - public RotHulk(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{B}{B}"); this.subtype.add(SubType.ZOMBIE); @@ -40,28 +31,14 @@ public RotHulk(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new MenaceAbility(false)); // When Rot Hulk enters the battlefield, return up to X target Zombie cards from your graveyard to the battlefield, where X is the number of opponents you have. - Effect effect = new ReturnFromGraveyardToBattlefieldTargetEffect(); - effect.setText("return up to X target Zombie cards from your graveyard to the battlefield, where X is the number of opponents you have."); - Ability ability = new EntersBattlefieldTriggeredAbility(effect); - ability.addTarget(new TargetCardInYourGraveyard()); - entersBattlefieldAbilityID = ability.getOriginalId(); // adjust targets + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect() + .setText("return up to X target Zombie cards from your graveyard to the battlefield, where X is the number of opponents you have.")); + ability.setTargetAdjuster(RotHulkAdjuster.instance); this.addAbility(ability); } - @Override - public void adjustTargets(Ability ability, Game game) { - if (ability.getOriginalId().equals(entersBattlefieldAbilityID)) { - // up to X target Zombie cards from your graveyard - // X is the number of opponents you have. - ability.getTargets().clear(); - int numbTargets = OpponentsCount.instance.calculate(game, ability, null); - ability.addTarget(new TargetCardInYourGraveyard(0, numbTargets, filterZombie)); - } - } - private RotHulk(final RotHulk card) { super(card); - this.entersBattlefieldAbilityID = card.entersBattlefieldAbilityID; } @Override @@ -69,3 +46,20 @@ public RotHulk copy() { return new RotHulk(this); } } + +enum RotHulkAdjuster implements TargetAdjuster { + instance; + private static final FilterCard filterZombie = new FilterCard("Zombie cards from your graveyard"); + + static { + filterZombie.add(SubType.ZOMBIE.getPredicate()); + } + + @Override + public void adjustTargets(Ability ability, Game game) { + ability.getTargets().clear(); + ability.addTarget(new TargetCardInYourGraveyard( + 0, game.getOpponents(ability.getControllerId()).size(), filterZombie + )); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RotShambler.java b/Mage.Sets/src/mage/cards/r/RotShambler.java index 80e255e60153..b20dee877390 100644 --- a/Mage.Sets/src/mage/cards/r/RotShambler.java +++ b/Mage.Sets/src/mage/cards/r/RotShambler.java @@ -12,7 +12,7 @@ import mage.constants.TargetController; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/r/RousingRefrain.java b/Mage.Sets/src/mage/cards/r/RousingRefrain.java new file mode 100644 index 000000000000..c941dd989eba --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RousingRefrain.java @@ -0,0 +1,83 @@ +package mage.cards.r; + +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileSpellEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.SuspendAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ManaType; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RousingRefrain extends CardImpl { + + public RousingRefrain(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}{R}"); + + // Add {R} for each card in target opponent's hand. Until end of turn, you don't lose this mana as steps and phases end. Exile Rousing Refrain with three time counters on it. + this.getSpellAbility().addEffect(new RousingRefrainEffect()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); + this.getSpellAbility().addEffect(new AddCountersSourceEffect( + CounterType.TIME.createInstance(), StaticValue.get(3), false, true + ).setText("with three time counters on it")); + this.getSpellAbility().addTarget(new TargetOpponent()); + + // Suspend 3—{1}{R} + this.addAbility(new SuspendAbility(3, new ManaCostsImpl<>("{1}{R}"), this)); + } + + private RousingRefrain(final RousingRefrain card) { + super(card); + } + + @Override + public RousingRefrain copy() { + return new RousingRefrain(this); + } +} + +class RousingRefrainEffect extends OneShotEffect { + + RousingRefrainEffect() { + super(Outcome.Benefit); + staticText = "Add {R} for each card in target opponent's hand. " + + "Until end of turn, you don't lose this mana as steps and phases end"; + } + + private RousingRefrainEffect(final RousingRefrainEffect effect) { + super(effect); + } + + @Override + public RousingRefrainEffect copy() { + return new RousingRefrainEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player player = game.getPlayer(source.getFirstTarget()); + if (controller == null || player == null || player.getHand().isEmpty()) { + return false; + } + controller.getManaPool().addMana( + new Mana(ManaType.RED, player.getHand().size()), + game, source, false + ); + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/r/RowanScholarOfSparks.java b/Mage.Sets/src/mage/cards/r/RowanScholarOfSparks.java new file mode 100644 index 000000000000..4e412594c996 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RowanScholarOfSparks.java @@ -0,0 +1,182 @@ +package mage.cards.r; + +import mage.MageItem; +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.GetEmblemEffect; +import mage.abilities.effects.common.continuous.SetPowerToughnessTargetEffect; +import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; +import mage.abilities.hint.Hint; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.cards.ModalDoubleFacesCard; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.common.FilterInstantOrSorceryCard; +import mage.game.Game; +import mage.game.command.emblems.RowanScholarOfSparksEmblem; +import mage.game.permanent.token.PrismariToken; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.watchers.common.CardsAmountDrawnThisTurnWatcher; + +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class RowanScholarOfSparks extends ModalDoubleFacesCard { + + private static final FilterCard filter = new FilterInstantOrSorceryCard("instant and sorcery spells"); + + public RowanScholarOfSparks(UUID ownerId, CardSetInfo setInfo) { + super( + ownerId, setInfo, + new CardType[]{CardType.PLANESWALKER}, new SubType[]{SubType.ROWAN}, "{2}{R}", + "Will, Scholar of Frost", + new CardType[]{CardType.PLANESWALKER}, new SubType[]{SubType.WILL}, "{4}{U}" + ); + + // 1. + // Rowan, Scholar of Sparks + // Legendary Planeswalker - Rowan + this.getLeftHalfCard().addSuperType(SuperType.LEGENDARY); + this.getLeftHalfCard().addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(2)); + + // Instant and sorcery spells you cast cost {1} less to cast. + this.getLeftHalfCard().addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 1))); + + // +1: Rowan, Scholar of Sparks deals 1 damage to each opponent. If you've drawn three or more cards this turn, she deals 3 damage to each opponent instead. + Ability ability = new LoyaltyAbility(new ConditionalOneShotEffect( + new DamagePlayersEffect(3, TargetController.OPPONENT), + new DamagePlayersEffect(1, TargetController.OPPONENT), + RowanScholarOfSparksCondition.instance, "{this} deals 1 damage to each opponent. " + + "If you've drawn three or more cards this turn, she deals 3 damage to each opponent instead" + ), 1); + ability.addWatcher(new CardsAmountDrawnThisTurnWatcher()); + this.getLeftHalfCard().addAbility(ability.addHint(RowanScholarOfSparksHint.instance)); + + // −4: You get an emblem with "Whenever you cast an instant or sorcery spell, you may pay {2}. If you do, copy that spell. You may choose new targets for the copy." + this.getLeftHalfCard().addAbility(new LoyaltyAbility( + new GetEmblemEffect(new RowanScholarOfSparksEmblem()), -4 + )); + + // 2. + // Will, Scholar of Frost + // Legendary Planeswalker - Will + this.getRightHalfCard().addSuperType(SuperType.LEGENDARY); + this.getRightHalfCard().addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + + // Instant and sorcery spells you cast cost {1} less to cast. + this.getRightHalfCard().addAbility(new SimpleStaticAbility( + new SpellsCostReductionControllerEffect(filter, 1) + )); + + // +1: Up to one target creature has base power and toughness 0/2 until your next turn. + ability = new LoyaltyAbility(new SetPowerToughnessTargetEffect( + 0, 2, Duration.UntilYourNextTurn + ), 1); + ability.addTarget(new TargetCreaturePermanent(0, 1)); + this.getRightHalfCard().addAbility(ability); + + // −3: Draw two cards. + this.getRightHalfCard().addAbility(new LoyaltyAbility(new DrawCardSourceControllerEffect(2), -3)); + + // −7: Exile up to five target permanents. For each permanent exiled this way, its controller creates a 4/4 blue and red Elemental Creature token. + ability = new LoyaltyAbility(new WillScholarOfFrostEffect(), -7); + ability.addTarget(new TargetPermanent(0, 5, StaticFilters.FILTER_PERMANENTS)); + this.getRightHalfCard().addAbility(ability); + } + + private RowanScholarOfSparks(final RowanScholarOfSparks card) { + super(card); + } + + @Override + public RowanScholarOfSparks copy() { + return new RowanScholarOfSparks(this); + } +} + +enum RowanScholarOfSparksCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + CardsAmountDrawnThisTurnWatcher watcher = game.getState().getWatcher(CardsAmountDrawnThisTurnWatcher.class); + return watcher != null && watcher.getAmountCardsDrawn(source.getControllerId()) >= 3; + } +} + +enum RowanScholarOfSparksHint implements Hint { + instance; + + @Override + public String getText(Game game, Ability ability) { + CardsAmountDrawnThisTurnWatcher watcher = game.getState().getWatcher(CardsAmountDrawnThisTurnWatcher.class); + int drawn = watcher != null ? watcher.getAmountCardsDrawn(ability.getControllerId()) : 0; + return "Cards drawn this turn: " + drawn; + } + + @Override + public RowanScholarOfSparksHint copy() { + return instance; + } +} + +class WillScholarOfFrostEffect extends OneShotEffect { + + WillScholarOfFrostEffect() { + super(Outcome.Benefit); + staticText = "exile up to five target permanents. For each permanent exiled this way, " + + "its controller creates a 4/4 blue and red Elemental creature token"; + } + + private WillScholarOfFrostEffect(final WillScholarOfFrostEffect effect) { + super(effect); + } + + @Override + public WillScholarOfFrostEffect copy() { + return new WillScholarOfFrostEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getSourceId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(targetPointer.getTargets(game, source)); + Map playerMap = cards + .getCards(game) + .stream() + .filter(Objects::nonNull) + .map(MageItem::getId) + .map(game::getControllerId) + .filter(Objects::nonNull) + .collect(Collectors.toMap( + Function.identity(), + x -> 1, Integer::sum + )); + player.moveCards(cards, Zone.EXILED, source, game); + for (Map.Entry entry : playerMap.entrySet()) { + new PrismariToken().putOntoBattlefield(entry.getValue(), game, source, entry.getKey()); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RuinGrinder.java b/Mage.Sets/src/mage/cards/r/RuinGrinder.java new file mode 100644 index 000000000000..7b77d648142c --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RuinGrinder.java @@ -0,0 +1,88 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.abilities.keyword.MountaincyclingAbility; +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.players.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RuinGrinder extends CardImpl { + + public RuinGrinder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}{R}"); + + this.subtype.add(SubType.CONSTRUCT); + this.power = new MageInt(7); + this.toughness = new MageInt(4); + + // Menace + this.addAbility(new MenaceAbility()); + + // When Ruin Grinder dies, each player may discard their hand and draw seven cards. + this.addAbility(new DiesSourceTriggeredAbility(new RuinGrinderEffect())); + + // Mountaincycling {2} + this.addAbility(new MountaincyclingAbility(new ManaCostsImpl<>("{2}"))); + } + + private RuinGrinder(final RuinGrinder card) { + super(card); + } + + @Override + public RuinGrinder copy() { + return new RuinGrinder(this); + } +} + +class RuinGrinderEffect extends OneShotEffect { + + RuinGrinderEffect() { + super(Outcome.Benefit); + staticText = "each player may discard their hand and draw seven cards"; + } + + private RuinGrinderEffect(final RuinGrinderEffect effect) { + super(effect); + } + + @Override + public RuinGrinderEffect copy() { + return new RuinGrinderEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + List wheelers = new ArrayList<>(); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(source.getSourceId()); + if (player != null && player.chooseUse( + Outcome.DrawCard, "Discard your hand and draw seven?", source, game + )) { + game.informPlayers(player.getName() + " chooses to discard their hand and draw seven"); + wheelers.add(player); + } + } + for (Player player : wheelers) { + player.discard(player.getHand(), false, source, game); + player.drawCards(7, source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RuinInTheirWake.java b/Mage.Sets/src/mage/cards/r/RuinInTheirWake.java index 6fa360c8ffa3..15358fcba51e 100644 --- a/Mage.Sets/src/mage/cards/r/RuinInTheirWake.java +++ b/Mage.Sets/src/mage/cards/r/RuinInTheirWake.java @@ -53,7 +53,7 @@ class RuinInTheirWakeEffect extends OneShotEffect { public RuinInTheirWakeEffect() { super(Outcome.Benefit); - this.staticText = "Search your library for a basic land card and reveal it. You may put that card onto the battlefield tapped if you control a land named Wastes. Otherwise, put that card into your hand. Then shuffle your library"; + this.staticText = "Search your library for a basic land card and reveal it. You may put that card onto the battlefield tapped if you control a land named Wastes. Otherwise, put that card into your hand. Then shuffle"; } public RuinInTheirWakeEffect(final RuinInTheirWakeEffect effect) { diff --git a/Mage.Sets/src/mage/cards/r/RuinRaider.java b/Mage.Sets/src/mage/cards/r/RuinRaider.java index 0f4efc98fa62..7459f2518ba6 100644 --- a/Mage.Sets/src/mage/cards/r/RuinRaider.java +++ b/Mage.Sets/src/mage/cards/r/RuinRaider.java @@ -34,7 +34,7 @@ public RuinRaider(UUID ownerId, CardSetInfo setInfo) { RaidCondition.instance, "Raid — At the beginning of your end step, if you attacked this turn, " + "reveal the top card of your library and put that card into your hand. " - + "You lose life equal to the card's converted mana cost."); + + "You lose life equal to the card's mana value."); ability.setAbilityWord(AbilityWord.RAID); ability.addHint(RaidHint.instance); this.addAbility(ability, new PlayerAttackedWatcher()); @@ -54,7 +54,7 @@ class RuinRaiderEffect extends OneShotEffect { RuinRaiderEffect() { super(Outcome.DrawCard); - this.staticText = "reveal the top card of your library and put that card into your hand. You lose life equal to its converted mana cost"; + this.staticText = "reveal the top card of your library and put that card into your hand. You lose life equal to its mana value"; } RuinRaiderEffect(final RuinRaiderEffect effect) { @@ -71,7 +71,7 @@ public boolean apply(Game game, Ability source) { Cards cards = new CardsImpl(card); controller.revealCards(source, cards, game); controller.moveCards(card, Zone.HAND, source, game); - controller.loseLife(card.getConvertedManaCost(), game, source, false); + controller.loseLife(card.getManaValue(), game, source, false); } return true; } diff --git a/Mage.Sets/src/mage/cards/r/RummagingWizard.java b/Mage.Sets/src/mage/cards/r/RummagingWizard.java index 74b104beb067..1e5aee50329d 100644 --- a/Mage.Sets/src/mage/cards/r/RummagingWizard.java +++ b/Mage.Sets/src/mage/cards/r/RummagingWizard.java @@ -71,7 +71,7 @@ public boolean apply(Game game, Ability source) { CardsImpl cards = new CardsImpl(); cards.add(card); controller.lookAtCards("Rummaging Wizard", cards, game); - if (controller.chooseUse(Outcome.Neutral, "Do you wish to put the card into your graveyard?", source, game)) { + if (controller.chooseUse(Outcome.Neutral, "Put that card into your graveyard?", source, game)) { return controller.moveCards(card, Zone.GRAVEYARD, source, game); } diff --git a/Mage.Sets/src/mage/cards/r/RuneOfMight.java b/Mage.Sets/src/mage/cards/r/RuneOfMight.java index b89d35362678..d1a7295659cc 100644 --- a/Mage.Sets/src/mage/cards/r/RuneOfMight.java +++ b/Mage.Sets/src/mage/cards/r/RuneOfMight.java @@ -11,7 +11,6 @@ import mage.abilities.effects.common.continuous.BoostEquippedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EnchantAbility; -import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -61,7 +60,7 @@ public RuneOfMight(UUID ownerId, CardSetInfo setInfo) { // As long as enchanted permanent is an Equipment, it has "Equipped creature gets +1/+1 and has trample." ability = new SimpleStaticAbility(new BoostEquippedEffect(1, 1)); ability.addEffect(new GainAbilityAttachedEffect( - FlyingAbility.getInstance(), AttachmentType.EQUIPMENT + TrampleAbility.getInstance(), AttachmentType.EQUIPMENT ).setText("and has trample")); this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( new GainAbilityAttachedEffect(ability, AttachmentType.AURA), diff --git a/Mage.Sets/src/mage/cards/r/RunedCrown.java b/Mage.Sets/src/mage/cards/r/RunedCrown.java index 635889f9d78e..133504f0b7fa 100644 --- a/Mage.Sets/src/mage/cards/r/RunedCrown.java +++ b/Mage.Sets/src/mage/cards/r/RunedCrown.java @@ -64,7 +64,7 @@ class RunedCrownEffect extends OneShotEffect { RunedCrownEffect() { super(Outcome.Benefit); staticText = "you may search your library, hand, and/or graveyard for a Rune card " + - "and put it onto the battlefield attached to {this}. If you search your library this way, shuffle it"; + "and put it onto the battlefield attached to {this}. If you search your library this way, shuffle"; } private RunedCrownEffect(final RunedCrownEffect effect) { diff --git a/Mage.Sets/src/mage/cards/r/RuneforgeChampion.java b/Mage.Sets/src/mage/cards/r/RuneforgeChampion.java index 37a9e95f27f1..2886ed941763 100644 --- a/Mage.Sets/src/mage/cards/r/RuneforgeChampion.java +++ b/Mage.Sets/src/mage/cards/r/RuneforgeChampion.java @@ -41,7 +41,7 @@ public RuneforgeChampion(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryGraveyardPutInHandEffect( filter, false, false ).setText("search your library and/or graveyard for a Rune card, reveal it, " + - "and put it into your hand. If you search your library this way, shuffle it"), true)); + "and put it into your hand. If you search your library this way, shuffle"), true)); // You may pay {1} rather than pay the mana cost for Rune spells you cast. this.addAbility(new SimpleStaticAbility(new RuneforgeChampionEffect())); diff --git a/Mage.Sets/src/mage/cards/r/RushOfKnowledge.java b/Mage.Sets/src/mage/cards/r/RushOfKnowledge.java index cc4caee959e5..d271dced86b9 100644 --- a/Mage.Sets/src/mage/cards/r/RushOfKnowledge.java +++ b/Mage.Sets/src/mage/cards/r/RushOfKnowledge.java @@ -2,7 +2,7 @@ package mage.cards.r; import java.util.UUID; -import mage.abilities.dynamicvalue.common.HighestConvertedManaCostValue; +import mage.abilities.dynamicvalue.common.HighestManaValueCount; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -18,8 +18,8 @@ public RushOfKnowledge(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{U}"); // Draw cards equal to the highest converted mana cost among permanents you control. - DrawCardSourceControllerEffect effect = new DrawCardSourceControllerEffect(new HighestConvertedManaCostValue()); - effect.setText("Draw cards equal to the highest converted mana cost among permanents you control"); + DrawCardSourceControllerEffect effect = new DrawCardSourceControllerEffect(new HighestManaValueCount()); + effect.setText("Draw cards equal to the highest mana value among permanents you control"); this.getSpellAbility().addEffect(effect); } diff --git a/Mage.Sets/src/mage/cards/r/RushedRebirth.java b/Mage.Sets/src/mage/cards/r/RushedRebirth.java new file mode 100644 index 000000000000..b4d79618f312 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RushedRebirth.java @@ -0,0 +1,137 @@ +package mage.cards.r; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +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.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RushedRebirth extends CardImpl { + + public RushedRebirth(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}{G}"); + + // Choose target creature. When that creature dies this turn, search your library for a creature card with lesser mana value, put it onto the battlefield tapped, then shuffle. + this.getSpellAbility().addEffect(new RushedRebirthEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private RushedRebirth(final RushedRebirth card) { + super(card); + } + + @Override + public RushedRebirth copy() { + return new RushedRebirth(this); + } +} + +class RushedRebirthEffect extends OneShotEffect { + + RushedRebirthEffect() { + super(Outcome.Benefit); + staticText = "choose target creature. When that creature dies this turn, " + + "search your library for a creature card with lesser mana value, " + + "put it onto the battlefield tapped, then shuffle"; + } + + private RushedRebirthEffect(final RushedRebirthEffect effect) { + super(effect); + } + + @Override + public RushedRebirthEffect copy() { + return new RushedRebirthEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + game.addDelayedTriggeredAbility(new RushedRebirthDelayedTriggeredAbility(permanent, game), source); + return true; + } +} + +class RushedRebirthDelayedTriggeredAbility extends DelayedTriggeredAbility { + + private static final class RushedRebirthPredicate implements Predicate { + + private final Permanent permanent; + + private RushedRebirthPredicate(Permanent permanent) { + this.permanent = permanent; + } + + @Override + public boolean apply(Card input, Game game) { + return input.getManaValue() < permanent.getManaValue(); + } + } + + private final MageObjectReference mor; + + RushedRebirthDelayedTriggeredAbility(Permanent permanent, Game game) { + super(makeEffect(permanent), Duration.EndOfTurn, false, false); + this.mor = new MageObjectReference(permanent, game); + } + + private RushedRebirthDelayedTriggeredAbility(final RushedRebirthDelayedTriggeredAbility ability) { + super(ability); + this.mor = ability.mor; + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + return zEvent.isDiesEvent() && mor.refersTo(zEvent.getTarget(), game); + } + + private static Effect makeEffect(Permanent permanent) { + FilterCard filter = new FilterCreatureCard( + "creature card with lesser mana value than " + permanent.getIdName() + ); + filter.add(new RushedRebirthPredicate(permanent)); + return new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter), true); + } + + @Override + public RushedRebirthDelayedTriggeredAbility copy() { + return new RushedRebirthDelayedTriggeredAbility(this); + } + + @Override + public String getRule() { + return "When that creature dies this turn, search your library for a creature card " + + "with lesser mana value, put it onto the battlefield tapped, then shuffle."; + } + +} diff --git a/Mage.Sets/src/mage/cards/r/Rust.java b/Mage.Sets/src/mage/cards/r/Rust.java index cd5877747e4c..2ff86879a7fe 100644 --- a/Mage.Sets/src/mage/cards/r/Rust.java +++ b/Mage.Sets/src/mage/cards/r/Rust.java @@ -7,7 +7,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.FilterStackObject; -import mage.filter.predicate.ability.ArtifactSourcePredicate; +import mage.filter.predicate.other.ArtifactSourcePredicate; import mage.target.common.TargetActivatedAbility; /** diff --git a/Mage.Sets/src/mage/cards/r/RustElemental.java b/Mage.Sets/src/mage/cards/r/RustElemental.java index 4557d90af082..a68eb8e3cc6a 100644 --- a/Mage.Sets/src/mage/cards/r/RustElemental.java +++ b/Mage.Sets/src/mage/cards/r/RustElemental.java @@ -11,7 +11,7 @@ import mage.constants.Outcome; import mage.constants.SubType; import mage.filter.common.FilterControlledArtifactPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/r/RuxaPatientProfessor.java b/Mage.Sets/src/mage/cards/r/RuxaPatientProfessor.java new file mode 100644 index 000000000000..23c1f5c425ae --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RuxaPatientProfessor.java @@ -0,0 +1,106 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.NoAbilityPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RuxaPatientProfessor extends CardImpl { + + private static final FilterCard filter + = new FilterCreatureCard("creature card with no abilities from your graveyard"); + private static final FilterCreaturePermanent filter2 + = new FilterCreaturePermanent("creatures you control with no abilities"); + + static { + filter.add(NoAbilityPredicate.instance); + filter2.add(NoAbilityPredicate.instance); + filter2.add(TargetController.YOU.getControllerPredicate()); + } + + public RuxaPatientProfessor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.BEAR); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Whenever Ruxa, Patient Professor enters the battlefield or attacks, return target creature card with no abilities from your graveyard to your hand. + Ability ability = new EntersBattlefieldOrAttacksSourceTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect()); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + + // Creatures you control with no abilities get +1/+1. + this.addAbility(new SimpleStaticAbility(new BoostAllEffect( + 1, 1, Duration.WhileOnBattlefield, filter2, false + ))); + + // You may have creatures you control with no abilities assign their combat damage as though they weren't blocked. + this.addAbility(new SimpleStaticAbility(new RuxaPatientProfessorEffect())); + } + + private RuxaPatientProfessor(final RuxaPatientProfessor card) { + super(card); + } + + @Override + public RuxaPatientProfessor copy() { + return new RuxaPatientProfessor(this); + } +} + +class RuxaPatientProfessorEffect extends AsThoughEffectImpl { + + RuxaPatientProfessorEffect() { + super(AsThoughEffectType.DAMAGE_NOT_BLOCKED, Duration.WhileOnBattlefield, Outcome.Damage); + this.staticText = "you may have creatures you control with no abilities " + + "assign their combat damage as though they weren't blocked"; + } + + private RuxaPatientProfessorEffect(RuxaPatientProfessorEffect effect) { + super(effect); + } + + @Override + public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(sourceId); + return controller != null + && permanent != null + && permanent.isControlledBy(controller.getId()) + && NoAbilityPredicate.instance.apply(permanent, game) + && controller.chooseUse(Outcome.Damage, "Have " + permanent.getLogName() + + " assign damage as though it weren't blocked?", source, game); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public RuxaPatientProfessorEffect copy() { + return new RuxaPatientProfessorEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RysorianBadger.java b/Mage.Sets/src/mage/cards/r/RysorianBadger.java index bc04b9607b99..ef4eb395a61a 100644 --- a/Mage.Sets/src/mage/cards/r/RysorianBadger.java +++ b/Mage.Sets/src/mage/cards/r/RysorianBadger.java @@ -8,7 +8,7 @@ import mage.cards.*; import mage.constants.*; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.permanent.DefendingPlayerOwnsCardPredicate; +import mage.filter.predicate.card.DefendingPlayerOwnsCardPredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInGraveyard; diff --git a/Mage.Sets/src/mage/cards/s/SacredRites.java b/Mage.Sets/src/mage/cards/s/SacredRites.java index 4c302b9f2cf8..aded1d344619 100644 --- a/Mage.Sets/src/mage/cards/s/SacredRites.java +++ b/Mage.Sets/src/mage/cards/s/SacredRites.java @@ -5,15 +5,11 @@ import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; -import mage.target.Target; -import mage.target.common.TargetDiscard; import java.util.UUID; @@ -61,9 +57,7 @@ public boolean apply(Game game, Ability source) { if (controller == null) { return false; } - Target target = new TargetDiscard(0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD, controller.getId()); - target.choose(outcome, controller.getId(), source.getSourceId(), game); - int numDiscarded = controller.discard(new CardsImpl(target.getTargets()), false, source, game).size(); + int numDiscarded = controller.discard(0, Integer.MAX_VALUE, false, source, game).size(); if (numDiscarded > 0) { game.addEffect(new BoostControlledEffect(0, numDiscarded, Duration.EndOfTurn), source); } diff --git a/Mage.Sets/src/mage/cards/s/Sacrifice.java b/Mage.Sets/src/mage/cards/s/Sacrifice.java index 061885b961fc..9824e920dffa 100644 --- a/Mage.Sets/src/mage/cards/s/Sacrifice.java +++ b/Mage.Sets/src/mage/cards/s/Sacrifice.java @@ -25,7 +25,7 @@ public Sacrifice(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); // Add an amount of {B} equal to the sacrificed creature's converted mana cost. this.getSpellAbility().addEffect(new DynamicManaEffect(Mana.BlackMana(1), new SacrificeCostConvertedMana("creature"), - "add an amount of {B} equal to the sacrificed creature's converted mana cost")); + "add an amount of {B} equal to the sacrificed creature's mana value")); } private Sacrifice(final Sacrifice card) { diff --git a/Mage.Sets/src/mage/cards/s/SaddlebackLagac.java b/Mage.Sets/src/mage/cards/s/SaddlebackLagac.java index 715fe21771d2..2e1c31286671 100644 --- a/Mage.Sets/src/mage/cards/s/SaddlebackLagac.java +++ b/Mage.Sets/src/mage/cards/s/SaddlebackLagac.java @@ -11,7 +11,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/s/SadisticSacrament.java b/Mage.Sets/src/mage/cards/s/SadisticSacrament.java index e3072e02e900..7861c852283b 100644 --- a/Mage.Sets/src/mage/cards/s/SadisticSacrament.java +++ b/Mage.Sets/src/mage/cards/s/SadisticSacrament.java @@ -25,7 +25,7 @@ */ public final class SadisticSacrament extends CardImpl { - private static final String ruleText = "Search target player's library for up to three cards, exile them, then that player shuffles their library. if this spell was kicked, instead search that player's library for up to fifteen cards, exile them, then that player shuffles their library"; + private static final String ruleText = "Search target player's library for up to three cards, exile them, then that player shuffles. If this spell was kicked, instead search that player's library for up to fifteen cards, exile them, then that player shuffles"; public SadisticSacrament(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{B}{B}{B}"); diff --git a/Mage.Sets/src/mage/cards/s/SafeHaven.java b/Mage.Sets/src/mage/cards/s/SafeHaven.java index ce568f1e674a..3a0ccfb5ca43 100644 --- a/Mage.Sets/src/mage/cards/s/SafeHaven.java +++ b/Mage.Sets/src/mage/cards/s/SafeHaven.java @@ -7,7 +7,7 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DoIfCostPaid; -import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.ExileTargetForSourceEffect; import mage.abilities.effects.common.ReturnFromExileEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -27,21 +27,18 @@ public SafeHaven(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // {2}, {tap}: Exile target creature you control. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(this.getId(), - this.getIdName()), new ManaCostsImpl("{2}")); + Ability ability = new SimpleActivatedAbility(new ExileTargetForSourceEffect(), new ManaCostsImpl("{2}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); // At the beginning of your upkeep, you may sacrifice Safe Haven. If you do, return each card exiled with Safe Haven to the battlefield under its owner's control. - ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, - new DoIfCostPaid( - new ReturnFromExileEffect(this.getId(), Zone.BATTLEFIELD, "return each card exiled with {this} to the battlefield under its owner's control"), - new SacrificeSourceCost() - ), - TargetController.YOU, - false); - this.addAbility(ability); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new DoIfCostPaid(new ReturnFromExileEffect( + Zone.BATTLEFIELD, "return each card exiled with " + + "{this} to the battlefield under its owner's control" + ), new SacrificeSourceCost()), TargetController.YOU, false + )); } private SafeHaven(final SafeHaven card) { diff --git a/Mage.Sets/src/mage/cards/s/SageOfAncientLore.java b/Mage.Sets/src/mage/cards/s/SageOfAncientLore.java index 26ec503c655c..1dd23df37256 100644 --- a/Mage.Sets/src/mage/cards/s/SageOfAncientLore.java +++ b/Mage.Sets/src/mage/cards/s/SageOfAncientLore.java @@ -1,31 +1,25 @@ - package mage.cards.s; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.condition.common.TransformedCondition; import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.CardsInControllerHandCount; import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.TargetController; +import mage.constants.SubType; import mage.constants.Zone; +import java.util.UUID; + /** * @author fireshoes */ @@ -53,8 +47,7 @@ public SageOfAncientLore(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each upkeep, if no spells were cast last turn, transform Sage of Ancient Lore. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private SageOfAncientLore(final SageOfAncientLore card) { diff --git a/Mage.Sets/src/mage/cards/s/SagesRowDenizen.java b/Mage.Sets/src/mage/cards/s/SagesRowDenizen.java index 211f4342bc0d..9f48bc610231 100644 --- a/Mage.Sets/src/mage/cards/s/SagesRowDenizen.java +++ b/Mage.Sets/src/mage/cards/s/SagesRowDenizen.java @@ -15,7 +15,7 @@ import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPlayer; /** diff --git a/Mage.Sets/src/mage/cards/s/SaheeliSublimeArtificer.java b/Mage.Sets/src/mage/cards/s/SaheeliSublimeArtificer.java index fea2ab95b1d0..64e669802438 100644 --- a/Mage.Sets/src/mage/cards/s/SaheeliSublimeArtificer.java +++ b/Mage.Sets/src/mage/cards/s/SaheeliSublimeArtificer.java @@ -17,7 +17,7 @@ import mage.filter.common.FilterControlledArtifactPermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.ServoToken; diff --git a/Mage.Sets/src/mage/cards/s/SaheelisDirective.java b/Mage.Sets/src/mage/cards/s/SaheelisDirective.java index 70b52465786b..4ec7f52a28e0 100644 --- a/Mage.Sets/src/mage/cards/s/SaheelisDirective.java +++ b/Mage.Sets/src/mage/cards/s/SaheelisDirective.java @@ -14,7 +14,7 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.common.FilterArtifactCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; @@ -51,7 +51,7 @@ public SaheelisDirectiveEffect() { super(Outcome.PutCardInPlay); staticText = "Reveal the top X cards of your library. " + "You may put any number of artifact cards with " - + "converted mana cost X or less from among them onto the battlefield. " + + "mana value X or less from among them onto the battlefield. " + "Then put all cards revealed this way that weren't " + "put onto the battlefield into your graveyard."; } @@ -70,8 +70,8 @@ public boolean apply(Game game, Ability source) { Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, xValue)); if (!cards.isEmpty()) { controller.revealCards(source, cards, game); - FilterCard filter = new FilterArtifactCard("artifact cards with converted mana cost " + xValue + " or less to put onto the battlefield"); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, xValue + 1)); + FilterCard filter = new FilterArtifactCard("artifact cards with mana value " + xValue + " or less to put onto the battlefield"); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); TargetCard target1 = new TargetCard(0, Integer.MAX_VALUE, Zone.LIBRARY, filter); target1.setNotTarget(true); controller.choose(Outcome.PutCardInPlay, cards, target1, game); diff --git a/Mage.Sets/src/mage/cards/s/SaiTok.java b/Mage.Sets/src/mage/cards/s/SaiTok.java index fb5387db8a21..69684d40f9e7 100644 --- a/Mage.Sets/src/mage/cards/s/SaiTok.java +++ b/Mage.Sets/src/mage/cards/s/SaiTok.java @@ -8,7 +8,7 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetPermanent; /** @@ -17,12 +17,12 @@ */ public final class SaiTok extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("creature or planeswalker if it has converted mana cost 4 or less"); + private static final FilterPermanent filter = new FilterPermanent("creature or planeswalker if it has mana value 4 or less"); static { filter.add(CardType.CREATURE.getPredicate()); filter.add(CardType.PLANESWALKER.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 5)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 5)); } public SaiTok(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/s/SaltRoadAmbushers.java b/Mage.Sets/src/mage/cards/s/SaltRoadAmbushers.java index 659e3ed2dd8c..310828ec3fde 100644 --- a/Mage.Sets/src/mage/cards/s/SaltRoadAmbushers.java +++ b/Mage.Sets/src/mage/cards/s/SaltRoadAmbushers.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/s/Saltskitter.java b/Mage.Sets/src/mage/cards/s/Saltskitter.java index b397830303d8..673cf8c5c84a 100644 --- a/Mage.Sets/src/mage/cards/s/Saltskitter.java +++ b/Mage.Sets/src/mage/cards/s/Saltskitter.java @@ -11,7 +11,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/s/SalvageTrader.java b/Mage.Sets/src/mage/cards/s/SalvageTrader.java index f3233e42a574..76e21c7352f3 100644 --- a/Mage.Sets/src/mage/cards/s/SalvageTrader.java +++ b/Mage.Sets/src/mage/cards/s/SalvageTrader.java @@ -34,7 +34,7 @@ public SalvageTrader(UUID ownerId, CardSetInfo setInfo) { // {T}: Exchange control of target artifact you control and target artifact an opponent controls with the same converted mana cost. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExchangeControlTargetEffect(Duration.EndOfGame, - "Exchange control of target artifact you control and target artifact an opponent controls with the same converted mana cost", false, true), + "Exchange control of target artifact you control and target artifact an opponent controls with the same mana value", false, true), new TapSourceCost()); FilterArtifactPermanent filterYou = new FilterArtifactPermanent("artifact you control"); filterYou.add(TargetController.YOU.getControllerPredicate()); @@ -71,7 +71,7 @@ public boolean apply(ObjectSourcePlayer input, Game game) { source.getStackAbility().getTargets().get(0).getTargets().get(0)); Permanent inputPermanent = game.getPermanent(input.getObject().getId()); if (firstTarget != null && inputPermanent != null) { - return firstTarget.getConvertedManaCost() == inputPermanent.getConvertedManaCost(); + return firstTarget.getManaValue() == inputPermanent.getManaValue(); } } return true; diff --git a/Mage.Sets/src/mage/cards/s/SalvagerOfRuin.java b/Mage.Sets/src/mage/cards/s/SalvagerOfRuin.java index d5d7b88f7ce6..54f95345feec 100644 --- a/Mage.Sets/src/mage/cards/s/SalvagerOfRuin.java +++ b/Mage.Sets/src/mage/cards/s/SalvagerOfRuin.java @@ -1,20 +1,17 @@ package mage.cards.s; import mage.MageInt; -import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.filter.FilterCard; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.Predicate; -import mage.game.Game; +import mage.filter.predicate.card.PutIntoGraveFromBattlefieldThisTurnPredicate; import mage.target.common.TargetCardInYourGraveyard; import mage.watchers.common.CardsPutIntoGraveyardWatcher; @@ -30,7 +27,7 @@ public final class SalvagerOfRuin extends CardImpl { ); static { - filter.add(SalvagerOfRuinPredicate.instance); + filter.add(PutIntoGraveFromBattlefieldThisTurnPredicate.instance); } public SalvagerOfRuin(UUID ownerId, CardSetInfo setInfo) { @@ -41,11 +38,13 @@ public SalvagerOfRuin(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(1); // Sacrifice Salvager of Ruin: Choose target permanent card in your graveyard that was put there from the battlefield this turn. Return it to your hand. - Ability ability = new SimpleActivatedAbility(new ReturnFromGraveyardToHandTargetEffect().setText( - "Choose target permanent card in your graveyard " + - "that was put there from the battlefield this turn. " + - "Return it to your hand." - ), new SacrificeSourceCost()); + Ability ability = new SimpleActivatedAbility( + new ReturnFromGraveyardToHandTargetEffect() + .setText("Choose target permanent card in your graveyard " + + "that was put there from the battlefield this turn. " + + "Return it to your hand."), + new SacrificeSourceCost() + ); ability.addTarget(new TargetCardInYourGraveyard(1, filter)); this.addAbility(ability, new CardsPutIntoGraveyardWatcher()); } @@ -59,14 +58,3 @@ public SalvagerOfRuin copy() { return new SalvagerOfRuin(this); } } - -enum SalvagerOfRuinPredicate implements Predicate { - instance; - - @Override - public boolean apply(Card input, Game game) { - CardsPutIntoGraveyardWatcher watcher = game.getState().getWatcher(CardsPutIntoGraveyardWatcher.class); - return watcher != null - && watcher.getCardsPutToGraveyardFromBattlefield().contains(new MageObjectReference(input, game)); - } -} diff --git a/Mage.Sets/src/mage/cards/s/SalvagingStation.java b/Mage.Sets/src/mage/cards/s/SalvagingStation.java index 56f6528c9c00..f2adc872ecac 100644 --- a/Mage.Sets/src/mage/cards/s/SalvagingStation.java +++ b/Mage.Sets/src/mage/cards/s/SalvagingStation.java @@ -15,7 +15,7 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInYourGraveyard; /** @@ -24,12 +24,12 @@ */ public final class SalvagingStation extends CardImpl { - private static final FilterCard filter = new FilterCard("noncreature artifact card with converted mana cost 1 or less"); + private static final FilterCard filter = new FilterCard("noncreature artifact card with mana value 1 or less from your graveyard"); static { filter.add(CardType.ARTIFACT.getPredicate()); filter.add(Predicates.not(CardType.CREATURE.getPredicate())); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 2)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 2)); } public SalvagingStation(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/s/SamutVoiceOfDissent.java b/Mage.Sets/src/mage/cards/s/SamutVoiceOfDissent.java index 76a11e4a3c84..b121dc6ba620 100644 --- a/Mage.Sets/src/mage/cards/s/SamutVoiceOfDissent.java +++ b/Mage.Sets/src/mage/cards/s/SamutVoiceOfDissent.java @@ -23,7 +23,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/s/SanctifierOfSouls.java b/Mage.Sets/src/mage/cards/s/SanctifierOfSouls.java index 31446794af04..6067078303a6 100644 --- a/Mage.Sets/src/mage/cards/s/SanctifierOfSouls.java +++ b/Mage.Sets/src/mage/cards/s/SanctifierOfSouls.java @@ -18,7 +18,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterCreatureCard; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.SpiritWhiteToken; import mage.target.common.TargetCardInYourGraveyard; diff --git a/Mage.Sets/src/mage/cards/s/SanctumGargoyle.java b/Mage.Sets/src/mage/cards/s/SanctumGargoyle.java index e67d040ced44..2e1f5f3feb35 100644 --- a/Mage.Sets/src/mage/cards/s/SanctumGargoyle.java +++ b/Mage.Sets/src/mage/cards/s/SanctumGargoyle.java @@ -5,6 +5,7 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -30,7 +31,7 @@ public SanctumGargoyle(UUID ownerId, CardSetInfo setInfo) { // Flying this.addAbility(FlyingAbility.getInstance()); // When Sanctum Gargoyle enters the battlefield, you may return target artifact card from your graveyard to your hand. - Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect(), true); + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect(), true); ability.addTarget(new TargetCardInYourGraveyard(new FilterArtifactCard("artifact card from your graveyard"))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SanctumOfEternity.java b/Mage.Sets/src/mage/cards/s/SanctumOfEternity.java index bbdfcf23ec05..337759ea92ba 100644 --- a/Mage.Sets/src/mage/cards/s/SanctumOfEternity.java +++ b/Mage.Sets/src/mage/cards/s/SanctumOfEternity.java @@ -14,7 +14,7 @@ import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.CommanderPredicate; +import mage.filter.predicate.mageobject.CommanderPredicate; import mage.target.TargetPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/s/SanctumOfUgin.java b/Mage.Sets/src/mage/cards/s/SanctumOfUgin.java index 1bd9f5b4db0f..bf6b58af91ab 100644 --- a/Mage.Sets/src/mage/cards/s/SanctumOfUgin.java +++ b/Mage.Sets/src/mage/cards/s/SanctumOfUgin.java @@ -15,7 +15,7 @@ import mage.filter.FilterSpell; import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.mageobject.ColorlessPredicate; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; /** @@ -24,12 +24,12 @@ public final class SanctumOfUgin extends CardImpl { private static final FilterCreatureCard filter = new FilterCreatureCard("colorless creature card"); - private static final FilterSpell filterSpells = new FilterSpell("colorless spell with converted mana cost 7 or greater"); + private static final FilterSpell filterSpells = new FilterSpell("colorless spell with mana value 7 or greater"); static { filter.add(ColorlessPredicate.instance); filterSpells.add(ColorlessPredicate.instance); - filterSpells.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 6)); + filterSpells.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 6)); } public SanctumOfUgin(UUID ownerId, CardSetInfo setInfo) { @@ -41,7 +41,7 @@ public SanctumOfUgin(UUID ownerId, CardSetInfo setInfo) { // Whenever you cast a colorless spell with converted mana cost 7 or greater, you may sacrifice Sanctum of Ugin. // If you do, search your library for a colorless creature card, reveal it, put it into your hand, then shuffle your library. Effect effect = new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true); - effect.setText("search your library for a colorless creature card, reveal it, put it into your hand, then shuffle your library"); + effect.setText("search your library for a colorless creature card, reveal it, put it into your hand, then shuffle"); this.addAbility(new SpellCastControllerTriggeredAbility(new DoIfCostPaid(effect, new SacrificeSourceCost()), filterSpells, false)); } diff --git a/Mage.Sets/src/mage/cards/s/SanctumPrelate.java b/Mage.Sets/src/mage/cards/s/SanctumPrelate.java index 1af5432aad59..a4fb50932095 100644 --- a/Mage.Sets/src/mage/cards/s/SanctumPrelate.java +++ b/Mage.Sets/src/mage/cards/s/SanctumPrelate.java @@ -63,7 +63,7 @@ public ChooseNumberEffect(final ChooseNumberEffect effect) { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - int numberChoice = controller.announceXMana(0, Integer.MAX_VALUE, "Choose a number. Noncreature spells with the chosen converted mana cost can't be cast", game, source); + int numberChoice = controller.announceXMana(0, Integer.MAX_VALUE, "Choose a number. Noncreature spells with the chosen mana value can't be cast", game, source); game.getState().setValue(source.getSourceId().toString(), numberChoice); Permanent permanent = game.getPermanentEntering(source.getSourceId()); @@ -82,7 +82,7 @@ public ChooseNumberEffect copy() { } private String setText() { - return "Choose a number. Noncreature spells with the chosen converted mana cost can't be cast"; + return "Choose a number. Noncreature spells with the chosen mana value can't be cast"; } } @@ -92,7 +92,7 @@ class SanctumPrelateReplacementEffect extends ContinuousRuleModifyingEffectImpl public SanctumPrelateReplacementEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); - staticText = "Noncreature spells with the chosen converted mana cost can't be cast"; + staticText = "Noncreature spells with the chosen mana value can't be cast"; } public SanctumPrelateReplacementEffect(final SanctumPrelateReplacementEffect effect) { @@ -113,7 +113,7 @@ public SanctumPrelateReplacementEffect copy() { public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source.getSourceId()); if (mageObject != null) { - return "You can't cast a noncreature card with that converted mana cost (" + mageObject.getIdName() + " in play)."; + return "You can't cast a noncreature card with that mana value (" + mageObject.getIdName() + " in play)."; } return null; } @@ -129,7 +129,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { Spell spell = game.getStack().getSpell(event.getTargetId()); if (spell != null && !spell.isCreature()) { - return spell.getConvertedManaCost() == choiceValue; + return spell.getManaValue() == choiceValue; } return false; } diff --git a/Mage.Sets/src/mage/cards/s/Sangromancer.java b/Mage.Sets/src/mage/cards/s/Sangromancer.java index 15b3d0bcb261..523571ccaaad 100644 --- a/Mage.Sets/src/mage/cards/s/Sangromancer.java +++ b/Mage.Sets/src/mage/cards/s/Sangromancer.java @@ -78,7 +78,7 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { - return "Whenever a creature an opponent controls is put into a graveyard from the battlefield, " + super.getRule(); + return "Whenever a creature an opponent controls dies, " + super.getRule(); } } diff --git a/Mage.Sets/src/mage/cards/s/SanguineGlorifier.java b/Mage.Sets/src/mage/cards/s/SanguineGlorifier.java index aab3c64b8c3e..4d65558b4319 100644 --- a/Mage.Sets/src/mage/cards/s/SanguineGlorifier.java +++ b/Mage.Sets/src/mage/cards/s/SanguineGlorifier.java @@ -12,7 +12,7 @@ import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledPermanent; /** diff --git a/Mage.Sets/src/mage/cards/s/SanguinePraetor.java b/Mage.Sets/src/mage/cards/s/SanguinePraetor.java index b5e2fb94caed..2ed3cb0813bc 100644 --- a/Mage.Sets/src/mage/cards/s/SanguinePraetor.java +++ b/Mage.Sets/src/mage/cards/s/SanguinePraetor.java @@ -56,7 +56,7 @@ class SanguinePraetorEffect extends OneShotEffect { public SanguinePraetorEffect() { super(Outcome.Damage); - staticText = "Destroy each creature with the same converted mana cost as the sacrificed creature"; + staticText = "Destroy each creature with the same mana value as the sacrificed creature"; } public SanguinePraetorEffect(final SanguinePraetorEffect effect) { @@ -68,13 +68,13 @@ public boolean apply(Game game, Ability source) { int cmc = 0; for (Cost cost : source.getCosts()) { if (cost instanceof SacrificeTargetCost && !((SacrificeTargetCost) cost).getPermanents().isEmpty()) { - cmc = ((SacrificeTargetCost) cost).getPermanents().get(0).getConvertedManaCost(); + cmc = ((SacrificeTargetCost) cost).getPermanents().get(0).getManaValue(); break; } } for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game)) { - if (permanent.getConvertedManaCost() == cmc) { + if (permanent.getManaValue() == cmc) { permanent.destroy(source, game, false); } } diff --git a/Mage.Sets/src/mage/cards/s/SaprolingInfestation.java b/Mage.Sets/src/mage/cards/s/SaprolingInfestation.java index 3d5dc788fcbe..57a9c30018e9 100644 --- a/Mage.Sets/src/mage/cards/s/SaprolingInfestation.java +++ b/Mage.Sets/src/mage/cards/s/SaprolingInfestation.java @@ -20,7 +20,6 @@ public final class SaprolingInfestation extends CardImpl { public SaprolingInfestation(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}"); - // Whenever a player kicks a spell, you put a 1/1 green Saproling creature token onto the battlefield. this.addAbility(new SaprolingInfestationTriggeredAbility()); } @@ -34,13 +33,13 @@ public SaprolingInfestation copy() { return new SaprolingInfestation(this); } - class SaprolingInfestationTriggeredAbility extends TriggeredAbilityImpl { + private static final class SaprolingInfestationTriggeredAbility extends TriggeredAbilityImpl { - SaprolingInfestationTriggeredAbility() { + private SaprolingInfestationTriggeredAbility() { super(Zone.BATTLEFIELD, new CreateTokenEffect(new SaprolingToken()), false); } - SaprolingInfestationTriggeredAbility(final SaprolingInfestationTriggeredAbility ability) { + private SaprolingInfestationTriggeredAbility(final SaprolingInfestationTriggeredAbility ability) { super(ability); } @@ -61,7 +60,7 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { - return "Whenever a player kicks a spell, you put a 1/1 green Saproling creature token onto the battlefield."; + return "Whenever a player kicks a spell, you creat a 1/1 green Saproling creature token."; } } } diff --git a/Mage.Sets/src/mage/cards/s/SarkhanTheMad.java b/Mage.Sets/src/mage/cards/s/SarkhanTheMad.java index e0f60881ea56..7b9def742df0 100644 --- a/Mage.Sets/src/mage/cards/s/SarkhanTheMad.java +++ b/Mage.Sets/src/mage/cards/s/SarkhanTheMad.java @@ -63,7 +63,7 @@ public SarkhanTheMad copy() { class SarkhanTheMadRevealAndDrawEffect extends OneShotEffect { - private static final String effectText = "Reveal the top card of your library and put it into your hand. {this} deals damage to himself equal to that card's converted mana cost"; + private static final String effectText = "Reveal the top card of your library and put it into your hand. {this} deals damage to himself equal to that card's mana value"; SarkhanTheMadRevealAndDrawEffect() { super(Outcome.DrawCard); @@ -84,7 +84,7 @@ public boolean apply(Game game, Ability source) { if (card != null) { controller.moveCards(card, Zone.HAND, source, game); if (sourcePermanent != null) { - sourcePermanent.damage(card.getConvertedManaCost(), source.getSourceId(), source, game, false, false); + sourcePermanent.damage(card.getManaValue(), source.getSourceId(), source, game, false, false); } controller.revealCards(sourceObject.getIdName(), new CardsImpl(card), game); } diff --git a/Mage.Sets/src/mage/cards/s/SarulfRealmEater.java b/Mage.Sets/src/mage/cards/s/SarulfRealmEater.java index 942179703f86..1bd2c0d63fff 100644 --- a/Mage.Sets/src/mage/cards/s/SarulfRealmEater.java +++ b/Mage.Sets/src/mage/cards/s/SarulfRealmEater.java @@ -18,8 +18,8 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterNonlandPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -59,7 +59,7 @@ public SarulfRealmEater(UUID ownerId, CardSetInfo setInfo) { new BeginningOfUpkeepTriggeredAbility( new SarulfRealmEaterEffect(), TargetController.YOU, true ), condition, "At the beginning of your upkeep, if {this} has one or more +1/+1 counters on it, " + - "you may remove all of them. If you do, exile each other nonland permanent with converted mana cost " + + "you may remove all of them. If you do, exile each other nonland permanent with mana value " + "less than or equal to the number of counters removed this way." )); } @@ -99,7 +99,7 @@ public boolean apply(Game game, Ability source) { int counterCount = permanent.getCounters(game).getCount(CounterType.P1P1); permanent.removeCounters(CounterType.P1P1.createInstance(counterCount), source, game); FilterPermanent filter = new FilterNonlandPermanent(); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, counterCount + 1)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, counterCount + 1)); filter.add(AnotherPredicate.instance); Cards cards = new CardsImpl(); game.getBattlefield() diff --git a/Mage.Sets/src/mage/cards/s/SatyrEnchanter.java b/Mage.Sets/src/mage/cards/s/SatyrEnchanter.java index d58f7f3d0deb..a4e1db9c862c 100644 --- a/Mage.Sets/src/mage/cards/s/SatyrEnchanter.java +++ b/Mage.Sets/src/mage/cards/s/SatyrEnchanter.java @@ -32,7 +32,7 @@ public SatyrEnchanter(UUID ownerId, CardSetInfo setInfo) { // Whenever you cast an enchantment spell, draw a card. this.addAbility(new SpellCastControllerTriggeredAbility( - new DrawCardSourceControllerEffect(1), filter, true + new DrawCardSourceControllerEffect(1), filter, false )); } diff --git a/Mage.Sets/src/mage/cards/s/SatyrFiredancer.java b/Mage.Sets/src/mage/cards/s/SatyrFiredancer.java index 6c78ddb103f6..a0b8c2fbfb13 100644 --- a/Mage.Sets/src/mage/cards/s/SatyrFiredancer.java +++ b/Mage.Sets/src/mage/cards/s/SatyrFiredancer.java @@ -85,7 +85,7 @@ public boolean checkTrigger(GameEvent event, Game game) { return false; } MageObject sourceObject = game.getObject(event.getSourceId()); - if (sourceObject == null || !(sourceObject.isInstant() || sourceObject.isSorcery())) { + if (sourceObject == null || !sourceObject.isInstantOrSorcery()) { return false; } for (Effect effect : this.getEffects()) { diff --git a/Mage.Sets/src/mage/cards/s/SavingGrace.java b/Mage.Sets/src/mage/cards/s/SavingGrace.java index e44fe7e5e60a..741e49f40c8d 100644 --- a/Mage.Sets/src/mage/cards/s/SavingGrace.java +++ b/Mage.Sets/src/mage/cards/s/SavingGrace.java @@ -77,9 +77,8 @@ class SavingGraceReplacementEffect extends ReplacementEffectImpl { @Override public boolean checksEventType(GameEvent event, Game game) { switch (event.getType()) { - case DAMAGE_CREATURE: case DAMAGE_PLAYER: - case DAMAGE_PLANESWALKER: + case DAMAGE_PERMANENT: return true; default: return false; @@ -88,10 +87,10 @@ public boolean checksEventType(GameEvent event, Game game) { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.DAMAGE_PLAYER && event.getPlayerId().equals(source.getControllerId())) { - return true; + if (event.getType() == GameEvent.EventType.DAMAGE_PLAYER) { + return event.getPlayerId().equals(source.getControllerId()); } - if (event.getType() == GameEvent.EventType.DAMAGE_CREATURE || event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER) { + if (event.getType() == GameEvent.EventType.DAMAGE_PERMANENT) { Permanent targetPermanent = game.getPermanent(event.getTargetId()); if (targetPermanent != null && targetPermanent.isControlledBy(source.getControllerId())) { diff --git a/Mage.Sets/src/mage/cards/s/ScabClanGiant.java b/Mage.Sets/src/mage/cards/s/ScabClanGiant.java index 73b5b2ef80c0..457a02c4a101 100644 --- a/Mage.Sets/src/mage/cards/s/ScabClanGiant.java +++ b/Mage.Sets/src/mage/cards/s/ScabClanGiant.java @@ -1,36 +1,34 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.FightTargetSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Outcome; import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class ScabClanGiant extends CardImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); + static { filter.add(TargetController.OPPONENT.getControllerPredicate()); } public ScabClanGiant(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{R}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{G}"); this.subtype.add(SubType.GIANT); this.subtype.add(SubType.WARRIOR); @@ -38,7 +36,8 @@ public ScabClanGiant(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(5); // When Scab-Clan Giant enters the battlefield, it fights target creature an opponent controls chosen at random. - Ability ability = new EntersBattlefieldTriggeredAbility(new ScabClanGiantEffect()); + Ability ability = new EntersBattlefieldTriggeredAbility(new FightTargetSourceEffect() + .setText("it fights target creature an opponent controls chosen at random")); Target target = new TargetCreaturePermanent(filter); target.setRandom(true); ability.addTarget(target); @@ -54,33 +53,3 @@ public ScabClanGiant copy() { return new ScabClanGiant(this); } } - -class ScabClanGiantEffect extends OneShotEffect { - - public ScabClanGiantEffect() { - super(Outcome.Damage); - this.staticText = "{this} fights target creature an opponent controls chosen at random"; - } - - public ScabClanGiantEffect(final ScabClanGiantEffect effect) { - super(effect); - } - - @Override - public ScabClanGiantEffect copy() { - return new ScabClanGiantEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent creature1 = game.getPermanent(source.getSourceId()); - Permanent creature2 = game.getPermanent(source.getFirstTarget()); - // 20110930 - 701.10 - if (creature1 != null && creature2 != null) { - if (creature1.isCreature() && creature2.isCreature()) { - return creature1.fight(creature2, source, game); - } - } - return false; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/ScaldingTarn.java b/Mage.Sets/src/mage/cards/s/ScaldingTarn.java index c57abc01a5f4..2d0ee21edc50 100644 --- a/Mage.Sets/src/mage/cards/s/ScaldingTarn.java +++ b/Mage.Sets/src/mage/cards/s/ScaldingTarn.java @@ -20,7 +20,7 @@ public final class ScaldingTarn extends CardImpl { public ScaldingTarn(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.LAND},null); this.frameColor = new ObjectColor("UR"); - this.addAbility(new FetchLandActivatedAbility(EnumSet.of(SubType.ISLAND, SubType.MOUNTAIN))); + this.addAbility(new FetchLandActivatedAbility(SubType.ISLAND, SubType.MOUNTAIN)); } private ScaldingTarn(final ScaldingTarn card) { diff --git a/Mage.Sets/src/mage/cards/s/ScaleguardSentinels.java b/Mage.Sets/src/mage/cards/s/ScaleguardSentinels.java index 53c976f3131e..710508cadd11 100644 --- a/Mage.Sets/src/mage/cards/s/ScaleguardSentinels.java +++ b/Mage.Sets/src/mage/cards/s/ScaleguardSentinels.java @@ -1,39 +1,24 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; -import mage.abilities.condition.Condition; -import mage.abilities.costs.common.RevealTargetFromHandCost; -import mage.abilities.effects.common.InfoEffect; +import mage.abilities.condition.common.RevealedOrControlledDragonCondition; +import mage.abilities.costs.common.RevealDragonFromHandCost; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AbilityType; import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; -import mage.filter.FilterCard; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.common.TargetCardInHand; import mage.watchers.common.DragonOnTheBattlefieldWhileSpellWasCastWatcher; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class ScaleguardSentinels extends CardImpl { - private static final FilterCard filter = new FilterCard("a Dragon card from your hand (you don't have to)"); - - static { - filter.add(SubType.DRAGON.getPredicate()); - } - public ScaleguardSentinels(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{G}"); this.subtype.add(SubType.HUMAN); @@ -42,26 +27,16 @@ public ScaleguardSentinels(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(3); // As an additional cost to cast Scaleguard Sentinels, you may reveal a Dragon card from your hand. - this.getSpellAbility().addEffect(new InfoEffect("as an additional cost to cast this spell, you may reveal a Dragon card from your hand")); + this.getSpellAbility().addCost(new RevealDragonFromHandCost()); // Scaleguard Sentinels enters the battlefield with a +1/+1 counter on it if you revealed a Dragon card or controlled a Dragon as you cast Scaleguard Sentinels. - this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(), true), - ScaleguardSentinelsCondition.instance, - "{this} enters the battlefield with a +1/+1 counter on it if you revealed a Dragon card or controlled a Dragon as you cast {this}.", ""), - new DragonOnTheBattlefieldWhileSpellWasCastWatcher()); - - } - - @Override - public void adjustCosts(Ability ability, Game game) { - if (ability.getAbilityType() == AbilityType.SPELL) { - Player controller = game.getPlayer(ability.getControllerId()); - if (controller != null) { - if (controller.getHand().count(filter, game) > 0) { - ability.addCost(new RevealTargetFromHandCost(new TargetCardInHand(0, 1, filter))); - } - } - } + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), + RevealedOrControlledDragonCondition.instance, "{this} enters the battlefield " + + "with a +1/+1 counter on it if you revealed a Dragon card " + + "or controlled a Dragon as you cast this spell.", "" + ), new DragonOnTheBattlefieldWhileSpellWasCastWatcher() + ); } private ScaleguardSentinels(final ScaleguardSentinels card) { @@ -73,18 +48,3 @@ public ScaleguardSentinels copy() { return new ScaleguardSentinels(this); } } - -enum ScaleguardSentinelsCondition implements Condition { - - instance; - - @Override - public boolean apply(Game game, Ability source) { - Permanent sourcePermanent = game.getPermanentEntering(source.getSourceId()); - if (sourcePermanent != null) { - DragonOnTheBattlefieldWhileSpellWasCastWatcher watcher = game.getState().getWatcher(DragonOnTheBattlefieldWhileSpellWasCastWatcher.class); - return (watcher != null && watcher.castWithConditionTrue(sourcePermanent.getSpellAbility().getId())); - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/s/Scapeshift.java b/Mage.Sets/src/mage/cards/s/Scapeshift.java index e08827de9e70..350f1ab5a3c5 100644 --- a/Mage.Sets/src/mage/cards/s/Scapeshift.java +++ b/Mage.Sets/src/mage/cards/s/Scapeshift.java @@ -45,7 +45,7 @@ class ScapeshiftEffect extends OneShotEffect { public ScapeshiftEffect() { super(Outcome.Neutral); - staticText = "Sacrifice any number of lands. Search your library for that many land cards, put them onto the battlefield tapped, then shuffle your library"; + staticText = "Sacrifice any number of lands. Search your library for that many land cards, put them onto the battlefield tapped, then shuffle"; } public ScapeshiftEffect(final ScapeshiftEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/Scaretiller.java b/Mage.Sets/src/mage/cards/s/Scaretiller.java index bca852d03fea..e12a8c1ba037 100644 --- a/Mage.Sets/src/mage/cards/s/Scaretiller.java +++ b/Mage.Sets/src/mage/cards/s/Scaretiller.java @@ -37,7 +37,7 @@ public Scaretiller(UUID ownerId, CardSetInfo setInfo) { )); // • Return target land card from your graveyard to the battlefield tapped. - Mode mode = new Mode(new ReturnFromGraveyardToBattlefieldTargetEffect(true, false)); + Mode mode = new Mode(new ReturnFromGraveyardToBattlefieldTargetEffect(true)); mode.addTarget(new TargetCardInYourGraveyard(filter)); ability.addMode(mode); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/ScatteringStroke.java b/Mage.Sets/src/mage/cards/s/ScatteringStroke.java index 7a1cde9e4c37..e95e297b5a4b 100644 --- a/Mage.Sets/src/mage/cards/s/ScatteringStroke.java +++ b/Mage.Sets/src/mage/cards/s/ScatteringStroke.java @@ -47,7 +47,7 @@ class ScatteringStrokeEffect extends OneShotEffect { public ScatteringStrokeEffect() { super(Outcome.Benefit); - this.staticText = "Counter target spell. Clash with an opponent. If you win, at the beginning of your next main phase, you may add {X}, where X is that spell's converted mana cost"; + this.staticText = "Counter target spell. Clash with an opponent. If you win, at the beginning of your next main phase, you may add {X}, where X is that spell's mana value"; } public ScatteringStrokeEffect(final ScatteringStrokeEffect effect) { @@ -66,7 +66,7 @@ public boolean apply(Game game, Ability source) { if (controller != null && spell != null) { game.getStack().counter(spell.getId(), source, game); if (ClashEffect.getInstance().apply(game, source)) { - Effect effect = new AddManaToManaPoolSourceControllerEffect(new Mana(0, 0, 0, 0, 0, 0, 0, spell.getConvertedManaCost())); + Effect effect = new AddManaToManaPoolSourceControllerEffect(new Mana(0, 0, 0, 0, 0, 0, 0, spell.getManaValue())); AtTheBeginOfMainPhaseDelayedTriggeredAbility delayedAbility = new AtTheBeginOfMainPhaseDelayedTriggeredAbility(effect, true, TargetController.YOU, AtTheBeginOfMainPhaseDelayedTriggeredAbility.PhaseSelection.NEXT_MAIN); game.addDelayedTriggeredAbility(delayedAbility, source); diff --git a/Mage.Sets/src/mage/cards/s/SchemingSymmetry.java b/Mage.Sets/src/mage/cards/s/SchemingSymmetry.java index a7073d0f8ca1..0518bc12727e 100644 --- a/Mage.Sets/src/mage/cards/s/SchemingSymmetry.java +++ b/Mage.Sets/src/mage/cards/s/SchemingSymmetry.java @@ -42,7 +42,7 @@ class SchemingSymmetryEffect extends OneShotEffect { SchemingSymmetryEffect() { super(Outcome.Benefit); staticText = "Choose two target players. Each of them searches their library for a card, " + - "then shuffles their library and puts that card on top of it."; + "then shuffles and puts that card on top of it."; } private SchemingSymmetryEffect(final SchemingSymmetryEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/Schismotivate.java b/Mage.Sets/src/mage/cards/s/Schismotivate.java index 131131371f61..c1ded39f941f 100644 --- a/Mage.Sets/src/mage/cards/s/Schismotivate.java +++ b/Mage.Sets/src/mage/cards/s/Schismotivate.java @@ -12,7 +12,7 @@ import mage.constants.Outcome; import mage.constants.SubLayer; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/s/ScholarshipSponsor.java b/Mage.Sets/src/mage/cards/s/ScholarshipSponsor.java new file mode 100644 index 000000000000..b2dbf27f6b9e --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ScholarshipSponsor.java @@ -0,0 +1,108 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Controllable; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInLibrary; + +import java.util.Map; +import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class ScholarshipSponsor extends CardImpl { + + public ScholarshipSponsor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ADVISOR); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When Scholarship Sponsor enters the battlefield, each player who controls fewer lands than the player who controls the most lands searches their library for a number of basic land cards less than or equal to the difference, puts those cards onto the battlefield tapped, then shuffles. + this.addAbility(new EntersBattlefieldTriggeredAbility(new ScholarshipSponsorEffect())); + } + + private ScholarshipSponsor(final ScholarshipSponsor card) { + super(card); + } + + @Override + public ScholarshipSponsor copy() { + return new ScholarshipSponsor(this); + } +} + +class ScholarshipSponsorEffect extends OneShotEffect { + + ScholarshipSponsorEffect() { + super(Outcome.Benefit); + staticText = "each player who controls fewer lands than the player who controls the most lands " + + "searches their library for a number of basic land cards less than or equal to the difference, " + + "puts those cards onto the battlefield tapped, then shuffles"; + } + + private ScholarshipSponsorEffect(final ScholarshipSponsorEffect effect) { + super(effect); + } + + @Override + public ScholarshipSponsorEffect copy() { + return new ScholarshipSponsorEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Map playerMap = game + .getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_LAND, source.getControllerId(), source.getSourceId(), game + ) + .stream() + .map(Controllable::getControllerId) + .collect(Collectors.toMap(Function.identity(), x -> 1, Integer::sum)); + int maxValue = playerMap + .values() + .stream() + .mapToInt(x -> x) + .max() + .orElse(0); + if (maxValue < 1) { + return false; + } + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + int diff = maxValue - playerMap.getOrDefault(playerId, 0); + if (player == null || diff < 1) { + continue; + } + TargetCardInLibrary target = new TargetCardInLibrary(diff, StaticFilters.FILTER_CARD_BASIC_LAND); + player.searchLibrary(target, source, game); + Cards cards = new CardsImpl(target.getTargets()); + cards.retainZone(Zone.LIBRARY, game); + player.moveCards( + cards.getCards(game), Zone.BATTLEFIELD, source, game, + true, false, false, null + ); + player.shuffleLibrary(source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/ScionOfDarkness.java b/Mage.Sets/src/mage/cards/s/ScionOfDarkness.java index 81c6edee740d..1db5653daab6 100644 --- a/Mage.Sets/src/mage/cards/s/ScionOfDarkness.java +++ b/Mage.Sets/src/mage/cards/s/ScionOfDarkness.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.game.events.DamagedPlayerEvent; import mage.game.events.GameEvent; @@ -95,6 +95,6 @@ public boolean checkTrigger(GameEvent event, Game game) { public String getRule() { return "Whenever {this} deals combat damage to a player, " + "you may put target creature card from that player's " - + "graveyard onto the battlefield under your control"; + + "graveyard onto the battlefield under your control."; } } diff --git a/Mage.Sets/src/mage/cards/s/ScionOfTheUrDragon.java b/Mage.Sets/src/mage/cards/s/ScionOfTheUrDragon.java index eb9ee99c8db0..61d588c0f704 100644 --- a/Mage.Sets/src/mage/cards/s/ScionOfTheUrDragon.java +++ b/Mage.Sets/src/mage/cards/s/ScionOfTheUrDragon.java @@ -61,7 +61,7 @@ class ScionOfTheUrDragonEffect extends SearchEffect { public ScionOfTheUrDragonEffect() { super(new TargetCardInLibrary(filter), Outcome.Copy); - staticText = "Search your library for a Dragon permanent card and put it into your graveyard. If you do, {this} becomes a copy of that card until end of turn. Then shuffle your library."; + staticText = "Search your library for a Dragon permanent card and put it into your graveyard. If you do, {this} becomes a copy of that card until end of turn. Then shuffle."; } ScionOfTheUrDragonEffect(final ScionOfTheUrDragonEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/ScorchedRuins.java b/Mage.Sets/src/mage/cards/s/ScorchedRuins.java index effc0d480b86..2fc70183dcc4 100644 --- a/Mage.Sets/src/mage/cards/s/ScorchedRuins.java +++ b/Mage.Sets/src/mage/cards/s/ScorchedRuins.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.cards.s; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/s/ScornedVillager.java b/Mage.Sets/src/mage/cards/s/ScornedVillager.java index 85a57937f0da..69cce60517ab 100644 --- a/Mage.Sets/src/mage/cards/s/ScornedVillager.java +++ b/Mage.Sets/src/mage/cards/s/ScornedVillager.java @@ -1,21 +1,15 @@ - package mage.cards.s; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.keyword.TransformAbility; import mage.abilities.mana.GreenManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** * @author North @@ -35,12 +29,10 @@ public ScornedVillager(UUID ownerId, CardSetInfo setInfo) { // {tap}: Add {G}. this.addAbility(new GreenManaAbility()); + // At the beginning of each upkeep, if no spells were cast last turn, transform Scorned Villager. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, - NoSpellsWereCastLastTurnCondition.instance, - TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private ScornedVillager(final ScornedVillager card) { diff --git a/Mage.Sets/src/mage/cards/s/ScourgeOfKherRidges.java b/Mage.Sets/src/mage/cards/s/ScourgeOfKherRidges.java index 9f8d5fb4f204..ddd7a5237fd6 100644 --- a/Mage.Sets/src/mage/cards/s/ScourgeOfKherRidges.java +++ b/Mage.Sets/src/mage/cards/s/ScourgeOfKherRidges.java @@ -16,7 +16,7 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/s/ScoutingTrek.java b/Mage.Sets/src/mage/cards/s/ScoutingTrek.java index b7959b6fc915..04d531a4df39 100644 --- a/Mage.Sets/src/mage/cards/s/ScoutingTrek.java +++ b/Mage.Sets/src/mage/cards/s/ScoutingTrek.java @@ -1,24 +1,24 @@ package mage.cards.s; -import java.util.UUID; import mage.abilities.effects.common.RecruiterEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterBasicLandCard; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author LoneFox */ public final class ScoutingTrek extends CardImpl { public ScoutingTrek(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}"); // Search your library for any number of basic land cards. Reveal those cards, then shuffle your library and put them on top of it. - this.getSpellAbility().addEffect(new RecruiterEffect(new FilterBasicLandCard("basic land cards"))); + this.getSpellAbility().addEffect(new RecruiterEffect(StaticFilters.FILTER_CARD_BASIC_LANDS).setText("search your library for any number of basic land cards, reveal those cards, then shuffle and put them on top")); } private ScoutingTrek(final ScoutingTrek card) { diff --git a/Mage.Sets/src/mage/cards/s/ScrabblingClaws.java b/Mage.Sets/src/mage/cards/s/ScrabblingClaws.java index 357f45b6bbb3..97cc5f9fc114 100644 --- a/Mage.Sets/src/mage/cards/s/ScrabblingClaws.java +++ b/Mage.Sets/src/mage/cards/s/ScrabblingClaws.java @@ -16,7 +16,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; diff --git a/Mage.Sets/src/mage/cards/s/ScrapTrawler.java b/Mage.Sets/src/mage/cards/s/ScrapTrawler.java index afff3db500e7..5934f5854c8d 100644 --- a/Mage.Sets/src/mage/cards/s/ScrapTrawler.java +++ b/Mage.Sets/src/mage/cards/s/ScrapTrawler.java @@ -12,7 +12,7 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.common.FilterArtifactCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; @@ -36,7 +36,7 @@ public ScrapTrawler(UUID ownerId, CardSetInfo setInfo) { // Whenever Scrap Trawler or another artifact you control is put into a graveyard from the battlefield, // return to your hand target artifact card in your graveyard with lesser converted mana cost. Ability ability = new ScrapTrawlerTriggeredAbility(); - ability.addTarget(new TargetCardInYourGraveyard(new FilterArtifactCard("artifact card in your graveyard with lesser converted mana cost"))); + ability.addTarget(new TargetCardInYourGraveyard(new FilterArtifactCard("artifact card in your graveyard with lesser mana value"))); this.addAbility(ability); } @@ -54,7 +54,7 @@ class ScrapTrawlerTriggeredAbility extends TriggeredAbilityImpl { public ScrapTrawlerTriggeredAbility() { super(Zone.BATTLEFIELD, new ReturnToHandTargetEffect()); - getEffects().get(0).setText("return to your hand target artifact card in your graveyard with lesser converted mana cost"); + getEffects().get(0).setText("return to your hand target artifact card in your graveyard with lesser mana value"); } public ScrapTrawlerTriggeredAbility(final ScrapTrawlerTriggeredAbility ability) { @@ -79,8 +79,8 @@ public boolean checkTrigger(GameEvent event, Game game) { if (permanent != null && permanent.isControlledBy(this.getControllerId()) && permanent.isArtifact()) { - FilterCard filter = new FilterArtifactCard("artifact card in your graveyard with converted mana cost less than " + permanent.getManaCost().convertedManaCost()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, permanent.getManaCost().convertedManaCost())); + FilterCard filter = new FilterArtifactCard("artifact card in your graveyard with mana value less than " + permanent.getManaCost().manaValue()); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, permanent.getManaCost().manaValue())); TargetCardInYourGraveyard target = new TargetCardInYourGraveyard(filter); getTargets().clear(); addTarget(target); diff --git a/Mage.Sets/src/mage/cards/s/Scrounge.java b/Mage.Sets/src/mage/cards/s/Scrounge.java index 31a6c3199834..a41d166a8f28 100644 --- a/Mage.Sets/src/mage/cards/s/Scrounge.java +++ b/Mage.Sets/src/mage/cards/s/Scrounge.java @@ -9,7 +9,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.common.FilterArtifactCard; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInGraveyard; diff --git a/Mage.Sets/src/mage/cards/s/ScroungingBandar.java b/Mage.Sets/src/mage/cards/s/ScroungingBandar.java index 7281614abb4b..fe245a17eaf3 100644 --- a/Mage.Sets/src/mage/cards/s/ScroungingBandar.java +++ b/Mage.Sets/src/mage/cards/s/ScroungingBandar.java @@ -17,7 +17,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/s/ScurridColony.java b/Mage.Sets/src/mage/cards/s/ScurridColony.java new file mode 100644 index 000000000000..2c2a10d59cee --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ScurridColony.java @@ -0,0 +1,55 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.common.LandsYouControlHint; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ScurridColony extends CardImpl { + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, ComparisonType.MORE_THAN, 7 + ); + + public ScurridColony(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.SQUIRREL); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Scurrid Colony gets +2/+2 as long as you control eight or more lands. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), + condition, "{this} gets +2/+2 as long as you control eight or more lands" + )).addHint(LandsYouControlHint.instance)); + } + + private ScurridColony(final ScurridColony card) { + super(card); + } + + @Override + public ScurridColony copy() { + return new ScurridColony(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/ScytheSpecter.java b/Mage.Sets/src/mage/cards/s/ScytheSpecter.java index c3fe6c80d7c0..9e4006e302d5 100644 --- a/Mage.Sets/src/mage/cards/s/ScytheSpecter.java +++ b/Mage.Sets/src/mage/cards/s/ScytheSpecter.java @@ -56,7 +56,7 @@ class ScytheSpecterEffect extends OneShotEffect { public ScytheSpecterEffect() { super(Outcome.Discard); - this.staticText = "each opponent discards a card. Each player who discarded a card with the highest converted mana cost among cards discarded this way loses life equal to that converted mana cost"; + this.staticText = "each opponent discards a card. Each player who discarded a card with the highest mana value among cards discarded this way loses life equal to that mana value"; } public ScytheSpecterEffect(final ScytheSpecterEffect effect) { @@ -78,7 +78,7 @@ public boolean apply(Game game, Ability source) { opponent.chooseTarget(Outcome.Discard, target, source, game); Card targetCard = game.getCard(target.getFirstTarget()); if (targetCard != null) { - currentCMC = targetCard.getConvertedManaCost(); + currentCMC = targetCard.getManaValue(); if (highestCMC <= currentCMC) { highestCMC = currentCMC; } @@ -96,7 +96,7 @@ public boolean apply(Game game, Ability source) { for (UUID playerId : game.getOpponents(controller.getId())) {//lose life equal to CMC Card card = cardDiscarded.get(playerId); - if ((card != null) && (card.getConvertedManaCost() == highestCMC)) { + if ((card != null) && (card.getManaValue() == highestCMC)) { Player opponent = game.getPlayer(playerId); if (opponent != null && discardedCheck.get(playerId) == 1) {//check that card was discarded diff --git a/Mage.Sets/src/mage/cards/s/SeaGateStormcaller.java b/Mage.Sets/src/mage/cards/s/SeaGateStormcaller.java index d746a7adf853..348aaaf66d93 100644 --- a/Mage.Sets/src/mage/cards/s/SeaGateStormcaller.java +++ b/Mage.Sets/src/mage/cards/s/SeaGateStormcaller.java @@ -42,7 +42,7 @@ public SeaGateStormcaller(UUID ownerId, CardSetInfo setInfo) { new CreateDelayedTriggeredAbilityEffect(new SeaGateStormcallerDelayedTriggeredAbility(true)), new CreateDelayedTriggeredAbilityEffect(new SeaGateStormcallerDelayedTriggeredAbility(false)), KickedCondition.instance, "copy the next instant or sorcery spell " + - "with converted mana cost 2 or less you cast this turn when you cast it. " + + "with mana value 2 or less you cast this turn when you cast it. " + "If {this} was kicked, copy that spell twice instead. You may choose new targets for the copies." ))); } @@ -90,7 +90,7 @@ public boolean checkTrigger(GameEvent event, Game game) { return false; } Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell == null || !spell.isInstantOrSorcery() || spell.getConvertedManaCost() > 2) { + if (spell == null || !spell.isInstantOrSorcery() || spell.getManaValue() > 2) { return false; } for (Effect effect : this.getEffects()) { @@ -101,7 +101,7 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { - return "When you cast your next instant or sorcery spell this turn with converted mana cost 2 or less, " + + return "When you cast your next instant or sorcery spell this turn with mana value 2 or less, " + "copy that spell" + (twice ? " twice" : "") + ". You may choose new targets for the cop" + (twice ? "ies" : "y") + "."; } diff --git a/Mage.Sets/src/mage/cards/s/SearchForAzcanta.java b/Mage.Sets/src/mage/cards/s/SearchForAzcanta.java index 0d35d779632f..32cb3ac14462 100644 --- a/Mage.Sets/src/mage/cards/s/SearchForAzcanta.java +++ b/Mage.Sets/src/mage/cards/s/SearchForAzcanta.java @@ -76,7 +76,7 @@ public boolean apply(Game game, Ability source) { Card card = controller.getLibrary().getFromTop(game); if (card != null) { controller.lookAtCards(sourceObject.getIdName(), new CardsImpl(card), game); - if (controller.chooseUse(Outcome.Neutral, "Do you wish to put the card into your graveyard?", source, game)) { + if (controller.chooseUse(Outcome.Neutral, "Put that card into your graveyard?", source, game)) { controller.moveCards(card, Zone.GRAVEYARD, source, game); } if (controller.getGraveyard().size() > 6 && controller.chooseUse(Outcome.Neutral, "Transform " + sourceObject.getLogName() + "?", source, game)) { diff --git a/Mage.Sets/src/mage/cards/s/SearchTheCity.java b/Mage.Sets/src/mage/cards/s/SearchTheCity.java index 95427da935f5..1454aa0b2dd5 100644 --- a/Mage.Sets/src/mage/cards/s/SearchTheCity.java +++ b/Mage.Sets/src/mage/cards/s/SearchTheCity.java @@ -15,7 +15,6 @@ import mage.game.ExileZone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.game.turn.TurnMod; @@ -154,11 +153,16 @@ public SearchTheCityExiledCardToHandEffect(final SearchTheCityExiledCardToHandEf @Override public boolean apply(Game game, Ability source) { String cardName = (String) this.getValue("cardName"); + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } ExileZone searchTheCityExileZone = game.getExile().getExileZone(source.getSourceId()); - if (cardName != null && searchTheCityExileZone != null) { + if (cardName != null + && searchTheCityExileZone != null) { for (Card card : searchTheCityExileZone.getCards(game)) { if (CardUtil.haveSameNames(card, cardName, game)) { - if (card.moveToZone(Zone.HAND, source, game, true)) { + if (controller.moveCards(card, Zone.HAND, source, game)) { game.informPlayers("Search the City: put " + card.getName() + " into owner's hand"); } searchTheCityExileZone.remove(card); diff --git a/Mage.Sets/src/mage/cards/s/SeasonOfGrowth.java b/Mage.Sets/src/mage/cards/s/SeasonOfGrowth.java index d347018ab50d..dcee18948d59 100644 --- a/Mage.Sets/src/mage/cards/s/SeasonOfGrowth.java +++ b/Mage.Sets/src/mage/cards/s/SeasonOfGrowth.java @@ -9,7 +9,7 @@ import mage.constants.CardType; import mage.filter.FilterSpell; import mage.filter.StaticFilters; -import mage.filter.predicate.other.TargetsPermanentPredicate; +import mage.filter.predicate.mageobject.TargetsPermanentPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/s/SeasonsPast.java b/Mage.Sets/src/mage/cards/s/SeasonsPast.java index d478fb5ecb0a..efdc6f6abc5f 100644 --- a/Mage.Sets/src/mage/cards/s/SeasonsPast.java +++ b/Mage.Sets/src/mage/cards/s/SeasonsPast.java @@ -47,7 +47,7 @@ class SeasonsPastEffect extends OneShotEffect { public SeasonsPastEffect() { super(Outcome.ReturnToHand); - this.staticText = "Return any number of cards with different converted mana costs from your graveyard to your hand"; + this.staticText = "Return any number of cards with different mana values from your graveyard to your hand"; } public SeasonsPastEffect(final SeasonsPastEffect effect) { @@ -75,7 +75,7 @@ public boolean apply(Game game, Ability source) { class SeasonsPastTarget extends TargetCardInYourGraveyard { public SeasonsPastTarget() { - super(0, Integer.MAX_VALUE, new FilterCard("cards with different converted mana costs from your graveyard")); + super(0, Integer.MAX_VALUE, new FilterCard("cards with different mana values from your graveyard")); } public SeasonsPastTarget(SeasonsPastTarget target) { @@ -88,14 +88,14 @@ public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game ga for (UUID targetId : this.getTargets()) { Card card = game.getCard(targetId); if (card != null) { - usedCMC.add(card.getConvertedManaCost()); + usedCMC.add(card.getManaValue()); } } Set possibleTargets = super.possibleTargets(sourceId, sourceControllerId, game); Set leftPossibleTargets = new HashSet<>(); for (UUID targetId : possibleTargets) { Card card = game.getCard(targetId); - if (card != null && !usedCMC.contains(card.getConvertedManaCost())) { + if (card != null && !usedCMC.contains(card.getManaValue())) { leftPossibleTargets.add(targetId); } } @@ -109,11 +109,11 @@ public boolean canTarget(UUID playerId, UUID objectId, Ability source, Game game for (UUID targetId : this.getTargets()) { Card card = game.getCard(targetId); if (card != null) { - usedCMC.add(card.getConvertedManaCost()); + usedCMC.add(card.getManaValue()); } } Card card = game.getCard(objectId); - return card != null && !usedCMC.contains(card.getConvertedManaCost()); + return card != null && !usedCMC.contains(card.getManaValue()); } return false; } diff --git a/Mage.Sets/src/mage/cards/s/SecondSunrise.java b/Mage.Sets/src/mage/cards/s/SecondSunrise.java index ea869b2237eb..7d8a17e573f6 100644 --- a/Mage.Sets/src/mage/cards/s/SecondSunrise.java +++ b/Mage.Sets/src/mage/cards/s/SecondSunrise.java @@ -1,37 +1,32 @@ - package mage.cards.s; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.WatcherScope; import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.card.PutIntoGraveFromBattlefieldThisTurnPredicate; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; -import mage.watchers.Watcher; +import mage.players.Player; +import mage.watchers.common.CardsPutIntoGraveyardWatcher; -import java.util.ArrayList; -import java.util.List; import java.util.UUID; /** - * * @author LevelX2 */ public final class SecondSunrise extends CardImpl { public SecondSunrise(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}{W}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}{W}"); // Each player returns to the battlefield all artifact, creature, enchantment, and land cards in their graveyard that were put there from the battlefield this turn. this.getSpellAbility().addEffect(new SecondSunriseEffect()); - this.getSpellAbility().addWatcher(new SecondSunriseWatcher()); + this.getSpellAbility().addWatcher(new CardsPutIntoGraveyardWatcher()); } private SecondSunrise(final SecondSunrise card) { @@ -46,30 +41,41 @@ public SecondSunrise copy() { class SecondSunriseEffect extends OneShotEffect { + private static final FilterCard filter = new FilterCard(); + + static { + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.CREATURE.getPredicate(), + CardType.ENCHANTMENT.getPredicate(), + CardType.LAND.getPredicate() + )); + filter.add(PutIntoGraveFromBattlefieldThisTurnPredicate.instance); + } + SecondSunriseEffect() { super(Outcome.PutCardInPlay); - staticText = "Each player returns to the battlefield all artifact, creature, enchantment, and land cards in their graveyard that were put there from the battlefield this turn"; + staticText = "Each player returns to the battlefield all artifact, creature, enchantment, " + + "and land cards in their graveyard that were put there from the battlefield this turn."; } - SecondSunriseEffect(final SecondSunriseEffect effect) { + private SecondSunriseEffect(final SecondSunriseEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { - SecondSunriseWatcher watcher = game.getState().getWatcher(SecondSunriseWatcher.class); - if (watcher != null) { - for (UUID id : watcher.getCards()) { - Card c = game.getCard(id); - if (c != null && game.getState().getZone(id) == Zone.GRAVEYARD) { - if (c.isArtifact() || c.isCreature() || - c.isEnchantment() || c.isLand()) - c.moveToZone(Zone.BATTLEFIELD, source, game, false); - } + boolean result = false; + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; } - return true; + result |= player.moveCards(player.getGraveyard().getCards( + filter, source.getSourceId(), source.getControllerId(), game + ), Zone.BATTLEFIELD, source, game); } - return false; + return result; } @Override @@ -77,28 +83,3 @@ public SecondSunriseEffect copy() { return new SecondSunriseEffect(this); } } - -class SecondSunriseWatcher extends Watcher { - private List cards = new ArrayList<>(); - - public SecondSunriseWatcher() { - super(WatcherScope.GAME); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.ZONE_CHANGE && ((ZoneChangeEvent)event).isDiesEvent()) { - cards.add(event.getTargetId()); - } - } - - @Override - public void reset() { - super.reset(); - cards.clear(); - } - - public List getCards(){ - return cards; - } -} diff --git a/Mage.Sets/src/mage/cards/s/SecretPlans.java b/Mage.Sets/src/mage/cards/s/SecretPlans.java index bc584763f6e6..be1ca35e0d51 100644 --- a/Mage.Sets/src/mage/cards/s/SecretPlans.java +++ b/Mage.Sets/src/mage/cards/s/SecretPlans.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.TurnedFaceUpAllTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -10,13 +8,13 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; -import mage.filter.common.FilterControlledPermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.other.FaceDownPredicate; +import mage.filter.predicate.card.FaceDownPredicate; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class SecretPlans extends CardImpl { @@ -28,15 +26,18 @@ public final class SecretPlans extends CardImpl { } public SecretPlans(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{G}{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{G}{U}"); // Face-down creatures you control get +0/+1. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(0,1, Duration.WhileOnBattlefield, filter))); + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 0, 1, Duration.WhileOnBattlefield, filter + ))); // Whenever a permanent you control is turned face up, draw a card. - this.addAbility(new TurnedFaceUpAllTriggeredAbility(new DrawCardSourceControllerEffect(1), new FilterControlledPermanent())); - + this.addAbility(new TurnedFaceUpAllTriggeredAbility( + new DrawCardSourceControllerEffect(1), + StaticFilters.FILTER_CONTROLLED_A_PERMANENT + )); } private SecretPlans(final SecretPlans card) { diff --git a/Mage.Sets/src/mage/cards/s/SecretRendezvous.java b/Mage.Sets/src/mage/cards/s/SecretRendezvous.java new file mode 100644 index 000000000000..91058adafba0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SecretRendezvous.java @@ -0,0 +1,34 @@ +package mage.cards.s; + +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.DrawCardTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SecretRendezvous extends CardImpl { + + public SecretRendezvous(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{W}{W}"); + + // You and target opponent each draw three cards. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(3).setText("you")); + this.getSpellAbility().addEffect(new DrawCardTargetEffect(3).setText("and target opponent each draw three cards")); + this.getSpellAbility().addTarget(new TargetOpponent()); + } + + private SecretRendezvous(final SecretRendezvous card) { + super(card); + } + + @Override + public SecretRendezvous copy() { + return new SecretRendezvous(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SecretSalvage.java b/Mage.Sets/src/mage/cards/s/SecretSalvage.java index f07f4ed354c7..b348f0655878 100644 --- a/Mage.Sets/src/mage/cards/s/SecretSalvage.java +++ b/Mage.Sets/src/mage/cards/s/SecretSalvage.java @@ -47,7 +47,7 @@ class SecretSalvageEffect extends OneShotEffect { public SecretSalvageEffect() { super(Outcome.DrawCard); staticText = "Exile target nonland card from your graveyard. Search your library for any number of cards with the same name as that card, " - + "reveal them, and put them into your hand. Then shuffle your library"; + + "reveal them, and put them into your hand. Then shuffle"; } public SecretSalvageEffect(final SecretSalvageEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SedgemoorWitch.java b/Mage.Sets/src/mage/cards/s/SedgemoorWitch.java new file mode 100644 index 000000000000..7256fb5efc8f --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SedgemoorWitch.java @@ -0,0 +1,48 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.abilities.keyword.WardAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.WitherbloomToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SedgemoorWitch extends CardImpl { + + public SedgemoorWitch(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Menace + this.addAbility(new MenaceAbility()); + + // Ward—Pay 3 life. + this.addAbility(new WardAbility(new PayLifeCost(3))); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, create a 1/1 black and green Pest creature token with "Whenever this creature dies, you gain 1 life." + this.addAbility(new MagecraftAbility(new CreateTokenEffect(new WitherbloomToken()))); + } + + private SedgemoorWitch(final SedgemoorWitch card) { + super(card); + } + + @Override + public SedgemoorWitch copy() { + return new SedgemoorWitch(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SeedguideAsh.java b/Mage.Sets/src/mage/cards/s/SeedguideAsh.java index bc423956d0b1..497605895f10 100644 --- a/Mage.Sets/src/mage/cards/s/SeedguideAsh.java +++ b/Mage.Sets/src/mage/cards/s/SeedguideAsh.java @@ -19,7 +19,7 @@ */ public final class SeedguideAsh extends CardImpl { - private static final FilterCard filter = new FilterCard("Forest"); + private static final FilterCard filter = new FilterCard("Forest cards"); static { filter.add(SubType.FOREST.getPredicate()); @@ -33,7 +33,7 @@ public SeedguideAsh(UUID ownerId, CardSetInfo setInfo) { this.power = new MageInt(4); this.toughness = new MageInt(4); // When Seedguide Ash dies, you may search your library for up to three Forest cards and put them onto the battlefield tapped. If you do, shuffle your library. - this.addAbility(new DiesSourceTriggeredAbility(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 3, filter), true, false, Outcome.PutLandInPlay), true)); + this.addAbility(new DiesSourceTriggeredAbility(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 3, filter), true, true, Outcome.PutLandInPlay), true)); } private SeedguideAsh(final SeedguideAsh card) { diff --git a/Mage.Sets/src/mage/cards/s/SeedsOfInnocence.java b/Mage.Sets/src/mage/cards/s/SeedsOfInnocence.java index 9cd973774694..51f2ff6deeed 100644 --- a/Mage.Sets/src/mage/cards/s/SeedsOfInnocence.java +++ b/Mage.Sets/src/mage/cards/s/SeedsOfInnocence.java @@ -41,7 +41,7 @@ class SeedsOfInnocenceEffect extends OneShotEffect { public SeedsOfInnocenceEffect() { super(Outcome.DestroyPermanent); - this.staticText = "Destroy all artifacts. They can't be regenerated. The controller of each of those artifacts gains life equal to its converted mana cost"; + this.staticText = "Destroy all artifacts. They can't be regenerated. The controller of each of those artifacts gains life equal to its mana value"; } public SeedsOfInnocenceEffect(final SeedsOfInnocenceEffect effect) { @@ -59,7 +59,7 @@ public boolean apply(Game game, Ability source) { if (controller != null) { for (Permanent artifact : game.getState().getBattlefield().getActivePermanents(new FilterArtifactPermanent(), controller.getId(), game)) { Player artifactController = game.getPlayer(artifact.getControllerId()); - int cmc = artifact.getConvertedManaCost(); + int cmc = artifact.getManaValue(); if (artifact.destroy(source, game, true)) { if(artifactController != null) { artifactController.gainLife(cmc, game, source); diff --git a/Mage.Sets/src/mage/cards/s/SeedsOfRenewal.java b/Mage.Sets/src/mage/cards/s/SeedsOfRenewal.java index f7fb2cb12263..f933b2bd27ee 100644 --- a/Mage.Sets/src/mage/cards/s/SeedsOfRenewal.java +++ b/Mage.Sets/src/mage/cards/s/SeedsOfRenewal.java @@ -28,7 +28,7 @@ public SeedsOfRenewal(UUID ownerId, CardSetInfo setInfo) { effect.setText("Return up to two target cards from your graveyard to your hand"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, StaticFilters.FILTER_CARD_FROM_YOUR_GRAVEYARD)); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } diff --git a/Mage.Sets/src/mage/cards/s/SeekTheHorizon.java b/Mage.Sets/src/mage/cards/s/SeekTheHorizon.java index 0374bc852a1c..39d47a6d188b 100644 --- a/Mage.Sets/src/mage/cards/s/SeekTheHorizon.java +++ b/Mage.Sets/src/mage/cards/s/SeekTheHorizon.java @@ -20,7 +20,7 @@ public SeekTheHorizon(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{G}"); // Search your library for up to three basic land cards, reveal them, and put them into your hand. Then shuffle your library. - this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 3, StaticFilters.FILTER_CARD_BASIC_LAND), true)); + this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 3, StaticFilters.FILTER_CARD_BASIC_LANDS), true)); } private SeekTheHorizon(final SeekTheHorizon card) { diff --git a/Mage.Sets/src/mage/cards/s/SeersVision.java b/Mage.Sets/src/mage/cards/s/SeersVision.java index 41a425bd0bb2..ce8c726b3c4d 100644 --- a/Mage.Sets/src/mage/cards/s/SeersVision.java +++ b/Mage.Sets/src/mage/cards/s/SeersVision.java @@ -27,7 +27,7 @@ public SeersVision(UUID ownerId, CardSetInfo setInfo) { // Your opponents play with their hands revealed. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayWithHandRevealedEffect(TargetController.OPPONENT))); // Sacrifice Seer's Vision: Look at target player's hand and choose a card from it. That player discards that card. Activate this ability only any time you could cast a sorcery. - Ability ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new DiscardCardYouChooseTargetEffect(TargetController.ANY), new SacrificeSourceCost()); + Ability ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new DiscardCardYouChooseTargetEffect(TargetController.ANY).setText("look at target player's hand and choose a card from it. That player discards that card"), new SacrificeSourceCost()); ability.addTarget(new TargetPlayer()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SekKuarDeathkeeper.java b/Mage.Sets/src/mage/cards/s/SekKuarDeathkeeper.java index 89ebea7ea559..09e18e4ed23b 100644 --- a/Mage.Sets/src/mage/cards/s/SekKuarDeathkeeper.java +++ b/Mage.Sets/src/mage/cards/s/SekKuarDeathkeeper.java @@ -13,7 +13,7 @@ import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.permanent.token.SekKuarDeathkeeperGravebornToken; diff --git a/Mage.Sets/src/mage/cards/s/SelectiveMemory.java b/Mage.Sets/src/mage/cards/s/SelectiveMemory.java index aa033333b4ce..e4b7d72c035b 100644 --- a/Mage.Sets/src/mage/cards/s/SelectiveMemory.java +++ b/Mage.Sets/src/mage/cards/s/SelectiveMemory.java @@ -42,7 +42,7 @@ class SelectiveMemoryEffect extends OneShotEffect { public SelectiveMemoryEffect() { super(Outcome.Exile); - this.staticText = "Search your library for any number of nonland cards and exile them. Then shuffle your library"; + this.staticText = "Search your library for any number of nonland cards, exile them, then shuffle"; } public SelectiveMemoryEffect(final SelectiveMemoryEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SelfAssembler.java b/Mage.Sets/src/mage/cards/s/SelfAssembler.java index 8f05cdec0777..f6b617040831 100644 --- a/Mage.Sets/src/mage/cards/s/SelfAssembler.java +++ b/Mage.Sets/src/mage/cards/s/SelfAssembler.java @@ -34,7 +34,7 @@ public SelfAssembler(UUID ownerId, CardSetInfo setInfo) { // When Self-Assembler enters the battlefield, you may search your library for an Assembly-Worker creature card, reveal it, put it into your hand, // then shuffle your library. Effect effect = new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 1, filter), true, true); - effect.setText("you may search your library for an Assembly-Worker card, reveal it, put it into your hand, then shuffle your library"); + effect.setText("you may search your library for an Assembly-Worker card, reveal it, put it into your hand, then shuffle"); this.addAbility(new EntersBattlefieldTriggeredAbility(effect, true)); } diff --git a/Mage.Sets/src/mage/cards/s/SelflessGlyphweaver.java b/Mage.Sets/src/mage/cards/s/SelflessGlyphweaver.java new file mode 100644 index 000000000000..51dfff39c291 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SelflessGlyphweaver.java @@ -0,0 +1,99 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.ExileSourceCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyAllEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.PermanentIdPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreatureOrPlaneswalker; + +import java.util.UUID; + +/** + * @author htrajan + */ +public final class SelflessGlyphweaver extends ModalDoubleFacesCard { + + public SelflessGlyphweaver(UUID ownerId, CardSetInfo setInfo) { + super( + ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.HUMAN, SubType.CLERIC}, "{2}{W}", + "Deadly Vanity", + new CardType[]{CardType.SORCERY}, new SubType[]{}, "{5}{B}{B}{B}" + ); + + // 1. + // Selfless Glyphweaver + // Creature - Human Cleric + this.getLeftHalfCard().setPT(2, 3); + + // Exile Selfless Glyphweaver: Creatures you control gain indestructible until end of turn. + this.getLeftHalfCard().addAbility(new SimpleActivatedAbility(new GainAbilityControlledEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURES), new ExileSourceCost())); + + // 2. + // Deadly Vanity + // Sorcery + // Choose a creature or planeswalker, then destroy all other creatures and planeswalkers. + this.getRightHalfCard().getSpellAbility().addEffect(new DeadlyVanityEffect()); + } + + private SelflessGlyphweaver(final SelflessGlyphweaver card) { + super(card); + } + + @Override + public SelflessGlyphweaver copy() { + return new SelflessGlyphweaver(this); + } +} + +class DeadlyVanityEffect extends OneShotEffect { + + DeadlyVanityEffect() { + super(Outcome.Benefit); + staticText = "choose a creature or planeswalker, then destroy all other creatures and planeswalkers"; + } + + DeadlyVanityEffect(DeadlyVanityEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + TargetPermanent target = new TargetCreatureOrPlaneswalker(); + target.setNotTarget(true); + controller.choose(outcome, target, source.getId(), game); + + FilterPermanent filter = new FilterCreatureOrPlaneswalkerPermanent(); + UUID targetId = target.getFirstTarget(); + if (targetId != null) { + filter.add(Predicates.not(new PermanentIdPredicate(targetId))); + } + return new DestroyAllEffect(filter).apply(game, source); + } + + @Override + public DeadlyVanityEffect copy() { + return new DeadlyVanityEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SelflessSavior.java b/Mage.Sets/src/mage/cards/s/SelflessSavior.java index b5fd97e4bc1e..d336af9c87cc 100644 --- a/Mage.Sets/src/mage/cards/s/SelflessSavior.java +++ b/Mage.Sets/src/mage/cards/s/SelflessSavior.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/s/SelvalaHeartOfTheWilds.java b/Mage.Sets/src/mage/cards/s/SelvalaHeartOfTheWilds.java index e8f60bb7e914..a0a72de56bb1 100644 --- a/Mage.Sets/src/mage/cards/s/SelvalaHeartOfTheWilds.java +++ b/Mage.Sets/src/mage/cards/s/SelvalaHeartOfTheWilds.java @@ -17,7 +17,7 @@ import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -35,7 +35,6 @@ public final class SelvalaHeartOfTheWilds extends CardImpl { } private static final String rule = "Whenever another creature enters the battlefield, its controller may draw a card if its power is greater than each other creature's power."; - private static final String rule2 = "Add X mana in any combination of colors, where X is the greatest power among creatures you control."; public SelvalaHeartOfTheWilds(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{G}"); @@ -50,8 +49,8 @@ public SelvalaHeartOfTheWilds(UUID ownerId, CardSetInfo setInfo) { // {G}, {T}: Add X mana in any combination of colors, where X is the greatest power among creatures you control. ManaEffect manaEffect = new AddManaInAnyCombinationEffect( - GreatestPowerAmongControlledCreaturesValue.instance, GreatestPowerAmongControlledCreaturesValue.instance, rule2, - ColoredManaSymbol.B, ColoredManaSymbol.U, ColoredManaSymbol.R, ColoredManaSymbol.W, ColoredManaSymbol.G); + GreatestPowerAmongControlledCreaturesValue.instance, GreatestPowerAmongControlledCreaturesValue.instance, + ColoredManaSymbol.W, ColoredManaSymbol.U, ColoredManaSymbol.B, ColoredManaSymbol.R, ColoredManaSymbol.G); Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, manaEffect, new ManaCostsImpl("{G}")); ability.addCost(new TapSourceCost()); this.addAbility(ability); @@ -97,7 +96,7 @@ public boolean apply(Game game, Ability source) { if (filter2.match(permanent, game)) { Player permanentController = game.getPlayer(permanent.getControllerId()); if (permanentController != null - && permanentController.chooseUse(Outcome.DrawCard, "Would you like to draw a card?", source, game)) { + && permanentController.chooseUse(Outcome.DrawCard, "Draw a card?", source, game)) { permanentController.drawCards(1, source, game); } } diff --git a/Mage.Sets/src/mage/cards/s/SelvalasStampede.java b/Mage.Sets/src/mage/cards/s/SelvalasStampede.java index f75dca4dfadc..1902217202b0 100644 --- a/Mage.Sets/src/mage/cards/s/SelvalasStampede.java +++ b/Mage.Sets/src/mage/cards/s/SelvalasStampede.java @@ -1,21 +1,21 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.effects.common.CouncilsDilemmaVoteEffect; +import mage.abilities.effects.OneShotEffect; import mage.cards.*; +import mage.choices.TwoChoiceVote; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.common.FilterPermanentCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInHand; +import java.util.UUID; + /** - * - * @author JRHerlehy + * @author JRHerlehy, TheElk801 */ public final class SelvalasStampede extends CardImpl { @@ -23,7 +23,7 @@ public SelvalasStampede(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{G}{G}"); // Council's dilemma &mdash Starting with you, each player votes for wild or free. Reveal cards from the top of your library until you reveal a creature card for each wild vote. Put those creature cards onto the battlefield, then shuffle the rest into your library. You may put a permanent card from your hand onto the battlefield for each free vote. - this.getSpellAbility().addEffect(new SelvalasStampedeDilemmaEffect()); + this.getSpellAbility().addEffect(new SelvalasStampedeEffect()); } private SelvalasStampede(final SelvalasStampede card) { @@ -36,63 +36,70 @@ public SelvalasStampede copy() { } } -class SelvalasStampedeDilemmaEffect extends CouncilsDilemmaVoteEffect { +class SelvalasStampedeEffect extends OneShotEffect { - public SelvalasStampedeDilemmaEffect() { - super(Outcome.PutCardInPlay); - this.staticText = "Council's dilemma — Starting with you, each player votes for wild or free. Reveal cards from the top of your library until you reveal a creature card for each wild vote. Put those creature cards onto the battlefield, then shuffle the rest into your library. " - + "You may put a permanent card from your hand onto the battlefield for each free vote"; + SelvalasStampedeEffect() { + super(Outcome.Benefit); + staticText = "Council's dilemma — Starting with you, each player votes for wild or free. " + + "Reveal cards from the top of your library until you reveal a creature card for each wild vote. " + + "Put those creature cards onto the battlefield, then shuffle the rest into your library. " + + "You may put a permanent card from your hand onto the battlefield for each free vote"; } - public SelvalasStampedeDilemmaEffect(final SelvalasStampedeDilemmaEffect effect) { + private SelvalasStampedeEffect(final SelvalasStampedeEffect effect) { super(effect); } @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); + public SelvalasStampedeEffect copy() { + return new SelvalasStampedeEffect(this); + } - //If no controller, exit here and do not vote. - if (controller == null) { + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { return false; } - this.vote("wild", "free", controller, game, source); - - //Wild Votes - if (voteOneCount > 0) { - Cards revealedCards = new CardsImpl(); - Cards toBattlefield = new CardsImpl(); - for (Card card : controller.getLibrary().getCards(game)) { - revealedCards.add(card); - if (card.isCreature()) { - toBattlefield.add(card); - if (toBattlefield.size() == voteOneCount) { - break; - } - } + // Outcome.Detriment - AI will use library will the time (Free choice) + // TODO: add AI hint logic in the choice method, see Tyrant's Choice as example + TwoChoiceVote vote = new TwoChoiceVote("Wild (from library to battlefield)", "Free (from hand to battlefield)", Outcome.Detriment); + vote.doVotes(source, game); + + int wildCount = vote.getVoteCount(true); + int freeCount = vote.getVoteCount(false); + + // Reveal cards from the top of your library until you reveal a creature card for each wild vote. + // Put those creature cards onto the battlefield, then shuffle the rest into your library. + Cards toReveal = new CardsImpl(); + Cards creatureCards = new CardsImpl(); + for (Card card : player.getLibrary().getCards(game)) { + if (creatureCards.size() >= wildCount) { + break; } - controller.revealCards(source, revealedCards, game); - controller.moveCards(toBattlefield, Zone.BATTLEFIELD, source, game); - revealedCards.removeAll(toBattlefield); - if (!revealedCards.isEmpty()) { - controller.shuffleLibrary(source, game); + if (card.isCreature()) { + creatureCards.add(card); } + toReveal.add(card); } - - //Free Votes - if (voteTwoCount > 0) { - TargetCardInHand target = new TargetCardInHand(0, voteTwoCount, new FilterPermanentCard("permanent cards")); - if (controller.choose(outcome, target, source.getSourceId(), game)) { - controller.moveCards(new CardsImpl(target.getTargets()), Zone.BATTLEFIELD, source, game); - } + if (toReveal.size() > 0) { + player.revealCards(source, toReveal, game); + } + if (creatureCards.size() > 0) { + player.moveCards(creatureCards, Zone.BATTLEFIELD, source, game); + } + player.shuffleLibrary(source, game); + + // You may put a permanent card from your hand onto the battlefield for each free vote + if (freeCount > 0) { + TargetCardInHand target = new TargetCardInHand(0, freeCount, StaticFilters.FILTER_CARD_PERMANENT); + player.choose(Outcome.PutCreatureInPlay, player.getHand(), target, game); + creatureCards.clear(); + creatureCards.addAll(target.getTargets()); + player.moveCards(creatureCards, Zone.BATTLEFIELD, source, game); } - return true; - } - - @Override - public SelvalasStampedeDilemmaEffect copy() { - return new SelvalasStampedeDilemmaEffect(this); + return wildCount + freeCount > 0; } } diff --git a/Mage.Sets/src/mage/cards/s/SemestersEnd.java b/Mage.Sets/src/mage/cards/s/SemestersEnd.java new file mode 100644 index 000000000000..b6b1fc6cde08 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SemestersEnd.java @@ -0,0 +1,138 @@ +package mage.cards.s; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetPermanent; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SemestersEnd extends CardImpl { + + private static final FilterPermanent filter + = new FilterCreatureOrPlaneswalkerPermanent("creatures and/or planeswalkers you control"); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + } + + public SemestersEnd(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{W}"); + + // Exile any number of target creatures and/or planeswalkers you control. At the beginning of the next end step, return each of them to the battlefield under its owner's control. Each of them enters the battlefield with an additional +1/+1 counter on it if it's a creature and an additional loyalty counter on it if it's a planeswalker. + this.getSpellAbility().addEffect(new SemestersEndEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(0, Integer.MAX_VALUE, filter)); + } + + private SemestersEnd(final SemestersEnd card) { + super(card); + } + + @Override + public SemestersEnd copy() { + return new SemestersEnd(this); + } +} + +class SemestersEndEffect extends OneShotEffect { + + SemestersEndEffect() { + super(Outcome.Benefit); + staticText = "exile any number of target creatures and/or planeswalkers you control. " + + "At the beginning of the next end step, return each of them to the battlefield " + + "under its owner's control. Each of them enters the battlefield with an additional " + + "+1/+1 counter on it if it's a creature and an additional loyalty counter on it if it's a planeswalker"; + } + + private SemestersEndEffect(final SemestersEndEffect effect) { + super(effect); + } + + @Override + public SemestersEndEffect copy() { + return new SemestersEndEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(targetPointer.getTargets(game, source)); + player.moveCards(cards, Zone.EXILED, source, game); + cards.retainZone(Zone.EXILED, game); + game.addDelayedTriggeredAbility( + new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new SemestersEndReturnEffect(cards, game)), source + ); + return true; + } +} + +class SemestersEndReturnEffect extends OneShotEffect { + + private final Set morSet = new HashSet<>(); + + SemestersEndReturnEffect(Cards cards, Game game) { + super(Outcome.Benefit); + staticText = "return the exiled card to the battlefield"; + cards.stream().map(uuid -> new MageObjectReference(uuid, game)).forEach(morSet::add); + } + + private SemestersEndReturnEffect(final SemestersEndReturnEffect effect) { + super(effect); + this.morSet.addAll(effect.morSet); + } + + @Override + public SemestersEndReturnEffect copy() { + return new SemestersEndReturnEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(); + morSet.stream().map(mor -> mor.getCard(game)).forEach(cards::add); + player.moveCards( + cards.getCards(game), Zone.BATTLEFIELD, source, game, + false, false, true, null + ); + cards.retainZone(Zone.BATTLEFIELD, game); + if (cards.isEmpty()) { + return false; + } + cards.stream().map(game::getPermanent).filter(Objects::nonNull).forEach(p -> { + if (p.isCreature()) { + p.addCounters(CounterType.P1P1.createInstance(), source.getControllerId(), source, game); + } + if (p.isPlaneswalker()) { + p.addCounters(CounterType.LOYALTY.createInstance(), source.getControllerId(), source, game); + } + }); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SengirNosferatu.java b/Mage.Sets/src/mage/cards/s/SengirNosferatu.java index 3465c596bfdf..7bf5b366653e 100644 --- a/Mage.Sets/src/mage/cards/s/SengirNosferatu.java +++ b/Mage.Sets/src/mage/cards/s/SengirNosferatu.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -82,19 +81,19 @@ public ReturnSengirNosferatuEffect copy() { @Override public boolean apply(Game game, Ability source) { - UUID controllerId = source.getControllerId(); + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } Target target = new TargetCardInExile(filter); target.setNotTarget(true); - if (!target.canChoose(source.getSourceId(), controllerId, game)) { + if (!target.canChoose(source.getSourceId(), controller.getId(), game)) { return false; } - Player player = game.getPlayer(controllerId); - if (player != null) { - player.chooseTarget(Outcome.PutCreatureInPlay, target, source, game); - Card card = game.getCard(target.getTargets().get(0)); - if (card != null) { - return card.moveToZone(Zone.BATTLEFIELD, source, game, false); - } + controller.chooseTarget(Outcome.PutCreatureInPlay, target, source, game); + Card card = game.getCard(target.getTargets().get(0)); + if (card != null) { + return controller.moveCards(card, Zone.BATTLEFIELD, source, game); } return false; } diff --git a/Mage.Sets/src/mage/cards/s/SentinelTower.java b/Mage.Sets/src/mage/cards/s/SentinelTower.java index 53bb9bf48ca6..5fafd90aa052 100644 --- a/Mage.Sets/src/mage/cards/s/SentinelTower.java +++ b/Mage.Sets/src/mage/cards/s/SentinelTower.java @@ -119,7 +119,7 @@ class SentinelTowerWatcher extends Watcher { public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { MageObject object = game.getObject(event.getTargetId()); - if (object != null && (object.isInstant() || object.isSorcery())) { + if (object != null && object.isInstantOrSorcery()) { spellsThisTurn.add(new MageObjectReference(object, game)); } } diff --git a/Mage.Sets/src/mage/cards/s/SepulchralPrimordial.java b/Mage.Sets/src/mage/cards/s/SepulchralPrimordial.java index bec61fb8a4fc..96b1178b3e65 100644 --- a/Mage.Sets/src/mage/cards/s/SepulchralPrimordial.java +++ b/Mage.Sets/src/mage/cards/s/SepulchralPrimordial.java @@ -15,7 +15,7 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.players.Player; import mage.target.Target; diff --git a/Mage.Sets/src/mage/cards/s/SequenceEngine.java b/Mage.Sets/src/mage/cards/s/SequenceEngine.java new file mode 100644 index 000000000000..675c0b248d24 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SequenceEngine.java @@ -0,0 +1,66 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.game.permanent.token.QuandrixToken; +import mage.target.common.TargetCardInGraveyard; +import mage.target.targetadjustment.TargetAdjuster; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SequenceEngine extends CardImpl { + + public SequenceEngine(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{G}"); + + // {X}, {T}: Exile target creature card with mana value X from a graveyard. Create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it. Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility( + new ExileTargetEffect() + .setText("exile target creature card with mana value X from a graveyard."), + new ManaCostsImpl<>("{X}") + ); + ability.addCost(new TapSourceCost()); + ability.addEffect(QuandrixToken.getEffect( + ManacostVariableValue.instance, "Put X +1/+1 counters on it" + )); + ability.setTargetAdjuster(SequenceEngineAdjuster.instance); + this.addAbility(ability); + } + + private SequenceEngine(final SequenceEngine card) { + super(card); + } + + @Override + public SequenceEngine copy() { + return new SequenceEngine(this); + } +} + +enum SequenceEngineAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + int xValue = ability.getManaCostsToPay().getX(); + ability.getTargets().clear(); + FilterCard filter = new FilterCreatureCard("creature card with mana value " + xValue); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, xValue)); + ability.addTarget(new TargetCardInGraveyard(filter)); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SerendibSorcerer.java b/Mage.Sets/src/mage/cards/s/SerendibSorcerer.java index 6d79ee30706b..74dcc964b402 100644 --- a/Mage.Sets/src/mage/cards/s/SerendibSorcerer.java +++ b/Mage.Sets/src/mage/cards/s/SerendibSorcerer.java @@ -14,7 +14,7 @@ import mage.constants.Duration; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/s/SereneOffering.java b/Mage.Sets/src/mage/cards/s/SereneOffering.java index 905aeaecae91..13905b1e1a2c 100644 --- a/Mage.Sets/src/mage/cards/s/SereneOffering.java +++ b/Mage.Sets/src/mage/cards/s/SereneOffering.java @@ -2,7 +2,7 @@ package mage.cards.s; import java.util.UUID; -import mage.abilities.dynamicvalue.common.TargetConvertedManaCost; +import mage.abilities.dynamicvalue.common.TargetManaValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.GainLifeEffect; @@ -22,8 +22,8 @@ public SereneOffering(UUID ownerId, CardSetInfo setInfo) { // Destroy target enchantment. You gain life equal to its converted mana cost. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - Effect effect = new GainLifeEffect(TargetConvertedManaCost.instance); - effect.setText("You gain life equal to its converted mana cost"); + Effect effect = new GainLifeEffect(TargetManaValue.instance); + effect.setText("You gain life equal to its mana value"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetEnchantmentPermanent()); } diff --git a/Mage.Sets/src/mage/cards/s/SerpentineCurve.java b/Mage.Sets/src/mage/cards/s/SerpentineCurve.java new file mode 100644 index 000000000000..268ae5ecbcc0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SerpentineCurve.java @@ -0,0 +1,47 @@ +package mage.cards.s; + +import mage.abilities.dynamicvalue.AdditiveDynamicValue; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.InstantSorceryExileGraveyardCount; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.permanent.token.QuandrixToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SerpentineCurve extends CardImpl { + + private static final DynamicValue xValue = new AdditiveDynamicValue( + InstantSorceryExileGraveyardCount.instance, StaticValue.get(1) + ); + private static final Hint hint = new ValueHint( + "Instant and sorcery cards in your exile and graveyard", InstantSorceryExileGraveyardCount.instance + ); + + public SerpentineCurve(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}"); + + // Create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it, where X is one plus the total number of instant and sorcery cards you own in exile and in your graveyard. + this.getSpellAbility().addEffect(QuandrixToken.getEffect( + xValue, "Put X +1/+1 counters on it, where X is one plus the total number " + + "of instant and sorcery cards you own in exile and in your graveyard" + )); + this.getSpellAbility().addHint(hint); + } + + private SerpentineCurve(final SerpentineCurve card) { + super(card); + } + + @Override + public SerpentineCurve copy() { + return new SerpentineCurve(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SerpentineSpike.java b/Mage.Sets/src/mage/cards/s/SerpentineSpike.java index 144b922ef718..069a9f885d30 100644 --- a/Mage.Sets/src/mage/cards/s/SerpentineSpike.java +++ b/Mage.Sets/src/mage/cards/s/SerpentineSpike.java @@ -13,7 +13,7 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/s/SerraAvenger.java b/Mage.Sets/src/mage/cards/s/SerraAvenger.java index 5e17e7a2bb3a..991f84cde749 100644 --- a/Mage.Sets/src/mage/cards/s/SerraAvenger.java +++ b/Mage.Sets/src/mage/cards/s/SerraAvenger.java @@ -58,7 +58,7 @@ class CantCastSerraAvengerEffect extends ContinuousRuleModifyingEffectImpl { public CantCastSerraAvengerEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); - staticText = "You can't cast {this} during your first, second, or third turns of the game"; + staticText = "You can't cast this spell during your first, second, or third turns of the game"; } public CantCastSerraAvengerEffect(final CantCastSerraAvengerEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SetessanTactics.java b/Mage.Sets/src/mage/cards/s/SetessanTactics.java index 7f7354f5ccc9..b5aa64fb14e9 100644 --- a/Mage.Sets/src/mage/cards/s/SetessanTactics.java +++ b/Mage.Sets/src/mage/cards/s/SetessanTactics.java @@ -14,7 +14,7 @@ import mage.constants.Duration; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/s/SettleTheWreckage.java b/Mage.Sets/src/mage/cards/s/SettleTheWreckage.java index 06432a365641..408484cafda5 100644 --- a/Mage.Sets/src/mage/cards/s/SettleTheWreckage.java +++ b/Mage.Sets/src/mage/cards/s/SettleTheWreckage.java @@ -49,7 +49,7 @@ class SettleTheWreckageEffect extends OneShotEffect { SettleTheWreckageEffect() { super(Outcome.Neutral); - this.staticText = "Exile all attacking creatures target player controls. That player may search their library for that many basic land cards, put those cards onto the battlefield tapped, then shuffle their library"; + this.staticText = "Exile all attacking creatures target player controls. That player may search their library for that many basic land cards, put those cards onto the battlefield tapped, then shuffle"; } SettleTheWreckageEffect(final SettleTheWreckageEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SevenDwarves.java b/Mage.Sets/src/mage/cards/s/SevenDwarves.java index 03a6b1caa1b1..56e287d903d5 100644 --- a/Mage.Sets/src/mage/cards/s/SevenDwarves.java +++ b/Mage.Sets/src/mage/cards/s/SevenDwarves.java @@ -15,7 +15,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.NamePredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/s/SevinnesReclamation.java b/Mage.Sets/src/mage/cards/s/SevinnesReclamation.java index 002de9b2aa23..e60c3d23784d 100644 --- a/Mage.Sets/src/mage/cards/s/SevinnesReclamation.java +++ b/Mage.Sets/src/mage/cards/s/SevinnesReclamation.java @@ -10,7 +10,7 @@ import mage.constants.*; import mage.filter.FilterCard; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.stack.Spell; import mage.players.Player; @@ -24,10 +24,10 @@ public final class SevinnesReclamation extends CardImpl { private static final FilterCard filter - = new FilterPermanentCard("permanent card with converted mana cost 3 or less from your graveyard"); + = new FilterPermanentCard("permanent card with mana value 3 or less from your graveyard"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public SevinnesReclamation(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/s/ShacklesOfTreachery.java b/Mage.Sets/src/mage/cards/s/ShacklesOfTreachery.java index ce86f7e65981..12af6a0c8b94 100644 --- a/Mage.Sets/src/mage/cards/s/ShacklesOfTreachery.java +++ b/Mage.Sets/src/mage/cards/s/ShacklesOfTreachery.java @@ -89,9 +89,8 @@ public ShacklesOfTreacheryTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE - || event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT + || event.getType() == GameEvent.EventType.DAMAGED_PLAYER; } @Override diff --git a/Mage.Sets/src/mage/cards/s/ShadewingLaureate.java b/Mage.Sets/src/mage/cards/s/ShadewingLaureate.java new file mode 100644 index 000000000000..959b93c2b130 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ShadewingLaureate.java @@ -0,0 +1,61 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.FlyingAbility; +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.AbilityPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ShadewingLaureate extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("another creature you control with flying"); + + static { + filter.add(AnotherPredicate.instance); + filter.add(new AbilityPredicate(FlyingAbility.class)); + } + + public ShadewingLaureate(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{W/B}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever another creature you control with flying dies, put a +1/+1 counter on target creature you control. + Ability ability = new DiesCreatureTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), false, filter + ); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + private ShadewingLaureate(final ShadewingLaureate card) { + super(card); + } + + @Override + public ShadewingLaureate copy() { + return new ShadewingLaureate(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/ShadowAlleyDenizen.java b/Mage.Sets/src/mage/cards/s/ShadowAlleyDenizen.java index 744d0a8244d9..bd6ac80cb2a8 100644 --- a/Mage.Sets/src/mage/cards/s/ShadowAlleyDenizen.java +++ b/Mage.Sets/src/mage/cards/s/ShadowAlleyDenizen.java @@ -16,7 +16,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/s/ShadowStinger.java b/Mage.Sets/src/mage/cards/s/ShadowStinger.java index 50b349c95ca6..124b92e78b64 100644 --- a/Mage.Sets/src/mage/cards/s/ShadowStinger.java +++ b/Mage.Sets/src/mage/cards/s/ShadowStinger.java @@ -16,7 +16,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.events.DamagedPlayerEvent; diff --git a/Mage.Sets/src/mage/cards/s/ShadowbornApostle.java b/Mage.Sets/src/mage/cards/s/ShadowbornApostle.java index 5a02ea1c61b3..8ab705985f24 100644 --- a/Mage.Sets/src/mage/cards/s/ShadowbornApostle.java +++ b/Mage.Sets/src/mage/cards/s/ShadowbornApostle.java @@ -27,8 +27,8 @@ */ public final class ShadowbornApostle extends CardImpl { - private static final FilterCreatureCard filter = new FilterCreatureCard("a Demon creature"); - private static final FilterControlledCreaturePermanent filterApostle = new FilterControlledCreaturePermanent("six creatures named Shadowborn Apostle"); + private static final FilterCreatureCard filter = new FilterCreatureCard("a Demon creature card"); + private static final FilterControlledCreaturePermanent filterApostle = new FilterControlledCreaturePermanent("creatures named Shadowborn Apostle"); static { filter.add(SubType.DEMON.getPredicate()); filterApostle.add(new NamePredicate("Shadowborn Apostle")); diff --git a/Mage.Sets/src/mage/cards/s/ShadowsOfThePast.java b/Mage.Sets/src/mage/cards/s/ShadowsOfThePast.java index 70b93c514a76..6d87bf22b93a 100644 --- a/Mage.Sets/src/mage/cards/s/ShadowsOfThePast.java +++ b/Mage.Sets/src/mage/cards/s/ShadowsOfThePast.java @@ -30,7 +30,7 @@ public ShadowsOfThePast(UUID ownerId, CardSetInfo setInfo) { // {4}{B}: Each opponent loses 2 life and you gain 2 life. Activate this ability only if there are four or more creature cards in your graveyard. Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, - new LoseLifeOpponentsEffect(2), new ManaCostsImpl<>("{4}{B}"), new CardsInControllerGraveyardCondition(4, StaticFilters.FILTER_CARD_CREATURE)); + new LoseLifeOpponentsEffect(2), new ManaCostsImpl<>("{4}{B}"), new CardsInControllerGraveyardCondition(4, StaticFilters.FILTER_CARD_CREATURES)); Effect effect = new GainLifeEffect(2); effect.setText("and you gain 2 life"); ability.addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/s/ShadowsVerdict.java b/Mage.Sets/src/mage/cards/s/ShadowsVerdict.java index e0f9a253caf4..f8ef523afa43 100644 --- a/Mage.Sets/src/mage/cards/s/ShadowsVerdict.java +++ b/Mage.Sets/src/mage/cards/s/ShadowsVerdict.java @@ -16,7 +16,7 @@ import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; import mage.filter.predicate.Predicate; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; @@ -51,7 +51,7 @@ class ShadowsVerdictEffect extends OneShotEffect { private static final FilterPermanent filter = new FilterCreatureOrPlaneswalkerPermanent(); private static final FilterCard filter2 = new FilterCard(); private static final Predicate predicate - = new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4); + = new ManaValuePredicate(ComparisonType.FEWER_THAN, 4); static { filter.add(predicate); @@ -64,8 +64,8 @@ class ShadowsVerdictEffect extends OneShotEffect { ShadowsVerdictEffect() { super(Outcome.Benefit); - staticText = "exile all creatures and all planeswalkers with converted mana cost 3 or less from the battlefield " + - "and all creature and planeswalker cards with converted mana cost 3 or less from all graveyards"; + staticText = "exile all creatures and planeswalkers with mana value 3 or less from the battlefield " + + "and all creature and planeswalker cards with mana value 3 or less from all graveyards"; } private ShadowsVerdictEffect(final ShadowsVerdictEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/ShadrixSilverquill.java b/Mage.Sets/src/mage/cards/s/ShadrixSilverquill.java new file mode 100644 index 000000000000..5eb3ea5fafb6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ShadrixSilverquill.java @@ -0,0 +1,130 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenTargetEffect; +import mage.abilities.effects.common.DrawCardTargetEffect; +import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPlayer; +import mage.filter.StaticFilters; +import mage.filter.predicate.other.AnotherTargetPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.SilverquillToken; +import mage.target.TargetPlayer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ShadrixSilverquill extends CardImpl { + + private static final FilterPlayer filter0 = new FilterPlayer("a different player"); + private static final FilterPlayer filter1 = new FilterPlayer(); + private static final FilterPlayer filter2 = new FilterPlayer(); + private static final FilterPlayer filter3 = new FilterPlayer(); + + static { + filter1.add(new AnotherTargetPredicate(1, true)); + filter2.add(new AnotherTargetPredicate(2, true)); + filter3.add(new AnotherTargetPredicate(3, true)); + } + + public ShadrixSilverquill(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ELDER); + this.subtype.add(SubType.DRAGON); + this.power = new MageInt(2); + this.toughness = new MageInt(5); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Double strike + this.addAbility(DoubleStrikeAbility.getInstance()); + + // At the beginning of combat on your turn, you may choose two. Each mode must target a different player. + Ability ability = new BeginningOfCombatTriggeredAbility(null, TargetController.YOU, false); + ability.getModes().setMinModes(2); + ability.getModes().setMaxModes(2); + ability.getModes().setMaxModesFilter(filter0); + ability.getModes().setMayChooseNone(true); + + // • Target player creates a 2/1 white and black Inkling creature token with flying. + ability.addEffect(new CreateTokenTargetEffect(new SilverquillToken())); + TargetPlayer target = new TargetPlayer(filter1); + target.setTargetTag(1); + ability.addTarget(target.withChooseHint("to create a token")); + + // • Target player draws a card and loses 1 life. + Mode mode = new Mode(new DrawCardTargetEffect(1)); + mode.addEffect(new LoseLifeTargetEffect(1).setText("and loses 1 life")); + target = new TargetPlayer(filter2); + target.setTargetTag(2); + mode.addTarget(target.withChooseHint("to draw a card and lose 1 life")); + ability.addMode(mode); + + // • Target player puts a +1/+1 counter on each creature they control. + mode = new Mode(new ShadrixSilverquillEffect()); + target = new TargetPlayer(filter3); + target.setTargetTag(3); + mode.addTarget(target.withChooseHint("to put a counter on each creature")); + ability.addMode(mode); + this.addAbility(ability); + } + + private ShadrixSilverquill(final ShadrixSilverquill card) { + super(card); + } + + @Override + public ShadrixSilverquill copy() { + return new ShadrixSilverquill(this); + } +} + +class ShadrixSilverquillEffect extends OneShotEffect { + + ShadrixSilverquillEffect() { + super(Outcome.Benefit); + staticText = "target player puts a +1/+1 counter on each creature they control"; + } + + private ShadrixSilverquillEffect(final ShadrixSilverquillEffect effect) { + super(effect); + } + + @Override + public ShadrixSilverquillEffect copy() { + return new ShadrixSilverquillEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (game.getPlayer(source.getFirstTarget()) == null) { + return false; + } + for (Permanent permanent : game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_CONTROLLED_CREATURE, + source.getFirstTarget(), source.getSourceId(), game + )) { + if (permanent == null) { + continue; + } + permanent.addCounters(CounterType.P1P1.createInstance(), source.getFirstTarget(), source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/ShaileDeanOfRadiance.java b/Mage.Sets/src/mage/cards/s/ShaileDeanOfRadiance.java new file mode 100644 index 000000000000..31bc50fd8d45 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ShaileDeanOfRadiance.java @@ -0,0 +1,91 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicate; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.predicate.permanent.EnteredThisTurnPredicate; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class ShaileDeanOfRadiance extends ModalDoubleFacesCard { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("another target creature"); + private static final FilterPermanent shaileFilter = new FilterControlledCreaturePermanent("creature that entered the battlefield under your control this turn"); + private static final FilterPermanent embroseFilter = new FilterControlledCreaturePermanent("a creature you control with a +1/+1 counter on it"); + + static { + filter.add(AnotherPredicate.instance); + shaileFilter.add(EnteredThisTurnPredicate.instance); + shaileFilter.add((Predicate) (input, game) -> !input.checkControlChanged(game)); + embroseFilter.add(CounterType.P1P1.getPredicate()); + } + + public ShaileDeanOfRadiance(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.BIRD, SubType.CLERIC}, "{1}{W}", + "Embrose, Dean of Shadow", new CardType[]{CardType.CREATURE}, new SubType[]{SubType.HUMAN, SubType.WARLOCK}, "{2}{B}{B}"); + + // 1. + // Shaile, Dean of Radiance + // Legendary Creature - Bird Cleric + this.getLeftHalfCard().addSuperType(SuperType.LEGENDARY); + this.getLeftHalfCard().setPT(1, 1); + + // Flying + this.getLeftHalfCard().addAbility(FlyingAbility.getInstance()); + + // Vigilance + this.getLeftHalfCard().addAbility(VigilanceAbility.getInstance()); + + // {T}: Put a +1/+1 counter on each creature that entered the battlefield under your control this turn. + this.getLeftHalfCard().addAbility(new SimpleActivatedAbility(new AddCountersAllEffect(CounterType.P1P1.createInstance(), shaileFilter), new TapSourceCost())); + + // 2. + // Embrose, Dean of Shadow + // Legendary Creature - Human Warlock + this.getRightHalfCard().addSuperType(SuperType.LEGENDARY); + this.getRightHalfCard().setPT(4, 4); + + // {T}: Put a +1/+1 counter on another target creature, then Embrose, Dean of Shadow deals 2 damage to that creature. + Ability ability = new SimpleActivatedAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), new TapSourceCost()); + ability.addEffect(new DamageTargetEffect(2).concatBy(", then").setText("{this} deals 2 damage to that creature")); + ability.addTarget(new TargetCreaturePermanent(filter)); + this.getRightHalfCard().addAbility(ability); + + // Whenever a creature you control with a +1/+1 counter on it dies, draw a card. + this.getRightHalfCard().addAbility(new DiesCreatureTriggeredAbility(new DrawCardSourceControllerEffect(1), false, embroseFilter)); + } + + private ShaileDeanOfRadiance(final ShaileDeanOfRadiance card) { + super(card); + } + + @Override + public ShaileDeanOfRadiance copy() { + return new ShaileDeanOfRadiance(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/ShaleskinBruiser.java b/Mage.Sets/src/mage/cards/s/ShaleskinBruiser.java index dc30824c2522..14cce8d24e66 100644 --- a/Mage.Sets/src/mage/cards/s/ShaleskinBruiser.java +++ b/Mage.Sets/src/mage/cards/s/ShaleskinBruiser.java @@ -14,7 +14,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.common.FilterAttackingCreature; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/s/ShamanEnKor.java b/Mage.Sets/src/mage/cards/s/ShamanEnKor.java index 1aebf0e24509..f66e8fc80773 100644 --- a/Mage.Sets/src/mage/cards/s/ShamanEnKor.java +++ b/Mage.Sets/src/mage/cards/s/ShamanEnKor.java @@ -95,7 +95,7 @@ public void init(Ability source, Game game) { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE; + return event.getType() == EventType.DAMAGE_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/s/ShaperApprentice.java b/Mage.Sets/src/mage/cards/s/ShaperApprentice.java index 2d30a7940c95..ba0c0cde1ccd 100644 --- a/Mage.Sets/src/mage/cards/s/ShaperApprentice.java +++ b/Mage.Sets/src/mage/cards/s/ShaperApprentice.java @@ -15,7 +15,7 @@ import mage.constants.Duration; import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/s/SharedAnimosity.java b/Mage.Sets/src/mage/cards/s/SharedAnimosity.java index 6bfed432a6c8..c4eb000b72da 100644 --- a/Mage.Sets/src/mage/cards/s/SharedAnimosity.java +++ b/Mage.Sets/src/mage/cards/s/SharedAnimosity.java @@ -1,19 +1,21 @@ package mage.cards.s; import mage.abilities.Ability; -import mage.abilities.Mode; import mage.abilities.common.AttacksCreatureYouControlTriggeredAbility; -import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AttackingPredicate; -import mage.filter.predicate.permanent.PermanentIdPredicate; -import mage.filter.predicate.permanent.SharesCreatureTypePredicate; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterAttackingCreature; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.predicate.mageobject.SharesCreatureTypePredicate; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; import java.util.UUID; @@ -27,8 +29,8 @@ public SharedAnimosity(UUID ownerId, CardSetInfo setInfo) { // Whenever a creature you control attacks, it gets +1/+0 until end of turn for each other attacking creature that shares a creature type with it. this.addAbility(new AttacksCreatureYouControlTriggeredAbility( - new SharedAnimosityEffect(), false, true) - ); + new SharedAnimosityEffect(), false, true + )); } private SharedAnimosity(final SharedAnimosity card) { @@ -41,18 +43,16 @@ public SharedAnimosity copy() { } } -class SharedAnimosityEffect extends ContinuousEffectImpl { - - private int power; - - public SharedAnimosityEffect() { - super(Duration.EndOfTurn, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.BoostCreature); +class SharedAnimosityEffect extends OneShotEffect { + SharedAnimosityEffect() { + super(Outcome.Benefit); + staticText = "it gets +1/+0 until end of turn for each other " + + "attacking creature that shares a creature type with it"; } - public SharedAnimosityEffect(final SharedAnimosityEffect effect) { + private SharedAnimosityEffect(final SharedAnimosityEffect effect) { super(effect); - this.power = effect.power; } @Override @@ -60,31 +60,21 @@ public SharedAnimosityEffect copy() { return new SharedAnimosityEffect(this); } - @Override - public void init(Ability source, Game game) { - super.init(source, game); - Permanent permanent = game.getPermanent(this.targetPointer.getFirst(game, source)); - if (permanent != null) { - FilterCreaturePermanent filter = new FilterCreaturePermanent(); - filter.add(Predicates.not(new PermanentIdPredicate(this.targetPointer.getFirst(game, source)))); - filter.add(AttackingPredicate.instance); - filter.add(new SharesCreatureTypePredicate(permanent)); - power = game.getBattlefield().count(filter, source.getControllerId(), source.getSourceId(), game); - } - } - @Override public boolean apply(Game game, Ability source) { - Permanent target = game.getPermanent(this.targetPointer.getFirst(game, source)); - if (target != null) { - target.addPower(power); - return true; + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (permanent == null) { + return false; } - return false; - } - - @Override - public String getText(Mode mode) { - return "it gets +1/+0 until end of turn for each other attacking creature that shares a creature type with it"; + FilterPermanent filter = new FilterAttackingCreature(); + filter.add(new SharesCreatureTypePredicate(permanent)); + filter.add(AnotherPredicate.instance); + int count = game.getBattlefield().count(filter, permanent.getId(), source.getControllerId(), game); + if (count > 0) { + game.addEffect(new BoostTargetEffect( + count, 0, Duration.EndOfTurn + ).setTargetPointer(new FixedTarget(permanent, game)), source); + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/s/SharkTyphoon.java b/Mage.Sets/src/mage/cards/s/SharkTyphoon.java index 4462becf5dec..cb4c2dc9c606 100644 --- a/Mage.Sets/src/mage/cards/s/SharkTyphoon.java +++ b/Mage.Sets/src/mage/cards/s/SharkTyphoon.java @@ -55,7 +55,7 @@ class SharkTyphoonCastEffect extends OneShotEffect { SharkTyphoonCastEffect() { super(Outcome.Benefit); - staticText = "create an X/X blue Shark creature token with flying, where X is that spell's converted mana cost"; + staticText = "create an X/X blue Shark creature token with flying, where X is that spell's mana value"; } private SharkTyphoonCastEffect(final SharkTyphoonCastEffect effect) { @@ -72,7 +72,7 @@ public boolean apply(Game game, Ability source) { Spell spell = game.getSpellOrLKIStack(getTargetPointer().getFirst(game, source)); int xValue = 0; if (spell != null) { - xValue = spell.getConvertedManaCost(); + xValue = spell.getManaValue(); } return new SharkToken(xValue).putOntoBattlefield(1, game, source, source.getControllerId()); } diff --git a/Mage.Sets/src/mage/cards/s/ShaukuEndbringer.java b/Mage.Sets/src/mage/cards/s/ShaukuEndbringer.java index 17794b916009..1ce2bcf9176d 100644 --- a/Mage.Sets/src/mage/cards/s/ShaukuEndbringer.java +++ b/Mage.Sets/src/mage/cards/s/ShaukuEndbringer.java @@ -16,7 +16,7 @@ import mage.constants.*; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/s/ShellOfTheLastKappa.java b/Mage.Sets/src/mage/cards/s/ShellOfTheLastKappa.java index 03636b629e9e..f7f3022ab0f9 100644 --- a/Mage.Sets/src/mage/cards/s/ShellOfTheLastKappa.java +++ b/Mage.Sets/src/mage/cards/s/ShellOfTheLastKappa.java @@ -143,8 +143,7 @@ public boolean apply(Game game, Ability source) { sourcePermanent.getZoneChangeCounter(game))), target, game)) { Card card = game.getCard(target.getFirstTarget()); if (card != null - && controller.chooseUse(outcome, "Do you wish to cast card exiled with " - + sourcePermanent.getLogName() + "?", source, game)) { + && controller.chooseUse(outcome, "Cast " + card.getLogName() + " without paying its mana cost?", source, game)) { game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(card, game, true), game, true, new ApprovingObject(source, game)); diff --git a/Mage.Sets/src/mage/cards/s/ShepherdOfTheCosmos.java b/Mage.Sets/src/mage/cards/s/ShepherdOfTheCosmos.java index 720d7d14d079..f62ecd723f04 100644 --- a/Mage.Sets/src/mage/cards/s/ShepherdOfTheCosmos.java +++ b/Mage.Sets/src/mage/cards/s/ShepherdOfTheCosmos.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.filter.FilterCard; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInYourGraveyard; import java.util.UUID; @@ -24,11 +24,11 @@ public final class ShepherdOfTheCosmos extends CardImpl { private static final FilterCard filter = new FilterPermanentCard( - "permanent card with converted mana cost 2 or less from your graveyard" + "permanent card with mana value 2 or less from your graveyard" ); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); } public ShepherdOfTheCosmos(UUID ownerId, CardSetInfo setInfo) { @@ -43,7 +43,7 @@ public ShepherdOfTheCosmos(UUID ownerId, CardSetInfo setInfo) { this.addAbility(FlyingAbility.getInstance()); // When Shepherd of the Cosmos enters the battlefield, return target permanent card with converted mana cost 2 or less from your graveyard to the battlefield. - Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(false, false)); + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect()); ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/ShieldDancer.java b/Mage.Sets/src/mage/cards/s/ShieldDancer.java index afcf974ff843..788c24f71907 100644 --- a/Mage.Sets/src/mage/cards/s/ShieldDancer.java +++ b/Mage.Sets/src/mage/cards/s/ShieldDancer.java @@ -68,7 +68,7 @@ public ShieldDancerRedirectionEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE; + return event.getType() == EventType.DAMAGE_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/s/ShieldhideDragon.java b/Mage.Sets/src/mage/cards/s/ShieldhideDragon.java index 23df3d2fe03e..79bd73c9b26a 100644 --- a/Mage.Sets/src/mage/cards/s/ShieldhideDragon.java +++ b/Mage.Sets/src/mage/cards/s/ShieldhideDragon.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/s/ShimianSpecter.java b/Mage.Sets/src/mage/cards/s/ShimianSpecter.java index af6983e7eefb..aa019301a50c 100644 --- a/Mage.Sets/src/mage/cards/s/ShimianSpecter.java +++ b/Mage.Sets/src/mage/cards/s/ShimianSpecter.java @@ -70,7 +70,7 @@ public ShimianSpecterEffect() { staticText = "that player reveals their hand. You choose a nonland card from it. " + "Search that player's graveyard, hand, and library for all cards " + "with the same name as that card and exile them. Then that " - + "player shuffles their library"; + + "player shuffles"; } public ShimianSpecterEffect(final ShimianSpecterEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/ShimmerwingChimera.java b/Mage.Sets/src/mage/cards/s/ShimmerwingChimera.java index 9693375b4499..d9a47c7913f3 100644 --- a/Mage.Sets/src/mage/cards/s/ShimmerwingChimera.java +++ b/Mage.Sets/src/mage/cards/s/ShimmerwingChimera.java @@ -12,7 +12,7 @@ import mage.constants.TargetController; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledEnchantmentPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/s/ShineshadowSnarl.java b/Mage.Sets/src/mage/cards/s/ShineshadowSnarl.java new file mode 100644 index 000000000000..d622053113a5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ShineshadowSnarl.java @@ -0,0 +1,56 @@ +package mage.cards.s; + +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.costs.common.RevealTargetFromHandCost; +import mage.abilities.effects.common.TapSourceUnlessPaysEffect; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.target.common.TargetCardInHand; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ShineshadowSnarl extends CardImpl { + + private static final FilterCard filter = new FilterCard("a Plains or Swamp card from your hand"); + + static { + filter.add(Predicates.or( + SubType.PLAINS.getPredicate(), + SubType.SWAMP.getPredicate() + )); + } + + public ShineshadowSnarl(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // As Shineshadow Snarl enters the battlefield, you may reveal a Plains or Swamp card from your hand. If you don't, Shineshadow Snarl enters the battlefield tapped. + this.addAbility(new AsEntersBattlefieldAbility( + new TapSourceUnlessPaysEffect( + new RevealTargetFromHandCost(new TargetCardInHand(filter)) + ), "you may reveal a Plains or Swamp card from your hand. " + + "If you don't, {this} enters the battlefield tapped" + )); + + // {T}: Add {W} or {B}. + this.addAbility(new WhiteManaAbility()); + this.addAbility(new BlackManaAbility()); + } + + private ShineshadowSnarl(final ShineshadowSnarl card) { + super(card); + } + + @Override + public ShineshadowSnarl copy() { + return new ShineshadowSnarl(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/ShiningShoal.java b/Mage.Sets/src/mage/cards/s/ShiningShoal.java index 53a0c47f04de..f918585c71af 100644 --- a/Mage.Sets/src/mage/cards/s/ShiningShoal.java +++ b/Mage.Sets/src/mage/cards/s/ShiningShoal.java @@ -38,7 +38,7 @@ public ShiningShoal(UUID ownerId, CardSetInfo setInfo) { this.subtype.add(SubType.ARCANE); // You may exile a white card with converted mana cost X from your hand rather than pay Shining Shoal's mana cost - FilterOwnedCard filter = new FilterOwnedCard("a white card with converted mana cost X from your hand"); + FilterOwnedCard filter = new FilterOwnedCard("a white card with mana value X from your hand"); filter.add(new ColorPredicate(ObjectColor.WHITE)); filter.add(Predicates.not(new CardIdPredicate(this.getId()))); // the exile cost can never be paid with the card itself this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(filter), true))); diff --git a/Mage.Sets/src/mage/cards/s/ShireiShizosCaretaker.java b/Mage.Sets/src/mage/cards/s/ShireiShizosCaretaker.java index d0d0617e5c3d..02fc5a9f995b 100644 --- a/Mage.Sets/src/mage/cards/s/ShireiShizosCaretaker.java +++ b/Mage.Sets/src/mage/cards/s/ShireiShizosCaretaker.java @@ -76,8 +76,7 @@ public boolean checkTrigger(GameEvent event, Game game) { if (card != null && LKIpermanent != null && card.isOwnedBy(this.controllerId) - && zEvent.getToZone() == Zone.GRAVEYARD - && zEvent.getFromZone() == Zone.BATTLEFIELD + && zEvent.isDiesEvent() && card.isCreature() && LKIpermanent.getPower().getValue() <= 1) { for (Effect effect : this.getEffects()) { diff --git a/Mage.Sets/src/mage/cards/s/ShivanFire.java b/Mage.Sets/src/mage/cards/s/ShivanFire.java index 5be3c8ccac7f..9d35b49117a6 100644 --- a/Mage.Sets/src/mage/cards/s/ShivanFire.java +++ b/Mage.Sets/src/mage/cards/s/ShivanFire.java @@ -29,7 +29,7 @@ public ShivanFire(UUID ownerId, CardSetInfo setInfo) { // Shivan Fire deals 2 damage to any target. If Shivan Fire was kicked, it deals 4 damage to that creature or player instead. this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new DamageTargetEffect(4), - new DamageTargetEffect(2), KickedCondition.instance, "{this} deals 2 damage to target creature. if this spell was kicked, it deals 4 damage to that creature instead")); + new DamageTargetEffect(2), KickedCondition.instance, "{this} deals 2 damage to target creature. If this spell was kicked, it deals 4 damage to that creature instead")); } private ShivanFire(final ShivanFire card) { diff --git a/Mage.Sets/src/mage/cards/s/ShivanSandMage.java b/Mage.Sets/src/mage/cards/s/ShivanSandMage.java index f1e5c3c20914..eca0d7e3fe6b 100644 --- a/Mage.Sets/src/mage/cards/s/ShivanSandMage.java +++ b/Mage.Sets/src/mage/cards/s/ShivanSandMage.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.Mode; @@ -13,21 +11,30 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.counters.CounterType; +import mage.filter.common.FilterPermanentOrSuspendedCard; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetPermanentOrSuspendedCard; +import java.util.UUID; + /** - * * @author L_J */ public final class ShivanSandMage extends CardImpl { + private static final FilterPermanentOrSuspendedCard filter + = new FilterPermanentOrSuspendedCard("permanent with a time counter on it or suspended card"); + + static { + filter.getPermanentFilter().add(CounterType.TIME.getPredicate()); + } + public ShivanSandMage(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); this.subtype.add(SubType.VIASHINO); this.subtype.add(SubType.SHAMAN); this.power = new MageInt(3); @@ -39,9 +46,8 @@ public ShivanSandMage(UUID ownerId, CardSetInfo setInfo) { ability.addTarget(new TargetPermanentOrSuspendedCard()); // Put two time counters on target permanent with a time counter on it or suspended card. - Mode mode = new Mode(); - mode.addEffect(new ShivanSandMageEffect(true)); - mode.addTarget(new TargetPermanentOrSuspendedCard()); + Mode mode = new Mode(new ShivanSandMageEffect(true)); + mode.addTarget(new TargetPermanentOrSuspendedCard(filter, false)); ability.addMode(mode); ability.getModes().addMode(mode); this.addAbility(ability); @@ -61,20 +67,20 @@ public ShivanSandMage copy() { } class ShivanSandMageEffect extends OneShotEffect { - + private final boolean addCounters; - public ShivanSandMageEffect(boolean addCounters) { + ShivanSandMageEffect(boolean addCounters) { super(Outcome.Benefit); this.addCounters = addCounters; if (addCounters) { - this.staticText = "put two time counters on target permanent or suspended card"; + this.staticText = "put two time counters on target permanent with a time counter on it or suspended card"; } else { this.staticText = "remove two time counters from target permanent or suspended card"; } } - public ShivanSandMageEffect(final ShivanSandMageEffect effect) { + private ShivanSandMageEffect(final ShivanSandMageEffect effect) { super(effect); this.addCounters = effect.addCounters; } diff --git a/Mage.Sets/src/mage/cards/s/ShowOfConfidence.java b/Mage.Sets/src/mage/cards/s/ShowOfConfidence.java new file mode 100644 index 000000000000..b688eca27f55 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ShowOfConfidence.java @@ -0,0 +1,90 @@ +package mage.cards.s; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CastSourceTriggeredAbility; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.target.common.TargetCreaturePermanent; +import mage.watchers.common.SpellsCastWatcher; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ShowOfConfidence extends CardImpl { + + public ShowOfConfidence(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); + + // When you cast this spell, copy it for each other instant or sorcery spell you've cast this turn. You may choose new targets for the copies. + this.addAbility(new CastSourceTriggeredAbility(new ShowOfConfidenceEffect()), new SpellsCastWatcher()); + + // Put a +1/+1 counter on target creature. It gains vigilance until end of turn. + this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + VigilanceAbility.getInstance(), Duration.EndOfTurn + ).setText("It gains vigilance until end of turn")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private ShowOfConfidence(final ShowOfConfidence card) { + super(card); + } + + @Override + public ShowOfConfidence copy() { + return new ShowOfConfidence(this); + } +} + +class ShowOfConfidenceEffect extends OneShotEffect { + + ShowOfConfidenceEffect() { + super(Outcome.Benefit); + staticText = "copy it for each other instant and sorcery spell you've cast this turn. " + + "You may choose new targets for the copies"; + } + + private ShowOfConfidenceEffect(final ShowOfConfidenceEffect effect) { + super(effect); + } + + @Override + public ShowOfConfidenceEffect copy() { + return new ShowOfConfidenceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Spell spell = (Spell) getValue("spellCast"); + SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class); + if (spell == null || watcher == null) { + return false; + } + int copies = watcher.getSpellsCastThisTurn(source.getControllerId()) + .stream() + .filter(Objects::nonNull) + .filter(MageObject::isInstantOrSorcery) + .filter(s -> !s.getSourceId().equals(source.getSourceId()) + || s.getZoneChangeCounter(game) != source.getSourceObjectZoneChangeCounter()) + .mapToInt(x -> 1) + .sum(); + if (copies > 0) { + spell.createCopyOnStack(game, source, source.getControllerId(), true, copies); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/ShriekingMogg.java b/Mage.Sets/src/mage/cards/s/ShriekingMogg.java index c6e3392d4a0c..1bb287ad28e8 100644 --- a/Mage.Sets/src/mage/cards/s/ShriekingMogg.java +++ b/Mage.Sets/src/mage/cards/s/ShriekingMogg.java @@ -12,7 +12,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/s/ShrivelingRot.java b/Mage.Sets/src/mage/cards/s/ShrivelingRot.java index fd0089978a51..2b2dc3c01b4e 100644 --- a/Mage.Sets/src/mage/cards/s/ShrivelingRot.java +++ b/Mage.Sets/src/mage/cards/s/ShrivelingRot.java @@ -1,12 +1,11 @@ package mage.cards.s; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.Mode; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.EntwineAbility; @@ -18,24 +17,25 @@ import mage.constants.Zone; 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.targetpointer.FixedTarget; -import mage.game.events.ZoneChangeEvent; + +import java.util.UUID; /** - * * @author L_J */ public final class ShrivelingRot extends CardImpl { public ShrivelingRot(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}{B}"); // Choose one - // Until end of turn, whenever a creature is dealt damage, destroy it. this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new ShrivelingRotDestroyTriggeredAbility())); - + // Until end of turn, whenever a creature dies, that creature's controller loses life equal to its toughness. Mode mode = new Mode(); mode.addEffect(new CreateDelayedTriggeredAbilityEffect(new ShrivelingRotLoseLifeTriggeredAbility())); @@ -72,14 +72,16 @@ public ShrivelingRotDestroyTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent == null || !permanent.isCreature()) { + return false; } + getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game)); return true; } diff --git a/Mage.Sets/src/mage/cards/s/ShroudedLore.java b/Mage.Sets/src/mage/cards/s/ShroudedLore.java index 96059b9da564..1d74a8a34d50 100644 --- a/Mage.Sets/src/mage/cards/s/ShroudedLore.java +++ b/Mage.Sets/src/mage/cards/s/ShroudedLore.java @@ -11,7 +11,7 @@ import mage.filter.FilterCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardIdPredicate; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInGraveyard; diff --git a/Mage.Sets/src/mage/cards/s/Shunt.java b/Mage.Sets/src/mage/cards/s/Shunt.java index 6e61949402fc..f06a560d7e8e 100644 --- a/Mage.Sets/src/mage/cards/s/Shunt.java +++ b/Mage.Sets/src/mage/cards/s/Shunt.java @@ -7,7 +7,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.NumberOfTargetsPredicate; +import mage.filter.predicate.other.NumberOfTargetsPredicate; import mage.target.TargetSpell; /** diff --git a/Mage.Sets/src/mage/cards/s/SickeningShoal.java b/Mage.Sets/src/mage/cards/s/SickeningShoal.java index 9c4f6ce122f0..20a19d766767 100644 --- a/Mage.Sets/src/mage/cards/s/SickeningShoal.java +++ b/Mage.Sets/src/mage/cards/s/SickeningShoal.java @@ -34,7 +34,7 @@ public SickeningShoal(UUID ownerId, CardSetInfo setInfo) { // You may exile a black card with converted mana cost X from your hand rather than pay Sickening Shoal's mana cost. - FilterOwnedCard filter = new FilterOwnedCard("a black card with converted mana cost X from your hand"); + FilterOwnedCard filter = new FilterOwnedCard("a black card with mana value X from your hand"); filter.add(new ColorPredicate(ObjectColor.BLACK)); filter.add(Predicates.not(new CardIdPredicate(this.getId()))); // the exile cost can never be paid with the card itself this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(filter), true))); diff --git a/Mage.Sets/src/mage/cards/s/SickleDancer.java b/Mage.Sets/src/mage/cards/s/SickleDancer.java index db82b07fd551..ab3a5a2e01e8 100644 --- a/Mage.Sets/src/mage/cards/s/SickleDancer.java +++ b/Mage.Sets/src/mage/cards/s/SickleDancer.java @@ -13,7 +13,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.filter.common.FilterTeamPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/s/SidewinderSliver.java b/Mage.Sets/src/mage/cards/s/SidewinderSliver.java index cebb60974ee7..2ffb7a4ff660 100644 --- a/Mage.Sets/src/mage/cards/s/SidewinderSliver.java +++ b/Mage.Sets/src/mage/cards/s/SidewinderSliver.java @@ -36,7 +36,7 @@ public SidewinderSliver(UUID ownerId, CardSetInfo setInfo) { // All Sliver creatures have flanking. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(new FlankingAbility(), Duration.WhileOnBattlefield, filter, false) - .setText("all Slivers have flanking") + .setText("all Sliver creatures have flanking") )); } diff --git a/Mage.Sets/src/mage/cards/s/SiegeBehemoth.java b/Mage.Sets/src/mage/cards/s/SiegeBehemoth.java index 9d58bd9c63f5..30c7f0662325 100644 --- a/Mage.Sets/src/mage/cards/s/SiegeBehemoth.java +++ b/Mage.Sets/src/mage/cards/s/SiegeBehemoth.java @@ -65,8 +65,7 @@ public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Player controller = game.getPlayer(source.getControllerId()); Permanent otherCreature = game.getPermanent(sourceId); if (controller != null && otherCreature != null && otherCreature.isControlledBy(controller.getId())){ - return controller.chooseUse(Outcome.Damage, "Do you wish to assign damage for " - + otherCreature.getLogName() + " as though it weren't blocked?", source, game); + return controller.chooseUse(Outcome.Damage, "Have " + otherCreature.getLogName() + " assign damage as though it weren't blocked?", source, game); } } return false; diff --git a/Mage.Sets/src/mage/cards/s/SiftThroughSands.java b/Mage.Sets/src/mage/cards/s/SiftThroughSands.java index 386fa94ee766..b26ca9b1f0bf 100644 --- a/Mage.Sets/src/mage/cards/s/SiftThroughSands.java +++ b/Mage.Sets/src/mage/cards/s/SiftThroughSands.java @@ -30,7 +30,7 @@ */ public final class SiftThroughSands extends CardImpl { - private static final String rule = "If you've cast a spell named Peer Through Depths and a spell named Reach Through Mists this turn, you may search your library for a card named The Unspeakable, put it onto the battlefield, then shuffle your library"; + private static final String rule = "
If you've cast a spell named Peer Through Depths and a spell named Reach Through Mists this turn, you may search your library for a card named The Unspeakable, put it onto the battlefield, then shuffle"; private static final FilterCreatureCard filter = new FilterCreatureCard("a card named The Unspeakable"); static { diff --git a/Mage.Sets/src/mage/cards/s/SifterOfSkulls.java b/Mage.Sets/src/mage/cards/s/SifterOfSkulls.java index 94e5b3810031..aef294ea7c58 100644 --- a/Mage.Sets/src/mage/cards/s/SifterOfSkulls.java +++ b/Mage.Sets/src/mage/cards/s/SifterOfSkulls.java @@ -14,7 +14,7 @@ import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.permanent.token.EldraziScionToken; diff --git a/Mage.Sets/src/mage/cards/s/SifterWurm.java b/Mage.Sets/src/mage/cards/s/SifterWurm.java index 665c95e5bda1..d5915ffbdc0f 100644 --- a/Mage.Sets/src/mage/cards/s/SifterWurm.java +++ b/Mage.Sets/src/mage/cards/s/SifterWurm.java @@ -54,7 +54,7 @@ class SifterWurmEffect extends OneShotEffect { public SifterWurmEffect() { super(Outcome.DrawCard); - this.staticText = "scry 3, then reveal the top card of your library. You gain life equal to that card's converted mana cost."; + this.staticText = "scry 3, then reveal the top card of your library. You gain life equal to that card's mana value."; } public SifterWurmEffect(final SifterWurmEffect effect) { @@ -79,7 +79,7 @@ public boolean apply(Game game, Ability source) { Cards cards = new CardsImpl(); cards.add(card); controller.revealCards(sourceObject.getIdName(), cards, game); - controller.gainLife(card.getConvertedManaCost(), game, source); + controller.gainLife(card.getManaValue(), game, source); } return true; } diff --git a/Mage.Sets/src/mage/cards/s/SilentSentinel.java b/Mage.Sets/src/mage/cards/s/SilentSentinel.java index 96c001856751..9eef6dfd0990 100644 --- a/Mage.Sets/src/mage/cards/s/SilentSentinel.java +++ b/Mage.Sets/src/mage/cards/s/SilentSentinel.java @@ -1,7 +1,6 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; @@ -11,24 +10,24 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; import mage.filter.FilterCard; -import mage.target.common.TargetCardInGraveyard; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class SilentSentinel extends CardImpl { private static final FilterCard filter = new FilterCard("enchantment card from your graveyard"); + static { filter.add(CardType.ENCHANTMENT.getPredicate()); - filter.add(TargetController.YOU.getOwnerPredicate()); } public SilentSentinel(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}{W}"); this.subtype.add(SubType.ARCHON); this.power = new MageInt(4); @@ -38,7 +37,7 @@ public SilentSentinel(UUID ownerId, CardSetInfo setInfo) { this.addAbility(FlyingAbility.getInstance()); // Whenever Silent Sentinel attacks, you may return target enchantment card from your graveyard to the battlefield. Ability ability = new AttacksTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(), true); - ability.addTarget(new TargetCardInGraveyard(filter)); + ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SilkwingScout.java b/Mage.Sets/src/mage/cards/s/SilkwingScout.java index 45a547d946da..5ade8a775fca 100644 --- a/Mage.Sets/src/mage/cards/s/SilkwingScout.java +++ b/Mage.Sets/src/mage/cards/s/SilkwingScout.java @@ -36,7 +36,7 @@ public SilkwingScout(UUID ownerId, CardSetInfo setInfo) { // {G}, Sacrifice Silkwing Scout: Search your library for a basic land card and put that card onto the battlefield tapped. Then shuffle your library. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 1, StaticFilters.FILTER_CARD_BASIC_LAND), true, true), + new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true, true), new ManaCostsImpl("{G}")); ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/Silkwrap.java b/Mage.Sets/src/mage/cards/s/Silkwrap.java index d23697fd50c1..ad197d354ae2 100644 --- a/Mage.Sets/src/mage/cards/s/Silkwrap.java +++ b/Mage.Sets/src/mage/cards/s/Silkwrap.java @@ -15,7 +15,7 @@ import mage.constants.Outcome; import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; @@ -27,10 +27,10 @@ */ public final class Silkwrap extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with converted mana cost 3 or less an opponent controls"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with mana value 3 or less an opponent controls"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); filter.add(TargetController.OPPONENT.getControllerPredicate()); } @@ -59,7 +59,7 @@ class SilkwrapEffect extends OneShotEffect { public SilkwrapEffect() { super(Outcome.Neutral); - this.staticText = "exile target creature with converted mana cost 3 or less an opponent controls until {this} leaves the battlefield. (That creature returns under its owner's control.)"; + this.staticText = "exile target creature with mana value 3 or less an opponent controls until {this} leaves the battlefield. (That creature returns under its owner's control.)"; } public SilkwrapEffect(final SilkwrapEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SilumgarScavenger.java b/Mage.Sets/src/mage/cards/s/SilumgarScavenger.java index f90512100899..da317c815842 100644 --- a/Mage.Sets/src/mage/cards/s/SilumgarScavenger.java +++ b/Mage.Sets/src/mage/cards/s/SilumgarScavenger.java @@ -15,7 +15,7 @@ import mage.constants.*; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/s/SilumgarsScorn.java b/Mage.Sets/src/mage/cards/s/SilumgarsScorn.java index 93779841e0f5..d922fd459603 100644 --- a/Mage.Sets/src/mage/cards/s/SilumgarsScorn.java +++ b/Mage.Sets/src/mage/cards/s/SilumgarsScorn.java @@ -1,65 +1,38 @@ - package mage.cards.s; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.costs.Cost; -import mage.abilities.costs.common.RevealTargetFromHandCost; +import mage.abilities.condition.common.RevealedOrControlledDragonCondition; +import mage.abilities.costs.common.RevealDragonFromHandCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.InfoEffect; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AbilityType; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.filter.FilterCard; -import mage.game.Game; -import mage.game.stack.StackObject; -import mage.players.Player; import mage.target.TargetSpell; -import mage.target.common.TargetCardInHand; -import mage.watchers.common.DragonOnTheBattlefieldWhileSpellWasCastWatcher; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class SilumgarsScorn extends CardImpl { - private static final FilterCard filter = new FilterCard("a Dragon card from your hand (you don't have to)"); - - static { - filter.add(SubType.DRAGON.getPredicate()); - } - public SilumgarsScorn(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}{U}"); // As an additional cost to cast Silumgar's Scorn, you may reveal a Dragon card from your hand. - this.getSpellAbility().addEffect(new InfoEffect("as an additional cost to cast this spell, you may reveal a Dragon card from your hand")); - + this.getSpellAbility().addCost(new RevealDragonFromHandCost()); + // Counter target spell unless its controller pays {1}. If you revealed a Dragon card or controlled a Dragon as you cast Silumgar's Scorn, counter that spell instead. - this.getSpellAbility().addEffect(new SilumgarsScornCounterEffect()); + this.getSpellAbility().addEffect(new ConditionalOneShotEffect( + new CounterTargetEffect(), new CounterUnlessPaysEffect(new GenericManaCost(1)), + RevealedOrControlledDragonCondition.instance, "counter target spell unless its controller pays {1}. " + + "If you revealed a Dragon card or controlled a Dragon as you cast this spell, counter that spell instead" + )); this.getSpellAbility().addTarget(new TargetSpell()); - this.getSpellAbility().addWatcher(new DragonOnTheBattlefieldWhileSpellWasCastWatcher()); - } - - @Override - public void adjustCosts(Ability ability, Game game) { - if (ability.getAbilityType() == AbilityType.SPELL) { - Player controller = game.getPlayer(ability.getControllerId()); - if (controller != null) { - if (controller.getHand().count(filter, game) > 0) { - ability.addCost(new RevealTargetFromHandCost(new TargetCardInHand(0,1, filter))); - } - } - } - } - private SilumgarsScorn(final SilumgarsScorn card) { super(card); } @@ -69,48 +42,3 @@ public SilumgarsScorn copy() { return new SilumgarsScorn(this); } } - -class SilumgarsScornCounterEffect extends OneShotEffect { - - public SilumgarsScornCounterEffect() { - super(Outcome.Detriment); - staticText = "
Counter target spell unless its controller pays {1}. If you revealed a Dragon card or controlled a Dragon as you cast {this}, counter that spell instead"; - } - - public SilumgarsScornCounterEffect(final SilumgarsScornCounterEffect effect) { - super(effect); - } - - @Override - public SilumgarsScornCounterEffect copy() { - return new SilumgarsScornCounterEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - StackObject spell = game.getStack().getStackObject(targetPointer.getFirst(game, source)); - if (spell != null) { - Player player = game.getPlayer(spell.getControllerId()); - if (player != null) { - DragonOnTheBattlefieldWhileSpellWasCastWatcher watcher = game.getState().getWatcher(DragonOnTheBattlefieldWhileSpellWasCastWatcher.class); - boolean condition = watcher != null && watcher.castWithConditionTrue(source.getId()); - if (!condition) { - for (Cost cost: source.getCosts()) { - if (cost instanceof RevealTargetFromHandCost) { - condition = ((RevealTargetFromHandCost)cost).getNumberRevealedCards() > 0; - } - } - } - if (condition) { - return game.getStack().counter(spell.getId(), source, game); - } - if (!(player.chooseUse(Outcome.Benefit, "Would you like to pay {1} to prevent counter effect?", source, game) && - new GenericManaCost(1).pay(source, game, source, spell.getControllerId(), false))) { - return game.getStack().counter(spell.getId(), source, game); - } - } - } - return true; - } - -} diff --git a/Mage.Sets/src/mage/cards/s/SilvergladePathfinder.java b/Mage.Sets/src/mage/cards/s/SilvergladePathfinder.java index 4d045e0b4829..c88e7f55ce77 100644 --- a/Mage.Sets/src/mage/cards/s/SilvergladePathfinder.java +++ b/Mage.Sets/src/mage/cards/s/SilvergladePathfinder.java @@ -33,7 +33,7 @@ public SilvergladePathfinder(UUID ownerId, CardSetInfo setInfo) { // {1}{G}, {tap}, Discard a card: Search your library for a basic land card and put that card onto the battlefield tapped. Then shuffle your library. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 1, StaticFilters.FILTER_CARD_BASIC_LAND), true, true), + new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true, true), new ManaCostsImpl("{1}{G}")); ability.addCost(new TapSourceCost()); ability.addCost(new DiscardCardCost()); diff --git a/Mage.Sets/src/mage/cards/s/SilverpeltWerewolf.java b/Mage.Sets/src/mage/cards/s/SilverpeltWerewolf.java index 64fa68a4c261..e98bee109b98 100644 --- a/Mage.Sets/src/mage/cards/s/SilverpeltWerewolf.java +++ b/Mage.Sets/src/mage/cards/s/SilverpeltWerewolf.java @@ -1,30 +1,23 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.abilities.effects.common.TransformSourceEffect; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** - * * @author Loki */ public final class SilverpeltWerewolf extends CardImpl { public SilverpeltWerewolf(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},null); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, null); this.subtype.add(SubType.WEREWOLF); this.color.setGreen(true); @@ -38,8 +31,7 @@ public SilverpeltWerewolf(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Silverpelt Werewolf. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private SilverpeltWerewolf(final SilverpeltWerewolf card) { diff --git a/Mage.Sets/src/mage/cards/s/SilverquillApprentice.java b/Mage.Sets/src/mage/cards/s/SilverquillApprentice.java new file mode 100644 index 000000000000..69d80de86d4a --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SilverquillApprentice.java @@ -0,0 +1,43 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +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 TheElk801 + */ +public final class SilverquillApprentice extends CardImpl { + + public SilverquillApprentice(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Magecraft — Whenever you cast or copy and instant or sorcery spell, target creature gets +1/+0 until end of turn. + Ability ability = new MagecraftAbility(new BoostTargetEffect(1, 0, Duration.EndOfTurn)); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private SilverquillApprentice(final SilverquillApprentice card) { + super(card); + } + + @Override + public SilverquillApprentice copy() { + return new SilverquillApprentice(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SilverquillCampus.java b/Mage.Sets/src/mage/cards/s/SilverquillCampus.java new file mode 100644 index 000000000000..39c59903a02f --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SilverquillCampus.java @@ -0,0 +1,46 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SilverquillCampus extends CardImpl { + + public SilverquillCampus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // Silverquill Campus enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {W} or {B}. + this.addAbility(new WhiteManaAbility()); + this.addAbility(new BlackManaAbility()); + + // {4}, {T}: Scry 1. + Ability ability = new SimpleActivatedAbility(new ScryEffect(1), new GenericManaCost(4)); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private SilverquillCampus(final SilverquillCampus card) { + super(card); + } + + @Override + public SilverquillCampus copy() { + return new SilverquillCampus(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SilverquillCommand.java b/Mage.Sets/src/mage/cards/s/SilverquillCommand.java new file mode 100644 index 000000000000..4543da487eea --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SilverquillCommand.java @@ -0,0 +1,83 @@ +package mage.cards.s; + +import mage.abilities.Mode; +import mage.abilities.effects.common.DrawCardTargetEffect; +import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.abilities.effects.common.SacrificeEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Duration; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.target.TargetPlayer; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SilverquillCommand extends CardImpl { + + private static final FilterCard filter + = new FilterCreatureCard("creature card with mana value 2 or less from your graveyard"); + + static { + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); + } + + public SilverquillCommand(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{W}{B}"); + + // Choose two — + this.getSpellAbility().getModes().setMinModes(2); + this.getSpellAbility().getModes().setMaxModes(2); + + // • Target creature gets +3/+3 and gains flying until end of turn. + this.getSpellAbility().addEffect(new BoostTargetEffect( + 3, 3 + ).setText("target creature gets +3/+3")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + FlyingAbility.getInstance(), Duration.EndOfTurn + ).setText("and gains flying until end of turn")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + + // • Return target creature card with mana value 2 or less from your graveyard to the battlefield. + Mode mode = new Mode(new ReturnFromGraveyardToBattlefieldTargetEffect()); + mode.addTarget(new TargetCardInYourGraveyard(filter)); + this.getSpellAbility().addMode(mode); + + // • Target player draws a card and loses 1 life. + mode = new Mode(new DrawCardTargetEffect(1)); + mode.addEffect(new LoseLifeTargetEffect(1).setText("and loses 1 life")); + mode.addTarget(new TargetPlayer()); + this.getSpellAbility().addMode(mode); + + // • Target opponent sacrifices a creature. + mode = new Mode(new SacrificeEffect( + StaticFilters.FILTER_PERMANENT_CREATURE, + 1, "Target opponent" + )); + mode.addTarget(new TargetOpponent()); + this.getSpellAbility().addMode(mode); + } + + private SilverquillCommand(final SilverquillCommand card) { + super(card); + } + + @Override + public SilverquillCommand copy() { + return new SilverquillCommand(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SilverquillPledgemage.java b/Mage.Sets/src/mage/cards/s/SilverquillPledgemage.java new file mode 100644 index 000000000000..6d158088e13b --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SilverquillPledgemage.java @@ -0,0 +1,76 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +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.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SilverquillPledgemage extends CardImpl { + + public SilverquillPledgemage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W/B}{W/B}"); + + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, Silverquill Pledgemage gains your choice of flying or lifelink until end of turn. + this.addAbility(new MagecraftAbility(new SilverquillPledgemageEffect())); + } + + private SilverquillPledgemage(final SilverquillPledgemage card) { + super(card); + } + + @Override + public SilverquillPledgemage copy() { + return new SilverquillPledgemage(this); + } +} + +class SilverquillPledgemageEffect extends OneShotEffect { + + SilverquillPledgemageEffect() { + super(Outcome.Benefit); + staticText = "{this} gains your choice of flying or lifelink until end of turn"; + } + + private SilverquillPledgemageEffect(final SilverquillPledgemageEffect effect) { + super(effect); + } + + @Override + public SilverquillPledgemageEffect copy() { + return new SilverquillPledgemageEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getSourceId()); + if (player == null) { + return false; + } + game.addEffect(new GainAbilitySourceEffect(player.chooseUse( + Outcome.Neutral, "Choose flying or lifelink", null, + "Flying", "Lifelink", source, game + ) ? FlyingAbility.getInstance() : LifelinkAbility.getInstance(), Duration.EndOfTurn), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SilverquillSilencer.java b/Mage.Sets/src/mage/cards/s/SilverquillSilencer.java new file mode 100644 index 000000000000..8ea5ef8ed9f6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SilverquillSilencer.java @@ -0,0 +1,78 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.SpellCastOpponentTriggeredAbility; +import mage.abilities.effects.common.ChooseACardNameEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.LoseLifeTargetEffect; +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.FilterSpell; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SilverquillSilencer extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("a spell with the chosen name"); + + static { + filter.add(SilverquillSilencerPredicate.instance); + } + + public SilverquillSilencer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // As Silverquill Silencer enters the battlefield, choose a nonland card name. + this.addAbility(new AsEntersBattlefieldAbility( + new ChooseACardNameEffect(ChooseACardNameEffect.TypeOfName.NON_LAND_NAME) + )); + + // Whenever an opponent casts a spell with the chosen name, they lose 3 life and you draw a card. + Ability ability = new SpellCastOpponentTriggeredAbility( + Zone.BATTLEFIELD, new LoseLifeTargetEffect(3).setText("they lose 3 life"), + filter, false, SetTargetPointer.PLAYER + ); + ability.addEffect(new DrawCardSourceControllerEffect(1).concatBy("and you")); + this.addAbility(ability); + } + + private SilverquillSilencer(final SilverquillSilencer card) { + super(card); + } + + @Override + public SilverquillSilencer copy() { + return new SilverquillSilencer(this); + } +} + +enum SilverquillSilencerPredicate implements ObjectSourcePlayerPredicate> { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + String cardName = (String) game.getState().getValue( + input.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY + ); + return CardUtil.haveSameNames(input.getObject().getName(), cardName); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SimianSpiritGuide.java b/Mage.Sets/src/mage/cards/s/SimianSpiritGuide.java index b54fc6f7e742..601da394f5ba 100644 --- a/Mage.Sets/src/mage/cards/s/SimianSpiritGuide.java +++ b/Mage.Sets/src/mage/cards/s/SimianSpiritGuide.java @@ -1,30 +1,24 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.Mana; -import mage.abilities.Ability; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; +import mage.abilities.costs.common.ExileSourceFromHandCost; import mage.abilities.mana.SimpleManaAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.game.Game; -import mage.players.Player; + +import java.util.UUID; /** - * * @author Plopman */ public final class SimianSpiritGuide extends CardImpl { public SimianSpiritGuide(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); this.subtype.add(SubType.APE); this.subtype.add(SubType.SPIRIT); @@ -44,40 +38,3 @@ public SimianSpiritGuide copy() { return new SimianSpiritGuide(this); } } - - -class ExileSourceFromHandCost extends CostImpl { - - public ExileSourceFromHandCost() { - this.text = "Exile {this} from your hand"; - } - - public ExileSourceFromHandCost(ExileSourceFromHandCost cost) { - super(cost); - } - - @Override - public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - Card card = game.getCard(source.getSourceId()); - Player player = game.getPlayer(controllerId); - if (player != null && player.getHand().contains(source.getSourceId()) && card != null) { - paid = card.moveToExile(ability.getSourceId(), "from Hand", source, game); - } - return paid; - } - - @Override - public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { - Player player = game.getPlayer(controllerId); - if (player != null && player.getHand().contains(source.getSourceId())) { - return true; - } - return false; - } - - @Override - public ExileSourceFromHandCost copy() { - return new ExileSourceFromHandCost(this); - } - -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/SimicGuildmage.java b/Mage.Sets/src/mage/cards/s/SimicGuildmage.java index 8d339588936a..06cd89b6126e 100644 --- a/Mage.Sets/src/mage/cards/s/SimicGuildmage.java +++ b/Mage.Sets/src/mage/cards/s/SimicGuildmage.java @@ -19,7 +19,7 @@ import mage.filter.predicate.ObjectSourcePlayer; import mage.filter.predicate.ObjectSourcePlayerPredicate; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.filter.predicate.permanent.PermanentIdPredicate; import mage.game.Game; diff --git a/Mage.Sets/src/mage/cards/s/SinProdder.java b/Mage.Sets/src/mage/cards/s/SinProdder.java index afc9f90c73e8..965e931652de 100644 --- a/Mage.Sets/src/mage/cards/s/SinProdder.java +++ b/Mage.Sets/src/mage/cards/s/SinProdder.java @@ -49,7 +49,7 @@ class SinProdderEffect extends OneShotEffect { public SinProdderEffect() { super(Outcome.DrawCard); this.staticText = "reveal the top card of your library. Any opponent may have you put that card into your graveyard. If a player does, " - + "{this} deals damage to that player equal to that card's converted mana cost. Otherwise, put that card into your hand"; + + "{this} deals damage to that player equal to that card's mana value. Otherwise, put that card into your hand"; } public SinProdderEffect(final SinProdderEffect effect) { @@ -74,7 +74,7 @@ public boolean apply(Game game, Ability source) { Player opponent = game.getPlayer(opponentUuid); if (opponent != null && !putInGraveyard && opponent.chooseUse(Outcome.Damage, message, source, game)) { putInGraveyard = true; - opponent.damage(card.getConvertedManaCost(), source.getSourceId(), source, game); + opponent.damage(card.getManaValue(), source.getSourceId(), source, game); // 4/8/2016: Each opponent in turn order, starting with the one after you in turn order, may choose to have you put that card into your graveyard. // Once a player does so, Sin Prodder deals damage equal to that card's converted mana cost to that player immediately // and Sin Prodder's trigger has no further action. diff --git a/Mage.Sets/src/mage/cards/s/SingeMindOgre.java b/Mage.Sets/src/mage/cards/s/SingeMindOgre.java index 3ddefba0e3a2..6a4bd7833331 100644 --- a/Mage.Sets/src/mage/cards/s/SingeMindOgre.java +++ b/Mage.Sets/src/mage/cards/s/SingeMindOgre.java @@ -53,7 +53,7 @@ class SingeMindOgreEffect extends OneShotEffect { public SingeMindOgreEffect() { super(Outcome.LoseLife); - this.staticText = "target player reveals a card at random from their hand, then loses life equal to that card's converted mana cost"; + this.staticText = "target player reveals a card at random from their hand, then loses life equal to that card's mana value"; } public SingeMindOgreEffect(final SingeMindOgreEffect effect) { @@ -74,7 +74,7 @@ public boolean apply(Game game, Ability source) { if (card != null) { revealed.add(card); targetPlayer.revealCards("Singe-Mind Ogre", revealed, game); - targetPlayer.loseLife(card.getConvertedManaCost(), game, source, false); + targetPlayer.loseLife(card.getManaValue(), game, source, false); return true; } } diff --git a/Mage.Sets/src/mage/cards/s/SinsOfThePast.java b/Mage.Sets/src/mage/cards/s/SinsOfThePast.java index 0d0a008567ab..829b2f7138b3 100644 --- a/Mage.Sets/src/mage/cards/s/SinsOfThePast.java +++ b/Mage.Sets/src/mage/cards/s/SinsOfThePast.java @@ -4,7 +4,7 @@ import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.ExileCardEnteringGraveyardReplacementEffect; import mage.abilities.effects.common.ExileSourceEffect; import mage.cards.Card; import mage.cards.CardImpl; @@ -12,9 +12,6 @@ import mage.constants.*; import mage.filter.common.FilterInstantOrSorceryCard; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; -import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; @@ -65,10 +62,10 @@ public SinsOfThePastEffect copy() { public boolean apply(Game game, Ability source) { Card card = game.getCard(this.getTargetPointer().getFirst(game, source)); if (card != null) { - ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.GRAVEYARD, TargetController.YOU, Duration.EndOfTurn, true);; + ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.GRAVEYARD, TargetController.YOU, Duration.EndOfTurn, true); effect.setTargetPointer(new FixedTarget(card, game)); game.addEffect(effect, source); - effect = new SinsOfThePastReplacementEffect(card.getId()); + effect = new ExileCardEnteringGraveyardReplacementEffect(card.getId()); game.addEffect(effect, source); return true; } @@ -76,40 +73,3 @@ public boolean apply(Game game, Ability source) { } } -class SinsOfThePastReplacementEffect extends ReplacementEffectImpl { - - private final UUID cardId; - - SinsOfThePastReplacementEffect(UUID cardId) { - super(Duration.EndOfTurn, Outcome.Exile); - this.cardId = cardId; - } - - SinsOfThePastReplacementEffect(final SinsOfThePastReplacementEffect effect) { - super(effect); - this.cardId = effect.cardId; - } - - @Override - public SinsOfThePastReplacementEffect copy() { - return new SinsOfThePastReplacementEffect(this); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - ((ZoneChangeEvent) event).setToZone(Zone.EXILED); - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ZONE_CHANGE; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - return zEvent.getToZone() == Zone.GRAVEYARD - && zEvent.getTargetId().equals(this.cardId); - } -} diff --git a/Mage.Sets/src/mage/cards/s/SisayWeatherlightCaptain.java b/Mage.Sets/src/mage/cards/s/SisayWeatherlightCaptain.java index 08f7634c3ad3..1334f8291e3a 100644 --- a/Mage.Sets/src/mage/cards/s/SisayWeatherlightCaptain.java +++ b/Mage.Sets/src/mage/cards/s/SisayWeatherlightCaptain.java @@ -17,7 +17,7 @@ import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCardInLibrary; @@ -98,8 +98,8 @@ class SisayWeatherlightCaptainEffect extends OneShotEffect { SisayWeatherlightCaptainEffect() { super(Outcome.Benefit); - staticText = "Search your library for a legendary permanent card with converted mana cost " + - "less than {this}'s power, put that card onto the battlefield, then shuffle your library."; + staticText = "Search your library for a legendary permanent card with mana value " + + "less than {this}'s power, put that card onto the battlefield, then shuffle."; } private SisayWeatherlightCaptainEffect(final SisayWeatherlightCaptainEffect effect) { @@ -118,9 +118,9 @@ public boolean apply(Game game, Ability source) { return false; } int power = permanent.getPower().getValue(); - FilterCard filter = new FilterPermanentCard("legendary permanent card with converted mana cost less than " + power); + FilterCard filter = new FilterPermanentCard("legendary permanent card with mana value less than " + power); filter.add(SuperType.LEGENDARY.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, power)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, power)); return new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter)).apply(game, source); } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/SivvisValor.java b/Mage.Sets/src/mage/cards/s/SivvisValor.java index 3ca8971efb27..71eb4e01c3d7 100644 --- a/Mage.Sets/src/mage/cards/s/SivvisValor.java +++ b/Mage.Sets/src/mage/cards/s/SivvisValor.java @@ -81,7 +81,7 @@ public SivvisValorEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/s/SkarrganHellkite.java b/Mage.Sets/src/mage/cards/s/SkarrganHellkite.java index 91818d5cc2af..778f98de68d6 100644 --- a/Mage.Sets/src/mage/cards/s/SkarrganHellkite.java +++ b/Mage.Sets/src/mage/cards/s/SkarrganHellkite.java @@ -41,7 +41,7 @@ public SkarrganHellkite(UUID ownerId, CardSetInfo setInfo) { Zone.BATTLEFIELD, new DamageMultiEffect(2), new ManaCostsImpl("{3}{R}"), new SourceHasCounterCondition(CounterType.P1P1), "{3}{R}: {this} deals 2 damage divided as you choose among one or two targets. " + - "Activate this ability only if {this} has a +1/+1 counter on it." + "Activate only if {this} has a +1/+1 counter on it." ); ability.addTarget(new TargetAnyTargetAmount(2)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/SkeletalScrying.java b/Mage.Sets/src/mage/cards/s/SkeletalScrying.java index 5e62e29fa256..37ac388198b4 100644 --- a/Mage.Sets/src/mage/cards/s/SkeletalScrying.java +++ b/Mage.Sets/src/mage/cards/s/SkeletalScrying.java @@ -1,114 +1,70 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.CostAdjuster; import mage.abilities.costs.common.ExileFromGraveCost; -import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.ManacostVariableValue; -import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.InfoEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterCard; import mage.game.Game; -import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class SkeletalScrying extends CardImpl { public SkeletalScrying(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{X}{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{B}"); // As an additional cost to cast Skeletal Scrying, exile X cards from your graveyard. - Ability ability = new SimpleStaticAbility(Zone.ALL, new SkeletalScryingRuleEffect()); + Ability ability = new SimpleStaticAbility( + Zone.ALL, + new InfoEffect( + "as an additional cost to cast this spell, " + + "exile X cards from your graveyard" + )); ability.setRuleAtTheTop(true); this.addAbility(ability); - // You draw X cards and you lose X life. - this.getSpellAbility().addEffect(new SkeletalScryingEffect(ManacostVariableValue.instance)); + this.getSpellAbility().setCostAdjuster(SkeletalScryingAdjuster.instance); + // You draw X cards and you lose X life. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect( + ManacostVariableValue.instance + ).setText("you draw X cards")); + this.getSpellAbility().addEffect(new GainLifeEffect( + ManacostVariableValue.instance + ).concatBy("and")); } private SkeletalScrying(final SkeletalScrying card) { super(card); } - @Override - public void adjustCosts(Ability ability, Game game) { - int xValue = ability.getManaCostsToPay().getX(); - if (xValue > 0) { - ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(xValue, xValue, new FilterCard("cards from your graveyard")))); - } - } - @Override public SkeletalScrying copy() { return new SkeletalScrying(this); } } -class SkeletalScryingRuleEffect extends OneShotEffect { - - public SkeletalScryingRuleEffect() { - super(Outcome.Benefit); - this.staticText = "as an additional cost to cast this spell, exile X cards from your graveyard"; - } - - public SkeletalScryingRuleEffect(final SkeletalScryingRuleEffect effect) { - super(effect); - } +enum SkeletalScryingAdjuster implements CostAdjuster { + instance; + private static final FilterCard filter = new FilterCard("cards from your graveyard"); @Override - public SkeletalScryingRuleEffect copy() { - return new SkeletalScryingRuleEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } -} -class SkeletalScryingEffect extends OneShotEffect { - - protected DynamicValue amount; - - public SkeletalScryingEffect(int amount) { - this(StaticValue.get(amount)); - } - - public SkeletalScryingEffect(DynamicValue amount) { - super(Outcome.Neutral); - this.amount = amount.copy(); - staticText = "You draw " + amount + " cards and you lose " + amount + " life"; - } - - public SkeletalScryingEffect(final SkeletalScryingEffect effect) { - super(effect); - this.amount = effect.amount; - } - - @Override - public SkeletalScryingEffect copy() { - return new SkeletalScryingEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if ( controller != null ) { - controller.drawCards(amount.calculate(game, source, this), source, game); - controller.loseLife(amount.calculate(game, source, this), game, source, false); - return true; - } - return false; + public void adjustCosts(Ability ability, Game game) { + int xValue = ability.getManaCostsToPay().getX(); + if (xValue > 0) { + ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(xValue, xValue, filter))); + } } } diff --git a/Mage.Sets/src/mage/cards/s/SkemfarAvenger.java b/Mage.Sets/src/mage/cards/s/SkemfarAvenger.java index 21501ed86634..1d275019af2d 100644 --- a/Mage.Sets/src/mage/cards/s/SkemfarAvenger.java +++ b/Mage.Sets/src/mage/cards/s/SkemfarAvenger.java @@ -12,7 +12,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/s/Skinshifter.java b/Mage.Sets/src/mage/cards/s/Skinshifter.java index a873598290ba..8019094272ab 100644 --- a/Mage.Sets/src/mage/cards/s/Skinshifter.java +++ b/Mage.Sets/src/mage/cards/s/Skinshifter.java @@ -1,11 +1,9 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.Mode; -import mage.abilities.common.LimitedTimesPerTurnActivatedAbility; +import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; import mage.abilities.keyword.FlyingAbility; @@ -13,28 +11,29 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; import mage.game.permanent.token.TokenImpl; +import java.util.UUID; + /** - * * @author North */ public final class Skinshifter extends CardImpl { public Skinshifter(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SHAMAN); this.power = new MageInt(1); this.toughness = new MageInt(1); - Ability ability = new LimitedTimesPerTurnActivatedAbility(Zone.BATTLEFIELD, + Ability ability = new SimpleActivatedAbility( new BecomesCreatureSourceEffect(new RhinoToken(), "", Duration.EndOfTurn), - new ManaCostsImpl("{G}")); + new ManaCostsImpl<>("{G}")); + ability.getModes().setChooseText("Choose one. Activate only once each turn."); Mode mode = new Mode(); mode.addEffect(new BecomesCreatureSourceEffect(new BirdToken(), "", Duration.EndOfTurn)); @@ -56,7 +55,7 @@ public Skinshifter copy() { return new Skinshifter(this); } - private class RhinoToken extends TokenImpl { + private static final class RhinoToken extends TokenImpl { public RhinoToken() { super("Rhino", "Rhino with base power and toughness 4/4 and gains trample"); @@ -68,6 +67,7 @@ public RhinoToken() { this.toughness = new MageInt(4); this.addAbility(TrampleAbility.getInstance()); } + public RhinoToken(final RhinoToken token) { super(token); } @@ -77,7 +77,7 @@ public RhinoToken copy() { } } - private class BirdToken extends TokenImpl { + private static final class BirdToken extends TokenImpl { public BirdToken() { super("Bird", "Bird with base power and toughness 2/2 and gains flying"); @@ -89,6 +89,7 @@ public BirdToken() { this.toughness = new MageInt(2); this.addAbility(FlyingAbility.getInstance()); } + public BirdToken(final BirdToken token) { super(token); } @@ -98,7 +99,7 @@ public BirdToken copy() { } } - private class PlantToken extends TokenImpl { + private static final class PlantToken extends TokenImpl { public PlantToken() { super("Plant", "Plant with base power and toughness 0/8"); @@ -109,6 +110,7 @@ public PlantToken() { this.power = new MageInt(0); this.toughness = new MageInt(8); } + public PlantToken(final PlantToken token) { super(token); } diff --git a/Mage.Sets/src/mage/cards/s/SkirkAlarmist.java b/Mage.Sets/src/mage/cards/s/SkirkAlarmist.java index ccd8731446b5..3f3cc3eca488 100644 --- a/Mage.Sets/src/mage/cards/s/SkirkAlarmist.java +++ b/Mage.Sets/src/mage/cards/s/SkirkAlarmist.java @@ -19,7 +19,7 @@ import mage.constants.Outcome; import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.other.FaceDownPredicate; +import mage.filter.predicate.card.FaceDownPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/s/SkitteringMonstrosity.java b/Mage.Sets/src/mage/cards/s/SkitteringMonstrosity.java index 25a20ef5c47d..be920cadcadc 100644 --- a/Mage.Sets/src/mage/cards/s/SkitteringMonstrosity.java +++ b/Mage.Sets/src/mage/cards/s/SkitteringMonstrosity.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; @@ -11,8 +9,9 @@ import mage.constants.SubType; import mage.filter.StaticFilters; +import java.util.UUID; + /** - * * @author nigelzor */ public final class SkitteringMonstrosity extends CardImpl { @@ -24,7 +23,10 @@ public SkitteringMonstrosity(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(5); // When you cast a creature spell, sacrifice Skittering Monstrosity. - this.addAbility(new SpellCastControllerTriggeredAbility(new SacrificeSourceEffect(), StaticFilters.FILTER_SPELL_A_CREATURE, false)); + this.addAbility(new SpellCastControllerTriggeredAbility( + new SacrificeSourceEffect(), StaticFilters.FILTER_SPELL_A_CREATURE, + false, "When you cast a creature spell, sacrifice {this}." + )); } private SkitteringMonstrosity(final SkitteringMonstrosity card) { diff --git a/Mage.Sets/src/mage/cards/s/Skitterskin.java b/Mage.Sets/src/mage/cards/s/Skitterskin.java index f98e4c404ee8..ed40db6bfc4c 100644 --- a/Mage.Sets/src/mage/cards/s/Skitterskin.java +++ b/Mage.Sets/src/mage/cards/s/Skitterskin.java @@ -17,7 +17,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.ColorlessPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/s/SkophosWarleader.java b/Mage.Sets/src/mage/cards/s/SkophosWarleader.java index 114b2bb080d3..f943dc1a5e0d 100644 --- a/Mage.Sets/src/mage/cards/s/SkophosWarleader.java +++ b/Mage.Sets/src/mage/cards/s/SkophosWarleader.java @@ -16,7 +16,7 @@ import mage.constants.SubType; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/s/Skullsnatcher.java b/Mage.Sets/src/mage/cards/s/Skullsnatcher.java index 7b98a150a877..ab7491100421 100644 --- a/Mage.Sets/src/mage/cards/s/Skullsnatcher.java +++ b/Mage.Sets/src/mage/cards/s/Skullsnatcher.java @@ -15,12 +15,11 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.filter.predicate.permanent.UnblockedPredicate; import mage.game.Game; import mage.game.events.DamagedPlayerEvent; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.target.common.TargetCardInOpponentsGraveyard; /** diff --git a/Mage.Sets/src/mage/cards/s/SkycatSovereign.java b/Mage.Sets/src/mage/cards/s/SkycatSovereign.java index 9991c2032b6b..4586b90fa234 100644 --- a/Mage.Sets/src/mage/cards/s/SkycatSovereign.java +++ b/Mage.Sets/src/mage/cards/s/SkycatSovereign.java @@ -17,7 +17,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.CatBirdToken; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/s/SkyclaveApparition.java b/Mage.Sets/src/mage/cards/s/SkyclaveApparition.java index 12b70ecc4081..55e68f277cad 100644 --- a/Mage.Sets/src/mage/cards/s/SkyclaveApparition.java +++ b/Mage.Sets/src/mage/cards/s/SkyclaveApparition.java @@ -13,7 +13,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterNonlandPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.ExileZone; import mage.game.Game; @@ -33,13 +33,13 @@ public final class SkyclaveApparition extends CardImpl { private static final FilterPermanent filter = new FilterNonlandPermanent( - "nonland, nontoken permanent you don't control with converted mana cost 4 or less" + "nonland, nontoken permanent you don't control with mana value 4 or less" ); static { filter.add(Predicates.not(TokenPredicate.instance)); filter.add(TargetController.NOT_YOU.getControllerPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 5)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 5)); } public SkyclaveApparition(UUID ownerId, CardSetInfo setInfo) { @@ -74,7 +74,7 @@ class SkyclaveApparitionEffect extends OneShotEffect { SkyclaveApparitionEffect() { super(Outcome.Benefit); staticText = "the exiled card's owner creates an X/X blue Illusion creature token, " + - "where X is the converted mana cost of the exiled card"; + "where X is the mana value of the exiled card"; } private SkyclaveApparitionEffect(final SkyclaveApparitionEffect effect) { @@ -107,7 +107,7 @@ public boolean apply(Game game, Ability source) { .stream() .filter(Objects::nonNull) .map(card -> owners.add(card.getOwnerId()) ? card : card) - .mapToInt(MageObject::getConvertedManaCost) + .mapToInt(MageObject::getManaValue) .sum(); for (UUID playerId : owners) { new CustomIllusionToken(totalCMC).putOntoBattlefield(1, game, source, playerId); diff --git a/Mage.Sets/src/mage/cards/s/SkyfireKirin.java b/Mage.Sets/src/mage/cards/s/SkyfireKirin.java index 754830914319..aee7bfba3585 100644 --- a/Mage.Sets/src/mage/cards/s/SkyfireKirin.java +++ b/Mage.Sets/src/mage/cards/s/SkyfireKirin.java @@ -14,7 +14,7 @@ import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.Spell; @@ -67,10 +67,10 @@ enum SkyfireKirinAdjuster implements TargetAdjuster { public void adjustTargets(Ability ability, Game game) { Spell spell = game.getStack().getSpell(ability.getEffects().get(0).getTargetPointer().getFirst(game, ability)); if (spell != null) { - int cmc = spell.getConvertedManaCost(); + int cmc = spell.getManaValue(); ability.getTargets().clear(); - FilterPermanent filter = new FilterCreaturePermanent("creature with converted mana cost " + cmc); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, cmc)); + FilterPermanent filter = new FilterCreaturePermanent("creature with mana value " + cmc); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, cmc)); ability.addTarget(new TargetPermanent(filter)); } } @@ -80,7 +80,7 @@ class SkyfireKirinEffect extends OneShotEffect { public SkyfireKirinEffect() { super(Outcome.Detriment); - this.staticText = "you may gain control of target creature with that spell's converted mana cost until end of turn"; + this.staticText = "you may gain control of target creature with that spell's mana value until end of turn"; } public SkyfireKirinEffect(final SkyfireKirinEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/Skygames.java b/Mage.Sets/src/mage/cards/s/Skygames.java index b546a3654802..b2a05fc6751c 100644 --- a/Mage.Sets/src/mage/cards/s/Skygames.java +++ b/Mage.Sets/src/mage/cards/s/Skygames.java @@ -40,7 +40,7 @@ public Skygames(UUID ownerId, CardSetInfo setInfo) { Ability gainAbility = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), new TapSourceCost()); gainAbility.addTarget(new TargetCreaturePermanent()); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(gainAbility, AttachmentType.AURA, - Duration.WhileOnBattlefield, "Enchanted land has \"{T}: Target creature gains flying until end of turn. Activate this ability only any time you could cast a sorcery.\""))); + Duration.WhileOnBattlefield, "Enchanted land has \"{T}: Target creature gains flying until end of turn. Activate only as a sorcery.\""))); } private Skygames(final Skygames card) { diff --git a/Mage.Sets/src/mage/cards/s/SkyriderPatrol.java b/Mage.Sets/src/mage/cards/s/SkyriderPatrol.java index 1ab4b0e83927..0c646acf190c 100644 --- a/Mage.Sets/src/mage/cards/s/SkyriderPatrol.java +++ b/Mage.Sets/src/mage/cards/s/SkyriderPatrol.java @@ -16,7 +16,7 @@ import mage.constants.TargetController; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/s/SkyshipWeatherlight.java b/Mage.Sets/src/mage/cards/s/SkyshipWeatherlight.java index 6f0e4d04ac80..ba85c402087d 100644 --- a/Mage.Sets/src/mage/cards/s/SkyshipWeatherlight.java +++ b/Mage.Sets/src/mage/cards/s/SkyshipWeatherlight.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.cards.s; import java.util.UUID; @@ -72,7 +67,7 @@ class SkyshipWeatherlightEffect extends SearchEffect { public SkyshipWeatherlightEffect() { super(new TargetCardInLibrary(0, Integer.MAX_VALUE, filter), Outcome.Neutral); - this.staticText = "search your library for any number of artifact and/or creature cards and exile them. Then shuffle your library"; + this.staticText = "search your library for any number of artifact and/or creature cards and exile them. Then shuffle"; } diff --git a/Mage.Sets/src/mage/cards/s/Skyshooter.java b/Mage.Sets/src/mage/cards/s/Skyshooter.java index c450d57a0610..d0c9105aa573 100644 --- a/Mage.Sets/src/mage/cards/s/Skyshooter.java +++ b/Mage.Sets/src/mage/cards/s/Skyshooter.java @@ -1,10 +1,9 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.FlyingAbility; @@ -13,25 +12,27 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; +import mage.filter.FilterPermanent; import mage.filter.common.FilterAttackingOrBlockingCreature; import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.target.common.TargetAttackingOrBlockingCreature; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class Skyshooter extends CardImpl { - private static final FilterAttackingOrBlockingCreature filter = new FilterAttackingOrBlockingCreature("attacking or blocking creature with flying"); + private static final FilterPermanent filter + = new FilterAttackingOrBlockingCreature("attacking or blocking creature with flying"); static { filter.add(new AbilityPredicate(FlyingAbility.class)); } public Skyshooter(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); this.subtype.add(SubType.CENTAUR); this.subtype.add(SubType.ARCHER); @@ -40,11 +41,12 @@ public Skyshooter(UUID ownerId, CardSetInfo setInfo) { // Reach this.addAbility(ReachAbility.getInstance()); + // {tap}, Sacrifice Skyshooter: Destroy target attacking or blocking creature with flying. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new TapSourceCost()); - ability.addTarget(new TargetAttackingOrBlockingCreature(1,1, filter, false)); + Ability ability = new SimpleActivatedAbility(new DestroyTargetEffect(), new TapSourceCost()); + ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); - } private Skyshooter(final Skyshooter card) { diff --git a/Mage.Sets/src/mage/cards/s/SlashTheRanks.java b/Mage.Sets/src/mage/cards/s/SlashTheRanks.java index 879c182a3046..5d68622a4ecc 100644 --- a/Mage.Sets/src/mage/cards/s/SlashTheRanks.java +++ b/Mage.Sets/src/mage/cards/s/SlashTheRanks.java @@ -7,7 +7,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.CommanderPredicate; +import mage.filter.predicate.mageobject.CommanderPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/s/SlaughterPriestOfMogis.java b/Mage.Sets/src/mage/cards/s/SlaughterPriestOfMogis.java index 1c6a1ba33b44..c6834e23dc0b 100644 --- a/Mage.Sets/src/mage/cards/s/SlaughterPriestOfMogis.java +++ b/Mage.Sets/src/mage/cards/s/SlaughterPriestOfMogis.java @@ -17,7 +17,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.target.common.TargetControlledPermanent; diff --git a/Mage.Sets/src/mage/cards/s/SlaughterTheStrong.java b/Mage.Sets/src/mage/cards/s/SlaughterTheStrong.java index 18251fde026e..65a4786aaefb 100644 --- a/Mage.Sets/src/mage/cards/s/SlaughterTheStrong.java +++ b/Mage.Sets/src/mage/cards/s/SlaughterTheStrong.java @@ -92,14 +92,16 @@ public boolean apply(Game game, Ability source) { // human can de-select targets, but AI must choose only one time Target target; - if (player.isHuman()) { - target = new TargetPermanent(0, 1, currentFilter, true); - } else { + if (player.isComputer()) { + // AI settings FilterControlledCreaturePermanent strictFilter = currentFilter.copy(); selectedCreatures.stream().forEach(id -> { strictFilter.add(Predicates.not(new PermanentIdPredicate(id))); }); target = new TargetPermanent(0, 1, strictFilter, true); + } else { + // Human settings + target = new TargetPermanent(0, 1, currentFilter, true); } player.chooseTarget(Outcome.BoostCreature, target, source, game); @@ -110,7 +112,11 @@ public boolean apply(Game game, Ability source) { selectedCreatures.add(target.getFirstTarget()); } } else { - if (player.isHuman()) { + if (player.isComputer()) { + // AI stops + selectionDone = true; + } else { + // Human can continue String selected = "Selected: "; for (UUID creatureId : selectedCreatures) { Permanent creature = game.getPermanent(creatureId); @@ -123,8 +129,6 @@ public boolean apply(Game game, Ability source) { selected, "End the selection", "Continue the selection", source, game); - } else { - selectionDone = true; } } } diff --git a/Mage.Sets/src/mage/cards/s/SlimefootTheStowaway.java b/Mage.Sets/src/mage/cards/s/SlimefootTheStowaway.java index 5cf4db8334d6..72f5a00063ab 100644 --- a/Mage.Sets/src/mage/cards/s/SlimefootTheStowaway.java +++ b/Mage.Sets/src/mage/cards/s/SlimefootTheStowaway.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DiesCreatureTriggeredAbility; @@ -10,27 +8,24 @@ import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DamagePlayersEffect; import mage.abilities.effects.common.GainLifeEffect; -import mage.constants.SubType; -import mage.constants.SuperType; 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.constants.Zone; import mage.filter.common.FilterControlledPermanent; import mage.game.permanent.token.SaprolingToken; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class SlimefootTheStowaway extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("a Saproling you control"); - - static { - filter.add(SubType.SAPROLING.getPredicate()); - } + private static final FilterControlledPermanent filter + = new FilterControlledPermanent(SubType.SAPROLING, "a Saproling you control"); public SlimefootTheStowaway(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{G}"); @@ -41,12 +36,14 @@ public SlimefootTheStowaway(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(3); // Whenever a Saproling you control dies, Slimefoot, the Stowaway deals 1 damage to each opponent and you gain 1 life. - Ability ability = new DiesCreatureTriggeredAbility(new DamagePlayersEffect(1, TargetController.OPPONENT), false, filter); - ability.addEffect(new GainLifeEffect(1)); + Ability ability = new DiesCreatureTriggeredAbility( + new DamagePlayersEffect(1, TargetController.OPPONENT), false, filter + ); + ability.addEffect(new GainLifeEffect(1).concatBy("and")); this.addAbility(ability); // {4}: Create a 1/1 green Saproling creature token. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new SaprolingToken()), new ManaCostsImpl("{4}"))); + this.addAbility(new SimpleActivatedAbility(new CreateTokenEffect(new SaprolingToken()), new ManaCostsImpl("{4}"))); } private SlimefootTheStowaway(final SlimefootTheStowaway card) { diff --git a/Mage.Sets/src/mage/cards/s/Slitherwisp.java b/Mage.Sets/src/mage/cards/s/Slitherwisp.java index 5a7c97216260..d1dc58327aae 100644 --- a/Mage.Sets/src/mage/cards/s/Slitherwisp.java +++ b/Mage.Sets/src/mage/cards/s/Slitherwisp.java @@ -12,7 +12,7 @@ import mage.constants.SubType; import mage.filter.FilterSpell; import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/s/SliverHive.java b/Mage.Sets/src/mage/cards/s/SliverHive.java index 5c64dd8dc851..3a6ecaf21d98 100644 --- a/Mage.Sets/src/mage/cards/s/SliverHive.java +++ b/Mage.Sets/src/mage/cards/s/SliverHive.java @@ -50,7 +50,7 @@ public SliverHive(UUID ownerId, CardSetInfo setInfo) { // {5}, {T}: Create a 1/1 colorless Sliver creature token. Activate this ability only if you control a Sliver. Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new SliverToken()), new TapSourceCost(), new PermanentsOnTheBattlefieldCondition(filter), - "{5}, {T}: Create a 1/1 colorless Sliver creature token. Activate this ability only if you control a Sliver."); + "{5}, {T}: Create a 1/1 colorless Sliver creature token. Activate only if you control a Sliver."); ability.addCost(new GenericManaCost(5)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SliverLegion.java b/Mage.Sets/src/mage/cards/s/SliverLegion.java index cab7d50d2ace..0b5ec240544d 100644 --- a/Mage.Sets/src/mage/cards/s/SliverLegion.java +++ b/Mage.Sets/src/mage/cards/s/SliverLegion.java @@ -11,7 +11,7 @@ import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/s/Sliversmith.java b/Mage.Sets/src/mage/cards/s/Sliversmith.java index d13b9ecf025d..bdfade99e92a 100644 --- a/Mage.Sets/src/mage/cards/s/Sliversmith.java +++ b/Mage.Sets/src/mage/cards/s/Sliversmith.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -14,7 +13,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.game.permanent.token.SliversmithToken; +import mage.game.permanent.token.MetallicSliverToken; /** * @@ -28,8 +27,8 @@ public Sliversmith(UUID ownerId, CardSetInfo setInfo) { this.power = new MageInt(1); this.toughness = new MageInt(1); - // {1}, {tap}, Discard a card: Create a 1/1 colorless Sliver artifact creature token named Metallic Sliver. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new SliversmithToken()), new ManaCostsImpl("{1}")); + // {1}, {T}, Discard a card: Create a 1/1 colorless Sliver artifact creature token named Metallic Sliver. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new MetallicSliverToken()), new ManaCostsImpl("{1}")); ability.addCost(new TapSourceCost()); ability.addCost(new DiscardCardCost()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/SludgeStrider.java b/Mage.Sets/src/mage/cards/s/SludgeStrider.java index 3b0fe581c621..53e6fdb4ad0a 100644 --- a/Mage.Sets/src/mage/cards/s/SludgeStrider.java +++ b/Mage.Sets/src/mage/cards/s/SludgeStrider.java @@ -10,10 +10,9 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterArtifactPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/s/SlumberingTora.java b/Mage.Sets/src/mage/cards/s/SlumberingTora.java index 6923cf64827b..0a3d9795181b 100644 --- a/Mage.Sets/src/mage/cards/s/SlumberingTora.java +++ b/Mage.Sets/src/mage/cards/s/SlumberingTora.java @@ -54,7 +54,7 @@ private static class SlumberingToraEffect extends ContinuousEffectImpl { private SlumberingToraEffect() { super(Duration.EndOfTurn, Outcome.BecomeCreature); staticText = "{this} becomes an X/X Cat artifact creature until end of turn, " + - "where X is the discarded card's converted mana cost"; + "where X is the discarded card's mana value"; } private SlumberingToraEffect(final SlumberingToraEffect effect) { @@ -71,7 +71,7 @@ public void init(Ability source, Game game) { super.init(source, game); for (Cost cost : source.getCosts()) { if (cost instanceof DiscardTargetCost && !((DiscardTargetCost) cost).getCards().isEmpty()) { - convManaCosts = ((DiscardTargetCost) cost).getCards().get(0).getConvertedManaCost(); + convManaCosts = ((DiscardTargetCost) cost).getCards().get(0).getManaValue(); return; } } diff --git a/Mage.Sets/src/mage/cards/s/SlyInstigator.java b/Mage.Sets/src/mage/cards/s/SlyInstigator.java new file mode 100644 index 000000000000..f75c8b96454b --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SlyInstigator.java @@ -0,0 +1,52 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.combat.CantBeBlockedTargetEffect; +import mage.abilities.effects.common.combat.GoadTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SlyInstigator extends CardImpl { + + public SlyInstigator(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // {U}, {T}: Until your next turn, target creature an opponent controls can't be blocked. Goad that creature. + Ability ability = new SimpleActivatedAbility( + new CantBeBlockedTargetEffect(Duration.UntilYourNextTurn) + .setText("until your next turn, target creature an opponent controls can't be blocked."), + new ManaCostsImpl<>("{U}") + ); + ability.addCost(new TapSourceCost()); + ability.addEffect(new GoadTargetEffect().setText("Goad that creature")); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + this.addAbility(ability); + } + + private SlyInstigator(final SlyInstigator card) { + super(card); + } + + @Override + public SlyInstigator copy() { + return new SlyInstigator(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SmokeTeller.java b/Mage.Sets/src/mage/cards/s/SmokeTeller.java index 52aece52e8fe..d015a6034010 100644 --- a/Mage.Sets/src/mage/cards/s/SmokeTeller.java +++ b/Mage.Sets/src/mage/cards/s/SmokeTeller.java @@ -17,7 +17,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.other.FaceDownPredicate; +import mage.filter.predicate.card.FaceDownPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/s/SmolderingTar.java b/Mage.Sets/src/mage/cards/s/SmolderingTar.java index 266ff45bf477..64362a07f186 100644 --- a/Mage.Sets/src/mage/cards/s/SmolderingTar.java +++ b/Mage.Sets/src/mage/cards/s/SmolderingTar.java @@ -31,7 +31,7 @@ public SmolderingTar(UUID ownerId, CardSetInfo setInfo) { ability.addTarget(new TargetPlayer()); this.addAbility(ability); // Sacrifice Smoldering Tar: Smoldering Tar deals 4 damage to target creature. Activate this ability only any time you could cast a sorcery. - ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(4), new SacrificeSourceCost()); + ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(4, "it"), new SacrificeSourceCost()); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/Smother.java b/Mage.Sets/src/mage/cards/s/Smother.java index 53a0dc0b2464..05b6892795ab 100644 --- a/Mage.Sets/src/mage/cards/s/Smother.java +++ b/Mage.Sets/src/mage/cards/s/Smother.java @@ -8,7 +8,7 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCreaturePermanent; /** @@ -17,10 +17,10 @@ */ public final class Smother extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with converted mana cost 3 or less"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with mana value 3 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public Smother(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/s/SnowDay.java b/Mage.Sets/src/mage/cards/s/SnowDay.java new file mode 100644 index 000000000000..b61ca17f77f3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SnowDay.java @@ -0,0 +1,39 @@ +package mage.cards.s; + +import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; +import mage.abilities.effects.common.DrawDiscardControllerEffect; +import mage.abilities.effects.common.DrawDiscardTargetEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SnowDay extends CardImpl { + + public SnowDay(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{U}{U}"); + + // Tap up to two target creatures. Those creatures don't untap during their controller's next untap step. + this.getSpellAbility().addEffect(new TapTargetEffect()); + this.getSpellAbility().addEffect(new DontUntapInControllersNextUntapStepTargetEffect("Those creatures")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 2)); + + // Draw two cards, then discard a card. + this.getSpellAbility().addEffect(new DrawDiscardControllerEffect(2, 1).concatBy("
")); + } + + private SnowDay(final SnowDay card) { + super(card); + } + + @Override + public SnowDay copy() { + return new SnowDay(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/Solarion.java b/Mage.Sets/src/mage/cards/s/Solarion.java index 7c169d7531b9..bfa4c87cd269 100644 --- a/Mage.Sets/src/mage/cards/s/Solarion.java +++ b/Mage.Sets/src/mage/cards/s/Solarion.java @@ -1,39 +1,34 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.dynamicvalue.common.CountersSourceCount; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.common.DoubleCountersSourceEffect; import mage.abilities.keyword.SunburstAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; import mage.counters.CounterType; +import java.util.UUID; + /** - * * @author Plopman */ public final class Solarion extends CardImpl { public Solarion(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{7}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{7}"); this.subtype.add(SubType.CONSTRUCT); this.power = new MageInt(0); this.toughness = new MageInt(0); // Sunburst this.addAbility(new SunburstAbility(this)); + // {tap}: Double the number of +1/+1 counters on Solarion. - Effect effect = new AddCountersSourceEffect(CounterType.P1P1.createInstance(1), new CountersSourceCount(CounterType.P1P1), true); - effect.setText("Double the number of +1/+1 counters on {this}"); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new TapSourceCost())); + this.addAbility(new SimpleActivatedAbility(new DoubleCountersSourceEffect(CounterType.P1P1), new TapSourceCost())); } private Solarion(final Solarion card) { diff --git a/Mage.Sets/src/mage/cards/s/SoldeviAdnate.java b/Mage.Sets/src/mage/cards/s/SoldeviAdnate.java index 03c1d9ceb95a..f3efd81b6a42 100644 --- a/Mage.Sets/src/mage/cards/s/SoldeviAdnate.java +++ b/Mage.Sets/src/mage/cards/s/SoldeviAdnate.java @@ -41,7 +41,7 @@ public SoldeviAdnate(UUID ownerId, CardSetInfo setInfo) { // {T}, Sacrifice a black or artifact creature: Add an amount of {B} equal to the sacrificed creature's converted mana cost. Ability ability = new DynamicManaAbility(Mana.BlackMana(1), new SacrificeCostConvertedMana("creature"), new TapSourceCost(), - "add an amount of {B} equal to the sacrificed creature's converted mana cost" , false, + "add an amount of {B} equal to the sacrificed creature's mana value" , false, new HighestCMCOfPermanentValue(filter, true)); ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter))); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/SolemnSimulacrum.java b/Mage.Sets/src/mage/cards/s/SolemnSimulacrum.java index d5981a1cb0e5..c9564dd99d04 100644 --- a/Mage.Sets/src/mage/cards/s/SolemnSimulacrum.java +++ b/Mage.Sets/src/mage/cards/s/SolemnSimulacrum.java @@ -28,7 +28,7 @@ public SolemnSimulacrum(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new EntersBattlefieldTriggeredAbility( new SearchLibraryPutInPlayEffect( new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true) - .setText("search your library for a basic land card, put that card onto the battlefield tapped, then shuffle your library"), + .setText("search your library for a basic land card, put that card onto the battlefield tapped, then shuffle"), true)); this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), true)); } diff --git a/Mage.Sets/src/mage/cards/s/SolitaryHunter.java b/Mage.Sets/src/mage/cards/s/SolitaryHunter.java index 06ef64b379b7..12a6694d70fb 100644 --- a/Mage.Sets/src/mage/cards/s/SolitaryHunter.java +++ b/Mage.Sets/src/mage/cards/s/SolitaryHunter.java @@ -1,20 +1,14 @@ - package mage.cards.s; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** * @author LevelX2 @@ -35,8 +29,7 @@ public SolitaryHunter(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each upkeep, if no spells were cast last turn, transform Solitary Hunter. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private SolitaryHunter(final SolitaryHunter card) { diff --git a/Mage.Sets/src/mage/cards/s/SolveTheEquation.java b/Mage.Sets/src/mage/cards/s/SolveTheEquation.java new file mode 100644 index 000000000000..7c6d6fbe6b68 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SolveTheEquation.java @@ -0,0 +1,34 @@ +package mage.cards.s; + +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SolveTheEquation extends CardImpl { + + public SolveTheEquation(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}"); + + // Search your library for an instant or sorcery card, reveal it, put it into your hand, then shuffle. + this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect( + new TargetCardInLibrary(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY), true, true + )); + } + + private SolveTheEquation(final SolveTheEquation card) { + super(card); + } + + @Override + public SolveTheEquation copy() { + return new SolveTheEquation(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SoothsayerAdept.java b/Mage.Sets/src/mage/cards/s/SoothsayerAdept.java new file mode 100644 index 000000000000..41c3446bf6c9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SoothsayerAdept.java @@ -0,0 +1,45 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DrawDiscardControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SoothsayerAdept extends CardImpl { + + public SoothsayerAdept(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + + this.subtype.add(SubType.MERFOLK); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // {1}{U}, {T}: Draw a card, then discard a card. + Ability ability = new SimpleActivatedAbility( + new DrawDiscardControllerEffect(), new ManaCostsImpl("{1}{U}") + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private SoothsayerAdept(final SoothsayerAdept card) { + super(card); + } + + @Override + public SoothsayerAdept copy() { + return new SoothsayerAdept(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SorceressQueen.java b/Mage.Sets/src/mage/cards/s/SorceressQueen.java index 6e498ff6623b..c47d4fc81f3b 100644 --- a/Mage.Sets/src/mage/cards/s/SorceressQueen.java +++ b/Mage.Sets/src/mage/cards/s/SorceressQueen.java @@ -14,7 +14,7 @@ import mage.constants.Duration; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/s/SorinGrimNemesis.java b/Mage.Sets/src/mage/cards/s/SorinGrimNemesis.java index 441eead7ca7f..3f480e662561 100644 --- a/Mage.Sets/src/mage/cards/s/SorinGrimNemesis.java +++ b/Mage.Sets/src/mage/cards/s/SorinGrimNemesis.java @@ -22,12 +22,12 @@ * @author fireshoes */ public final class SorinGrimNemesis extends CardImpl { - + public SorinGrimNemesis(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{4}{W}{B}"); this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SORIN); - + this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(6)); // +1: Reveal the top card of your library and put that card into your hand. Each opponent loses life equal to its converted mana cost. @@ -42,11 +42,11 @@ public SorinGrimNemesis(UUID ownerId, CardSetInfo setInfo) { // -9: Create a number of 1/1 black Vampire Knight creature tokens with lifelink equal to the highest life total among all players. this.addAbility(new LoyaltyAbility(new SorinTokenEffect(), -9)); } - + private SorinGrimNemesis(final SorinGrimNemesis card) { super(card); } - + @Override public SorinGrimNemesis copy() { return new SorinGrimNemesis(this); @@ -54,41 +54,41 @@ public SorinGrimNemesis copy() { } class SorinGrimNemesisRevealEffect extends OneShotEffect { - + public SorinGrimNemesisRevealEffect() { super(Outcome.DrawCard); - this.staticText = "reveal the top card of your library and put that card into your hand. Each opponent loses life equal to that card's converted mana cost"; + this.staticText = "reveal the top card of your library and put that card into your hand. Each opponent loses life equal to its mana value"; } - + public SorinGrimNemesisRevealEffect(final SorinGrimNemesisRevealEffect effect) { super(effect); } - + @Override public SorinGrimNemesisRevealEffect copy() { return new SorinGrimNemesisRevealEffect(this); } - + @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player == null) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { return false; } - - if (player.getLibrary().hasCards()) { - Card card = player.getLibrary().getFromTop(game); + + if (controller.getLibrary().hasCards()) { + Card card = controller.getLibrary().getFromTop(game); if (card != null) { Cards cards = new CardsImpl(); cards.add(card); - player.revealCards("Sorin, Grim Nemesis", cards, game); - - if (card.moveToZone(Zone.HAND, source, game, false)) { + controller.revealCards("Sorin, Grim Nemesis", cards, game); + + if (controller.moveCards(card, Zone.HAND, source, game)) { for (UUID playerId : game.getOpponents(source.getControllerId())) { - if (card.getConvertedManaCost() > 0) { + if (card.getManaValue() > 0) { Player opponent = game.getPlayer(playerId); if (opponent != null) { - opponent.loseLife(card.getConvertedManaCost(), game, source, false); + opponent.loseLife(card.getManaValue(), game, source, false); } } } @@ -100,18 +100,17 @@ public boolean apply(Game game, Ability source) { } } - class SorinTokenEffect extends OneShotEffect { - + SorinTokenEffect() { super(Outcome.GainLife); staticText = "Create a number of 1/1 black Vampire Knight creature tokens with lifelink equal to the highest life total among all players"; } - + private SorinTokenEffect(final SorinTokenEffect effect) { super(effect); } - + @Override public boolean apply(Game game, Ability source) { int maxLife = 0; @@ -127,7 +126,7 @@ public boolean apply(Game game, Ability source) { new CreateTokenEffect(new VampireKnightToken(), maxLife).apply(game, source); return true; } - + @Override public SorinTokenEffect copy() { return new SorinTokenEffect(this); diff --git a/Mage.Sets/src/mage/cards/s/SorinVengefulBloodlord.java b/Mage.Sets/src/mage/cards/s/SorinVengefulBloodlord.java index 526d6c4f767d..a19cba0367f3 100644 --- a/Mage.Sets/src/mage/cards/s/SorinVengefulBloodlord.java +++ b/Mage.Sets/src/mage/cards/s/SorinVengefulBloodlord.java @@ -20,7 +20,7 @@ import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCardInYourGraveyard; @@ -57,7 +57,7 @@ public SorinVengefulBloodlord(UUID ownerId, CardSetInfo setInfo) { // -X: Return target creature card with converted mana cost X from your graveyard to the battlefield. That creature is a vampire in addition to its other types. ability = new LoyaltyAbility(new ReturnFromGraveyardToBattlefieldTargetEffect().setText( - "Return target creature card with converted mana cost X from your graveyard to the battlefield" + "Return target creature card with mana value X from your graveyard to the battlefield" )); ability.addEffect(new SorinVengefulBloodlordEffect()); ability.setTargetAdjuster(SorinVengefulBloodlordAdjuster.instance); @@ -85,8 +85,8 @@ public void adjustTargets(Ability ability, Game game) { xValue = ((PayVariableLoyaltyCost) cost).getAmount(); } } - FilterCard filter = new FilterCreatureCard("creature card with converted mana cost " + xValue + " or less"); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, xValue + 1)); + FilterCard filter = new FilterCreatureCard("creature card with mana value " + xValue + " or less"); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); ability.getTargets().clear(); ability.addTarget(new TargetCardInYourGraveyard(filter)); } diff --git a/Mage.Sets/src/mage/cards/s/SosukeSonOfSeshiro.java b/Mage.Sets/src/mage/cards/s/SosukeSonOfSeshiro.java index 33a0e10daebb..25dd0700ee63 100644 --- a/Mage.Sets/src/mage/cards/s/SosukeSonOfSeshiro.java +++ b/Mage.Sets/src/mage/cards/s/SosukeSonOfSeshiro.java @@ -14,16 +14,14 @@ import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; -import mage.game.events.DamagedCreatureEvent; +import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; import java.util.UUID; /** - * * @author LevelX */ public final class SosukeSonOfSeshiro extends CardImpl { @@ -31,11 +29,11 @@ public final class SosukeSonOfSeshiro extends CardImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Snake creatures"); static { - filter.add(SubType.SNAKE.getPredicate()); + filter.add(SubType.SNAKE.getPredicate()); } public SosukeSonOfSeshiro(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SNAKE); this.subtype.add(SubType.WARRIOR); @@ -79,21 +77,25 @@ public SosukeSonOfSeshiroTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (((DamagedCreatureEvent) event).isCombatDamage()) { - Permanent sourceCreature = game.getPermanent(event.getSourceId()); - Permanent targetCreature = game.getPermanent(event.getTargetId()); - if (sourceCreature != null && sourceCreature.isControlledBy(this.getControllerId()) - && targetCreature != null && sourceCreature.hasSubtype(SubType.WARRIOR, game)) { - this.getEffects().get(0).setTargetPointer(new FixedTarget(targetCreature.getId())); - return true; - } + if (!((DamagedEvent) event).isCombatDamage()) { + return false; } - return false; + Permanent sourceCreature = game.getPermanent(event.getSourceId()); + Permanent targetCreature = game.getPermanent(event.getTargetId()); + if (sourceCreature == null + || !sourceCreature.isControlledBy(this.getControllerId()) + || targetCreature == null + || !targetCreature.isCreature() + || !sourceCreature.hasSubtype(SubType.WARRIOR, game)) { + return false; + } + this.getEffects().setTargetPointer(new FixedTarget(targetCreature, game)); + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/s/SoulBarrier.java b/Mage.Sets/src/mage/cards/s/SoulBarrier.java index ae28138ad877..ce35957adfa2 100644 --- a/Mage.Sets/src/mage/cards/s/SoulBarrier.java +++ b/Mage.Sets/src/mage/cards/s/SoulBarrier.java @@ -64,8 +64,7 @@ public boolean apply(Game game, Ability source) { if (player != null && permanent != null) { Cost cost = ManaUtil.createManaCost(2, false); - String message = "Would you like to pay {2} to prevent taking 2 damage from " + permanent.getLogName() + "?"; - if (!(player.chooseUse(Outcome.Benefit, message, source, game) + if (!(player.chooseUse(Outcome.Benefit, "Pay {2}?", source, game) && cost.pay(source, game, source, player.getId(), false, null))) { player.damage(2, source.getSourceId(), source, game); } diff --git a/Mage.Sets/src/mage/cards/s/SoulConduit.java b/Mage.Sets/src/mage/cards/s/SoulConduit.java index 06c16bc431c2..606d83caae38 100644 --- a/Mage.Sets/src/mage/cards/s/SoulConduit.java +++ b/Mage.Sets/src/mage/cards/s/SoulConduit.java @@ -1,23 +1,18 @@ - package mage.cards.s; -import java.util.UUID; 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.effects.common.ExchangeLifeTwoTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.players.Player; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author North */ public final class SoulConduit extends CardImpl { @@ -26,7 +21,7 @@ public SoulConduit(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{6}"); // {6}, {tap}: Two target players exchange life totals. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new SoulConduitEffect(), new GenericManaCost(6)); + Ability ability = new SimpleActivatedAbility(new ExchangeLifeTwoTargetEffect(), new GenericManaCost(6)); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetPlayer(2)); this.addAbility(ability); @@ -41,52 +36,3 @@ public SoulConduit copy() { return new SoulConduit(this); } } - -class SoulConduitEffect extends OneShotEffect { - - public SoulConduitEffect() { - super(Outcome.Neutral); - this.staticText = "Two target players exchange life totals"; - } - - public SoulConduitEffect(final SoulConduitEffect effect) { - super(effect); - } - - @Override - public SoulConduitEffect copy() { - return new SoulConduitEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player1 = game.getPlayer(source.getTargets().get(0).getTargets().get(0)); - Player player2 = game.getPlayer(source.getTargets().get(0).getTargets().get(1)); - if (player1 != null && player2 != null) { - int lifePlayer1 = player1.getLife(); - int lifePlayer2 = player2.getLife(); - - if (lifePlayer1 == lifePlayer2) { - return false; - } - - if (!player1.isLifeTotalCanChange() || !player2.isLifeTotalCanChange()) { - return false; - } - - // 20110930 - 118.7, 118.8 - if (lifePlayer1 < lifePlayer2 && (!player1.isCanGainLife() || !player2.isCanLoseLife())) { - return false; - } - - if (lifePlayer1 > lifePlayer2 && (!player1.isCanLoseLife() || !player2.isCanGainLife())) { - return false; - } - - player1.setLife(lifePlayer2, game, source); - player2.setLife(lifePlayer1, game, source); - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/s/SoulEcho.java b/Mage.Sets/src/mage/cards/s/SoulEcho.java index ef525f541ff1..eb227a537d30 100644 --- a/Mage.Sets/src/mage/cards/s/SoulEcho.java +++ b/Mage.Sets/src/mage/cards/s/SoulEcho.java @@ -81,7 +81,7 @@ public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Player opponent = game.getPlayer(targetPointer.getFirst(game, source)); if (controller != null && opponent != null && permanent != null) { - if (opponent.chooseUse(outcome, "Would you like to have all damage dealt to " + controller.getLogName() + " be decremented from echo counters on " + permanent.getLogName() + " until " + controller.getLogName() + "'s next upkeep instead?", source, game)) { + if (opponent.chooseUse(outcome, "Have all damage dealt to " + controller.getLogName() + " be decremented from echo counters on " + permanent.getLogName() + " until " + controller.getLogName() + "'s next upkeep instead?", source, game)) { game.informPlayers("Until " + controller.getLogName() + "'s next upkeep, for each 1 damage that would be dealt to " + controller.getLogName() + ", an echo counter from " + permanent.getLogName() + " is removed instead"); game.addEffect(new SoulEchoReplacementEffect(), source); } diff --git a/Mage.Sets/src/mage/cards/s/SoulFoundry.java b/Mage.Sets/src/mage/cards/s/SoulFoundry.java index b6b3287821cf..0b897eb5fb05 100644 --- a/Mage.Sets/src/mage/cards/s/SoulFoundry.java +++ b/Mage.Sets/src/mage/cards/s/SoulFoundry.java @@ -1,10 +1,9 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.CostAdjuster; import mage.abilities.costs.VariableCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; @@ -26,8 +25,9 @@ import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class SoulFoundry extends CardImpl { @@ -36,46 +36,50 @@ public SoulFoundry(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); // Imprint - When Soul Foundry enters the battlefield, you may exile a creature card from your hand. - this.addAbility(new EntersBattlefieldTriggeredAbility(new SoulFoundryImprintEffect(), true, "Imprint — ")); + this.addAbility(new EntersBattlefieldTriggeredAbility( + new SoulFoundryImprintEffect(), true, "Imprint — " + )); // {X}, {T}: Create a token that's a copy of the exiled card. X is the converted mana cost of that card. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new SoulFoundryEffect(), new ManaCostsImpl("{X}")); + Ability ability = new SimpleActivatedAbility(new SoulFoundryEffect(), new ManaCostsImpl("{X}")); ability.addCost(new TapSourceCost()); + ability.setCostAdjuster(SoulFoundryAdjuster.instance); this.addAbility(ability); - } private SoulFoundry(final SoulFoundry card) { super(card); } + @Override + public SoulFoundry copy() { + return new SoulFoundry(this); + } +} + +enum SoulFoundryAdjuster implements CostAdjuster { + instance; + @Override public void adjustCosts(Ability ability, Game game) { - if (ability instanceof SimpleActivatedAbility) { - Permanent sourcePermanent = game.getPermanent(ability.getSourceId()); - if (sourcePermanent != null) { - if (!sourcePermanent.getImprinted().isEmpty()) { - Card imprinted = game.getCard(sourcePermanent.getImprinted().get(0)); - if (imprinted != null) { - ability.getManaCostsToPay().clear(); - ability.getManaCostsToPay().add(0, new GenericManaCost(imprinted.getConvertedManaCost())); - } + Permanent sourcePermanent = game.getPermanent(ability.getSourceId()); + if (sourcePermanent != null) { + if (!sourcePermanent.getImprinted().isEmpty()) { + Card imprinted = game.getCard(sourcePermanent.getImprinted().get(0)); + if (imprinted != null) { + ability.getManaCostsToPay().clear(); + ability.getManaCostsToPay().add(0, new GenericManaCost(imprinted.getManaValue())); } } + } - // no {X} anymore as we already have imprinted the card with defined manacost - for (ManaCost cost : ability.getManaCostsToPay()) { - if (cost instanceof VariableCost) { - cost.setPaid(); - } + // no {X} anymore as we already have imprinted the card with defined manacost + for (ManaCost cost : ability.getManaCostsToPay()) { + if (cost instanceof VariableCost) { + cost.setPaid(); } } } - - @Override - public SoulFoundry copy() { - return new SoulFoundry(this); - } } class SoulFoundryImprintEffect extends OneShotEffect { @@ -86,12 +90,12 @@ class SoulFoundryImprintEffect extends OneShotEffect { filter.add(CardType.CREATURE.getPredicate()); } - public SoulFoundryImprintEffect() { + SoulFoundryImprintEffect() { super(Outcome.Neutral); staticText = "you may exile a creature card from your hand"; } - public SoulFoundryImprintEffect(SoulFoundryImprintEffect effect) { + private SoulFoundryImprintEffect(SoulFoundryImprintEffect effect) { super(effect); } @@ -128,12 +132,12 @@ public SoulFoundryImprintEffect copy() { class SoulFoundryEffect extends OneShotEffect { - public SoulFoundryEffect() { + SoulFoundryEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "Create a token that's a copy of the exiled card. X is the converted mana cost of that card"; + this.staticText = "Create a token that's a copy of the exiled card. X is the mana value of that card"; } - public SoulFoundryEffect(final SoulFoundryEffect effect) { + private SoulFoundryEffect(final SoulFoundryEffect effect) { super(effect); } diff --git a/Mage.Sets/src/mage/cards/s/SoulOfTheHarvest.java b/Mage.Sets/src/mage/cards/s/SoulOfTheHarvest.java index 54857abbb676..fbdbd033dd04 100644 --- a/Mage.Sets/src/mage/cards/s/SoulOfTheHarvest.java +++ b/Mage.Sets/src/mage/cards/s/SoulOfTheHarvest.java @@ -14,7 +14,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; diff --git a/Mage.Sets/src/mage/cards/s/SoulScarMage.java b/Mage.Sets/src/mage/cards/s/SoulScarMage.java index 80a150347f24..cca5bb549eaf 100644 --- a/Mage.Sets/src/mage/cards/s/SoulScarMage.java +++ b/Mage.Sets/src/mage/cards/s/SoulScarMage.java @@ -1,7 +1,6 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; @@ -11,20 +10,17 @@ import mage.abilities.keyword.ProwessAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.CounterType; import mage.game.Game; -import mage.game.events.DamageCreatureEvent; +import mage.game.events.DamageEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author stravant */ public final class SoulScarMage extends CardImpl { @@ -84,19 +80,15 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT; } @Override public boolean applies(GameEvent event, Ability source, Game game) { - UUID sourceControllerId = game.getControllerId(event.getSourceId()); - UUID targetControllerId = game.getControllerId(event.getTargetId()); - UUID controllerId = source.getControllerId(); - boolean weControlSource = controllerId.equals(sourceControllerId); - boolean opponentControlsTarget = game.getOpponents(sourceControllerId).contains(targetControllerId); - boolean isNoncombatDamage = !((DamageCreatureEvent) event).isCombatDamage(); - return weControlSource - && isNoncombatDamage - && opponentControlsTarget; + Permanent permanent = game.getPermanent(event.getTargetId()); + return permanent != null + && permanent.isCreature() + && !((DamageEvent) event).isCombatDamage() + && game.getOpponents(permanent.getControllerId()).contains(source.getControllerId()); } } diff --git a/Mage.Sets/src/mage/cards/s/SoulShatter.java b/Mage.Sets/src/mage/cards/s/SoulShatter.java index aa88352af949..60ec94f3a53f 100644 --- a/Mage.Sets/src/mage/cards/s/SoulShatter.java +++ b/Mage.Sets/src/mage/cards/s/SoulShatter.java @@ -22,7 +22,7 @@ public final class SoulShatter extends CardImpl { private static final FilterPermanent filter = new FilterCreatureOrPlaneswalkerPermanent( - "creature or planeswalker with the highest converted mana cost " + + "creature or planeswalker with the highest mana value " + "among creatures and planeswalkers they control" ); @@ -62,9 +62,9 @@ public boolean apply(ObjectPlayer input, Game game) { .getActivePermanents(filter, input.getPlayerId(), game) .stream() .filter(Objects::nonNull) - .mapToInt(MageObject::getConvertedManaCost) + .mapToInt(MageObject::getManaValue) .max() .orElse(0); - return input.getObject().getConvertedManaCost() >= cmc; + return input.getObject().getManaValue() >= cmc; } } diff --git a/Mage.Sets/src/mage/cards/s/SoulTithe.java b/Mage.Sets/src/mage/cards/s/SoulTithe.java index 251f520e0143..33286023435f 100644 --- a/Mage.Sets/src/mage/cards/s/SoulTithe.java +++ b/Mage.Sets/src/mage/cards/s/SoulTithe.java @@ -26,7 +26,7 @@ */ public final class SoulTithe extends CardImpl { - static final String rule = "At the beginning of the upkeep of enchanted permanent's controller, that player sacrifices it unless they pay {X}, where X is its converted mana cost"; + static final String rule = "At the beginning of the upkeep of enchanted permanent's controller, that player sacrifices it unless they pay {X}, where X is its mana value"; public SoulTithe(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); @@ -59,7 +59,7 @@ class SoulTitheEffect extends OneShotEffect { public SoulTitheEffect() { super(Outcome.Sacrifice); - staticText = "that player sacrifices it unless they pay {X}, where X is its converted mana cost"; + staticText = "that player sacrifices it unless they pay {X}, where X is its mana value"; } public SoulTitheEffect(final SoulTitheEffect effect) { @@ -74,7 +74,7 @@ public boolean apply(Game game, Ability source) { if (permanent != null) { Player player = game.getPlayer(permanent.getControllerId()); if (player != null) { - Cost cost = ManaUtil.createManaCost(permanent.getConvertedManaCost(), true); + Cost cost = ManaUtil.createManaCost(permanent.getManaValue(), true); if (player.chooseUse(Outcome.Benefit, "Pay " + cost.getText() + " for " + permanent.getName() + "? (otherwise you sacrifice it)", source, game)) { if (cost.pay(source, game, source, player.getId(), false, null)) { return true; diff --git a/Mage.Sets/src/mage/cards/s/SoulWarden.java b/Mage.Sets/src/mage/cards/s/SoulWarden.java index c70e7080787b..a966eac90e1e 100644 --- a/Mage.Sets/src/mage/cards/s/SoulWarden.java +++ b/Mage.Sets/src/mage/cards/s/SoulWarden.java @@ -11,7 +11,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/s/SoulfireEruption.java b/Mage.Sets/src/mage/cards/s/SoulfireEruption.java index e23cf249d2fb..9b04b7aee81d 100644 --- a/Mage.Sets/src/mage/cards/s/SoulfireEruption.java +++ b/Mage.Sets/src/mage/cards/s/SoulfireEruption.java @@ -47,7 +47,7 @@ class SoulfireEruptionEffect extends OneShotEffect { super(Outcome.Benefit); staticText = "choose any number of target creatures, planeswalkers, and/or players. " + "For each of them, exile the top card of your library, " + - "then {this} deals damage equal to that card's converted mana cost to that permanent or player. " + + "then {this} deals damage equal to that card's mana value to that permanent or player. " + "You may play the exiled cards until the end of your next turn"; } @@ -83,14 +83,14 @@ public boolean apply(Game game, Ability source) { } controller.moveCards(card, Zone.EXILED, source, game); game.addEffect(new SoulfireEruptionCastEffect().setTargetPointer(new FixedTarget(card, game)), source); - if (card.getConvertedManaCost() < 1) { + if (card.getManaValue() < 1) { continue; } if (permanent != null) { - permanent.damage(card.getConvertedManaCost(), source.getSourceId(), source, game); + permanent.damage(card.getManaValue(), source.getSourceId(), source, game); } if (player != null) { - player.damage(card.getConvertedManaCost(), source.getSourceId(), source, game); + player.damage(card.getManaValue(), source.getSourceId(), source, game); } } return true; diff --git a/Mage.Sets/src/mage/cards/s/Soulherder.java b/Mage.Sets/src/mage/cards/s/Soulherder.java index aabf3f94181c..8bf19ea2a0c2 100644 --- a/Mage.Sets/src/mage/cards/s/Soulherder.java +++ b/Mage.Sets/src/mage/cards/s/Soulherder.java @@ -15,7 +15,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; diff --git a/Mage.Sets/src/mage/cards/s/Soulquake.java b/Mage.Sets/src/mage/cards/s/Soulquake.java index 808b7b991f48..b60cc69a7067 100644 --- a/Mage.Sets/src/mage/cards/s/Soulquake.java +++ b/Mage.Sets/src/mage/cards/s/Soulquake.java @@ -1,6 +1,7 @@ - package mage.cards.s; +import java.util.LinkedHashSet; +import java.util.Set; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; @@ -23,7 +24,7 @@ public final class Soulquake extends CardImpl { public Soulquake(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{U}{U}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}{U}{B}{B}"); // Return all creatures on the battlefield and all creature cards in graveyards to their owners' hands. this.getSpellAbility().addEffect(new SoulquakeEffect()); @@ -59,17 +60,22 @@ public SoulquakeEffect copy() { @Override public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Set cardsToHand = new LinkedHashSet<>(); for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), source.getSourceId(), game)) { - permanent.moveToZone(Zone.HAND, source, game, true); + cardsToHand.add((Card) permanent); } for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { Player player = game.getPlayer(playerId); if (player != null) { for (Card card : player.getGraveyard().getCards(filter2, game)) { - card.moveToZone(Zone.HAND, source, game, true); + cardsToHand.add(card); } } } - return true; + return controller.moveCards(cardsToHand, Zone.HAND, source, game); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/s/SoulsAttendant.java b/Mage.Sets/src/mage/cards/s/SoulsAttendant.java index 1e95da950628..efa1d0fccc9d 100644 --- a/Mage.Sets/src/mage/cards/s/SoulsAttendant.java +++ b/Mage.Sets/src/mage/cards/s/SoulsAttendant.java @@ -11,7 +11,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/s/SoulsOfTheFaultless.java b/Mage.Sets/src/mage/cards/s/SoulsOfTheFaultless.java index 7bf09e331010..f646bb6204d2 100644 --- a/Mage.Sets/src/mage/cards/s/SoulsOfTheFaultless.java +++ b/Mage.Sets/src/mage/cards/s/SoulsOfTheFaultless.java @@ -15,9 +15,8 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamagedCreatureEvent; +import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; @@ -67,13 +66,13 @@ public SoulsOfTheFaultlessTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getTargetId().equals(this.sourceId) - && ((DamagedCreatureEvent) event).isCombatDamage()) { + && ((DamagedEvent) event).isCombatDamage()) { Permanent source = game.getPermanent(event.getSourceId()); if (source == null) { source = (Permanent) game.getLastKnownInformation(event.getSourceId(), Zone.BATTLEFIELD); diff --git a/Mage.Sets/src/mage/cards/s/SoultetherGolem.java b/Mage.Sets/src/mage/cards/s/SoultetherGolem.java index 199ac02d3bef..450ac7f58287 100644 --- a/Mage.Sets/src/mage/cards/s/SoultetherGolem.java +++ b/Mage.Sets/src/mage/cards/s/SoultetherGolem.java @@ -17,7 +17,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/s/SovereignsOfLostAlara.java b/Mage.Sets/src/mage/cards/s/SovereignsOfLostAlara.java index 9b02d9d6269d..707e1e114c19 100644 --- a/Mage.Sets/src/mage/cards/s/SovereignsOfLostAlara.java +++ b/Mage.Sets/src/mage/cards/s/SovereignsOfLostAlara.java @@ -15,10 +15,9 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.other.AuraCardCanAttachToPermanentId; +import mage.filter.predicate.card.AuraCardCanAttachToPermanentId; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInLibrary; @@ -95,7 +94,7 @@ class SovereignsOfLostAlaraEffect extends OneShotEffect { public SovereignsOfLostAlaraEffect() { super(Outcome.BoostCreature); - staticText = "you may search your library for an Aura card that could enchant that creature, put it onto the battlefield attached to that creature, then shuffle your library"; + staticText = "you may search your library for an Aura card that could enchant that creature, put it onto the battlefield attached to that creature, then shuffle"; } public SovereignsOfLostAlaraEffect(final SovereignsOfLostAlaraEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SparkDouble.java b/Mage.Sets/src/mage/cards/s/SparkDouble.java index 985f8b13efd0..be54d9620395 100644 --- a/Mage.Sets/src/mage/cards/s/SparkDouble.java +++ b/Mage.Sets/src/mage/cards/s/SparkDouble.java @@ -26,7 +26,7 @@ */ public final class SparkDouble extends CardImpl { - private static FilterPermanent filter = new FilterControlledPermanent("a creature or planeswalker you control"); + private static final FilterPermanent filter = new FilterControlledPermanent("a creature or planeswalker you control"); static { filter.add(Predicates.or( @@ -40,13 +40,8 @@ public SparkDouble(UUID ownerId, CardSetInfo setInfo) { this.power = new MageInt(0); this.toughness = new MageInt(0); - // You may have Spark Double enter the battlefield as a copy of a creature or planeswalker you control, - // except it enters with an additional +1/+1 counter on it if it’s a creature, - // it enters with an additional loyalty counter on it if it’s a planeswalker, and it isn’t legendary if that permanent is legendary. - Effect effect = new CopyPermanentEffect(filter, new SparkDoubleExceptEffectsCopyApplier()); - effect.setText("as a copy of a creature or planeswalker you control, " - + "except it enters with an additional +1/+1 counter on it if it's a creature, " - + "it enters with an additional loyalty counter on it if it's a planeswalker, and it isn't legendary if that permanent is legendary."); + // You may have Spark Double enter the battlefield as a copy of a creature or planeswalker you control, except it enters with an additional +1/+1 counter on it if it’s a creature, it enters with an additional loyalty counter on it if it’s a planeswalker, and it isn’t legendary if that permanent is legendary. + Effect effect = new CopyPermanentEffect(filter, new SparkDoubleCopyApplier()); EntersBattlefieldAbility ability = new EntersBattlefieldAbility(effect, true); this.addAbility(ability); } @@ -61,7 +56,14 @@ public SparkDouble copy() { } } -class SparkDoubleExceptEffectsCopyApplier extends CopyApplier { +class SparkDoubleCopyApplier extends CopyApplier { + + @Override + public String getText() { + return ", except it enters with an additional +1/+1 counter on it if it's a creature, it enters with " + + "an additional loyalty counter on it if it's a planeswalker, and it isn't legendary if " + + "that permanent is legendary"; + } @Override public boolean apply(Game game, MageObject blueprint, Ability source, UUID copyToObjectId) { @@ -88,12 +90,12 @@ public boolean apply(Game game, MageObject blueprint, Ability source, UUID copyT // Spark Double enters as a planeswalker creature and gets both kinds of counters. // counters only for original card, not copies - if (!isCopyOfCopy(source, copyToObjectId)) { + if (!isCopyOfCopy(source, blueprint, copyToObjectId)) { // enters with an additional +1/+1 counter on it if it’s a creature if (blueprint.isCreature()) { blueprint.getAbilities().add(new EntersBattlefieldAbility( new AddCountersSourceEffect(CounterType.P1P1.createInstance(), false) - .setText("with an additional +1/+1 counter on it") + .setText("with an additional +1/+1 counter on it") )); } @@ -101,7 +103,7 @@ public boolean apply(Game game, MageObject blueprint, Ability source, UUID copyT if (blueprint.isPlaneswalker()) { blueprint.getAbilities().add(new EntersBattlefieldAbility( new AddCountersSourceEffect(CounterType.LOYALTY.createInstance(), false) - .setText("with an additional loyalty counter on it") + .setText("with an additional loyalty counter on it") )); } } diff --git a/Mage.Sets/src/mage/cards/s/SparkOfCreativity.java b/Mage.Sets/src/mage/cards/s/SparkOfCreativity.java index 1f5d0f000a8e..17f83d65c2a3 100644 --- a/Mage.Sets/src/mage/cards/s/SparkOfCreativity.java +++ b/Mage.Sets/src/mage/cards/s/SparkOfCreativity.java @@ -48,7 +48,7 @@ class SparkOfCreativityEffect extends OneShotEffect { public SparkOfCreativityEffect() { super(Outcome.Benefit); - this.staticText = "Choose target creature. Exile the top card of your library. You may have Spark of Creativity deal damage to that creature equal to the converted mana cost of the exiled card. If you don't, you may play that card until end of turn"; + this.staticText = "Choose target creature. Exile the top card of your library. You may have {this} deal damage to that creature equal to the exiled card's mana value. If you don't, you may play that card until end of turn"; } public SparkOfCreativityEffect(final SparkOfCreativityEffect effect) { @@ -71,7 +71,7 @@ public boolean apply(Game game, Ability source) { // You may have Spark of Creativity deal damage to that creature equal to the converted mana cost of the exiled card. Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); if (targetCreature != null) { - int cmc = card.getManaCost().convertedManaCost(); + int cmc = card.getManaCost().manaValue(); if (controller.chooseUse(outcome, "Let " + sourceObject.getLogName() + " deal " + cmc + " damage to " + targetCreature.getLogName() + '?', source, game)) { targetCreature.damage(cmc, source.getSourceId(), source, game, false, true); return true; diff --git a/Mage.Sets/src/mage/cards/s/SparringRegimen.java b/Mage.Sets/src/mage/cards/s/SparringRegimen.java new file mode 100644 index 000000000000..ee78fc105966 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SparringRegimen.java @@ -0,0 +1,45 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.LearnEffect; +import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.target.common.TargetAttackingCreature; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SparringRegimen extends CardImpl { + + public SparringRegimen(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); + + // When Sparring Regimen enters the battlefield, learn. + this.addAbility(new EntersBattlefieldTriggeredAbility(new LearnEffect())); + + // Whenever you attack, put a +1/+1 counter on target attacking creature and untap it. + Ability ability = new AttacksWithCreaturesTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), 0 + ); + ability.addEffect(new UntapTargetEffect().setText("and untap it")); + ability.addTarget(new TargetAttackingCreature()); + this.addAbility(ability); + } + + private SparringRegimen(final SparringRegimen card) { + super(card); + } + + @Override + public SparringRegimen copy() { + return new SparringRegimen(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpatulaOfTheAges.java b/Mage.Sets/src/mage/cards/s/SpatulaOfTheAges.java index c416501a5636..797f9d09579d 100644 --- a/Mage.Sets/src/mage/cards/s/SpatulaOfTheAges.java +++ b/Mage.Sets/src/mage/cards/s/SpatulaOfTheAges.java @@ -16,7 +16,7 @@ import mage.filter.common.FilterPermanentCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.NamePredicate; -import mage.filter.predicate.other.ExpansionSetPredicate; +import mage.filter.predicate.card.ExpansionSetPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/s/SpawningKraken.java b/Mage.Sets/src/mage/cards/s/SpawningKraken.java new file mode 100644 index 000000000000..cbff3c5ed377 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpawningKraken.java @@ -0,0 +1,57 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SetTargetPointer; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.game.permanent.token.KioraKrakenToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpawningKraken extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledPermanent("a Kraken, Leviathan, Octopus, or Serpent you control"); + + static { + filter.add(Predicates.or( + SubType.KRAKEN.getPredicate(), + SubType.LEVIATHAN.getPredicate(), + SubType.OCTOPUS.getPredicate(), + SubType.SERPENT.getPredicate() + )); + } + + public SpawningKraken(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}"); + + this.subtype.add(SubType.KRAKEN); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Whenever a Kraken, Leviathan, Octopus, or Serpent you control deals combat damage to a player, create a 9/9 blue Kraken creature token. + this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility( + new CreateTokenEffect(new KioraKrakenToken()), filter, + false, SetTargetPointer.NONE, true + )); + } + + private SpawningKraken(final SpawningKraken card) { + super(card); + } + + @Override + public SpawningKraken copy() { + return new SpawningKraken(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpeakerOfTheHeavens.java b/Mage.Sets/src/mage/cards/s/SpeakerOfTheHeavens.java index 94f369ee2c6e..fc43ccaae9dc 100644 --- a/Mage.Sets/src/mage/cards/s/SpeakerOfTheHeavens.java +++ b/Mage.Sets/src/mage/cards/s/SpeakerOfTheHeavens.java @@ -74,6 +74,6 @@ public boolean apply(Game game, Ability source) { @Override public String toString() { - return "you have at least 7 life more than your starting life total and only any time you could cast a sorcery"; + return "you have at least 7 life more than your starting life total and only as a sorcery"; } } diff --git a/Mage.Sets/src/mage/cards/s/SpearOfHeliod.java b/Mage.Sets/src/mage/cards/s/SpearOfHeliod.java index 96e755b5c535..16dc4a435e18 100644 --- a/Mage.Sets/src/mage/cards/s/SpearOfHeliod.java +++ b/Mage.Sets/src/mage/cards/s/SpearOfHeliod.java @@ -15,7 +15,7 @@ import mage.constants.SuperType; import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.DamagedPlayerThisTurnPredicate; +import mage.filter.predicate.other.DamagedPlayerThisTurnPredicate; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/s/SpectacleMage.java b/Mage.Sets/src/mage/cards/s/SpectacleMage.java new file mode 100644 index 000000000000..debd1d8371d4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpectacleMage.java @@ -0,0 +1,53 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; +import mage.abilities.keyword.FlyingAbility; +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.FilterInstantOrSorceryCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpectacleMage extends CardImpl { + + private static final FilterCard filter = new FilterInstantOrSorceryCard(); + + static { + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 4)); + } + + public SpectacleMage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{R}"); + + this.subtype.add(SubType.BIRD); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Instant and sorcery spells you cast with mana value 5 or greater cost {1} less to cast. + this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 1) + .setText("instant and sorcery spells you cast with mana value 5 or greater cost {1} less to cast"))); + } + + private SpectacleMage(final SpectacleMage card) { + super(card); + } + + @Override + public SpectacleMage copy() { + return new SpectacleMage(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpecterOfTheFens.java b/Mage.Sets/src/mage/cards/s/SpecterOfTheFens.java new file mode 100644 index 000000000000..691b56ee63d2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpecterOfTheFens.java @@ -0,0 +1,48 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +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.keyword.FlyingAbility; +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 SpecterOfTheFens extends CardImpl { + + public SpecterOfTheFens(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.SPECTER); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // {5}{B}: Target opponent loses 2 life and you gain 2 life. + Ability ability = new SimpleActivatedAbility(new LoseLifeTargetEffect(2), new ManaCostsImpl("{5}{B}")); + ability.addEffect(new GainLifeEffect(2).concatBy("and")); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + } + + private SpecterOfTheFens(final SpecterOfTheFens card) { + super(card); + } + + @Override + public SpecterOfTheFens copy() { + return new SpecterOfTheFens(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpectralDeluge.java b/Mage.Sets/src/mage/cards/s/SpectralDeluge.java index 4265c2a0bbd1..be30c1e1822f 100644 --- a/Mage.Sets/src/mage/cards/s/SpectralDeluge.java +++ b/Mage.Sets/src/mage/cards/s/SpectralDeluge.java @@ -8,6 +8,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterOpponentsCreaturePermanent; import mage.filter.predicate.ObjectSourcePlayer; import mage.filter.predicate.ObjectSourcePlayerPredicate; @@ -51,7 +52,7 @@ public SpectralDeluge copy() { enum SpectralDelugePredicate implements ObjectSourcePlayerPredicate> { instance; - private static final FilterPermanent filter = new FilterPermanent(SubType.ISLAND, ""); + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.ISLAND); @Override public boolean apply(ObjectSourcePlayer input, Game game) { diff --git a/Mage.Sets/src/mage/cards/s/SpectralSteel.java b/Mage.Sets/src/mage/cards/s/SpectralSteel.java index 10290953bf84..a9dd8a75bfc7 100644 --- a/Mage.Sets/src/mage/cards/s/SpectralSteel.java +++ b/Mage.Sets/src/mage/cards/s/SpectralSteel.java @@ -17,7 +17,7 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/s/SpellBlast.java b/Mage.Sets/src/mage/cards/s/SpellBlast.java index 0d2503d792ca..692ae53a1edc 100644 --- a/Mage.Sets/src/mage/cards/s/SpellBlast.java +++ b/Mage.Sets/src/mage/cards/s/SpellBlast.java @@ -8,7 +8,7 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.target.TargetSpell; import mage.target.targetadjustment.TargetAdjuster; @@ -24,7 +24,7 @@ public SpellBlast(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{U}"); // Counter target spell with converted mana cost X. - this.getSpellAbility().addEffect(new CounterTargetEffect().setText("counter target spell with converted mana cost X")); + this.getSpellAbility().addEffect(new CounterTargetEffect().setText("counter target spell with mana value X")); this.getSpellAbility().setTargetAdjuster(SpellBlastAdjuster.instance); } @@ -45,8 +45,8 @@ enum SpellBlastAdjuster implements TargetAdjuster { public void adjustTargets(Ability ability, Game game) { int xValue = ability.getManaCostsToPay().getX(); ability.getTargets().clear(); - FilterSpell filter = new FilterSpell("spell with converted mana cost " + xValue); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, xValue)); + FilterSpell filter = new FilterSpell("spell with mana value " + xValue); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, xValue)); ability.addTarget(new TargetSpell(filter)); } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/SpellBurst.java b/Mage.Sets/src/mage/cards/s/SpellBurst.java index 389e23b7c9d0..f6599e6f6d6e 100644 --- a/Mage.Sets/src/mage/cards/s/SpellBurst.java +++ b/Mage.Sets/src/mage/cards/s/SpellBurst.java @@ -9,7 +9,7 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.target.TargetSpell; import mage.target.targetadjustment.TargetAdjuster; @@ -28,7 +28,7 @@ public SpellBurst(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new BuybackAbility("{3}")); // Counter target spell with converted mana cost X. - this.getSpellAbility().addEffect(new CounterTargetEffect().setText("counter target spell with converted mana cost X")); + this.getSpellAbility().addEffect(new CounterTargetEffect().setText("counter target spell with mana value X")); this.getSpellAbility().setTargetAdjuster(SpellBurstAdjuster.instance); } @@ -49,8 +49,8 @@ enum SpellBurstAdjuster implements TargetAdjuster { public void adjustTargets(Ability ability, Game game) { int xValue = ability.getManaCostsToPay().getX(); ability.getTargets().clear(); - FilterSpell filter = new FilterSpell("spell with converted mana cost " + xValue); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, xValue)); + FilterSpell filter = new FilterSpell("spell with mana value " + xValue); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, xValue)); ability.addTarget(new TargetSpell(filter)); } } diff --git a/Mage.Sets/src/mage/cards/s/SpellQueller.java b/Mage.Sets/src/mage/cards/s/SpellQueller.java index ff481aa58861..70a2a9e74a5f 100644 --- a/Mage.Sets/src/mage/cards/s/SpellQueller.java +++ b/Mage.Sets/src/mage/cards/s/SpellQueller.java @@ -21,7 +21,7 @@ import mage.constants.Outcome; import mage.constants.SubType; import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.ExileZone; import mage.game.Game; import mage.game.permanent.Permanent; @@ -37,10 +37,10 @@ */ public final class SpellQueller extends CardImpl { - private static final FilterSpell filter = new FilterSpell("spell with converted mana cost 4 or less"); + private static final FilterSpell filter = new FilterSpell("spell with mana value 4 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 5)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 5)); } public SpellQueller(UUID ownerId, CardSetInfo setInfo) { @@ -77,7 +77,7 @@ class SpellQuellerEntersEffect extends OneShotEffect { public SpellQuellerEntersEffect() { super(Outcome.Benefit); - this.staticText = "exile target spell with converted mana cost 4 or less"; + this.staticText = "exile target spell with mana value 4 or less"; } public SpellQuellerEntersEffect(final SpellQuellerEntersEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SpellSatchel.java b/Mage.Sets/src/mage/cards/s/SpellSatchel.java new file mode 100644 index 000000000000..8b42c6199c78 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpellSatchel.java @@ -0,0 +1,50 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.MagecraftAbility; +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.ColorlessManaAbility; +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 SpellSatchel extends CardImpl { + + public SpellSatchel(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, put a book counter on Spell Satchel. + this.addAbility(new MagecraftAbility(new AddCountersSourceEffect(CounterType.BOOK.createInstance()))); + + // {T}, Remove a book counter from Spell Satchel: Add {C}. + Ability ability = new ColorlessManaAbility(); + ability.addCost(new RemoveCountersSourceCost(CounterType.BOOK.createInstance())); + this.addAbility(ability); + + // {3}, {T}, Remove three book counters from Spell Satchel: Draw a card. + ability = new SimpleActivatedAbility(new DrawCardSourceControllerEffect(1), new GenericManaCost(3)); + ability.addCost(new TapSourceCost()); + ability.addCost(new RemoveCountersSourceCost(CounterType.BOOK.createInstance(3))); + this.addAbility(ability); + } + + private SpellSatchel(final SpellSatchel card) { + super(card); + } + + @Override + public SpellSatchel copy() { + return new SpellSatchel(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpellShrivel.java b/Mage.Sets/src/mage/cards/s/SpellShrivel.java index 02ec1047e8c4..7c5d760256a6 100644 --- a/Mage.Sets/src/mage/cards/s/SpellShrivel.java +++ b/Mage.Sets/src/mage/cards/s/SpellShrivel.java @@ -1,23 +1,12 @@ package mage.cards.s; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.Mode; -import mage.abilities.costs.Cost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.abilities.keyword.DevoidAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.stack.Spell; -import mage.game.stack.StackObject; -import mage.players.Player; import mage.target.TargetSpell; -import mage.util.ManaUtil; import java.util.UUID; @@ -33,7 +22,7 @@ public SpellShrivel(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new DevoidAbility(this.color)); // Counter target spell unless its controller pays {4}. If that spell is countered this way, exile it instead of putting it into its owner's graveyard. - this.getSpellAbility().addEffect(new SpellShrivelCounterUnlessPaysEffect()); + this.getSpellAbility().addEffect(new CounterUnlessPaysEffect(new GenericManaCost(4), true)); this.getSpellAbility().addTarget(new TargetSpell()); } @@ -46,50 +35,3 @@ public SpellShrivel copy() { return new SpellShrivel(this); } } - -class SpellShrivelCounterUnlessPaysEffect extends OneShotEffect { - - public SpellShrivelCounterUnlessPaysEffect() { - super(Outcome.Detriment); - } - - public SpellShrivelCounterUnlessPaysEffect(final SpellShrivelCounterUnlessPaysEffect effect) { - super(effect); - } - - @Override - public SpellShrivelCounterUnlessPaysEffect copy() { - return new SpellShrivelCounterUnlessPaysEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - StackObject spell = game.getStack().getStackObject(targetPointer.getFirst(game, source)); - MageObject sourceObject = source.getSourceObject(game); - if ((spell instanceof Spell) && sourceObject != null) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Cost cost = ManaUtil.createManaCost(4, false); - if (!cost.pay(source, game, source, spell.getControllerId(), false)) { - StackObject stackObject = game.getStack().getStackObject(source.getFirstTarget()); - if (stackObject != null && !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, source.getFirstTarget(), source, stackObject.getControllerId()))) { - game.informPlayers(sourceObject.getIdName() + ": cost wasn't payed - countering " + stackObject.getName()); - game.rememberLKI(source.getFirstTarget(), Zone.STACK, stackObject); - controller.moveCards((Spell) spell, Zone.EXILED, source, game); - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERED, source.getFirstTarget(), source, stackObject.getControllerId())); - return true; - } - return false; - } - - } - } - return false; - } - - @Override - public String getText(Mode mode) { - return "Counter target spell unless its controller pays {4}. If that spell is countered this way, exile it instead of putting it into its owner's graveyard"; - } - -} diff --git a/Mage.Sets/src/mage/cards/s/SpellSnare.java b/Mage.Sets/src/mage/cards/s/SpellSnare.java index baaba2a16b28..b1135ddf0388 100644 --- a/Mage.Sets/src/mage/cards/s/SpellSnare.java +++ b/Mage.Sets/src/mage/cards/s/SpellSnare.java @@ -8,7 +8,7 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetSpell; /** @@ -17,10 +17,10 @@ */ public final class SpellSnare extends CardImpl { - private static final FilterSpell filter = new FilterSpell("spell with converted mana cost 2"); + private static final FilterSpell filter = new FilterSpell("spell with mana value 2"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, 2)); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, 2)); } public SpellSnare(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/s/SpellSwindle.java b/Mage.Sets/src/mage/cards/s/SpellSwindle.java index a10f87beded6..b224891a1945 100644 --- a/Mage.Sets/src/mage/cards/s/SpellSwindle.java +++ b/Mage.Sets/src/mage/cards/s/SpellSwindle.java @@ -42,7 +42,7 @@ class SpellSwindleEffect extends OneShotEffect { public SpellSwindleEffect() { super(Outcome.Detriment); - staticText = "Counter target spell. Create X colorless Treasure artifact tokens, where X is that spell's converted mana cost. " + staticText = "Counter target spell. Create X colorless Treasure artifact tokens, where X is that spell's mana value. " + "They have \"{T}, Sacrifice this artifact: Add one mana of any color.\""; } @@ -60,7 +60,7 @@ public boolean apply(Game game, Ability source) { StackObject stackObject = game.getStack().getStackObject(targetPointer.getFirst(game, source)); if (stackObject != null) { game.getStack().counter(source.getFirstTarget(), source, game); - return new CreateTokenEffect(new TreasureToken(), stackObject.getConvertedManaCost()).apply(game, source); + return new CreateTokenEffect(new TreasureToken(), stackObject.getManaValue()).apply(game, source); } return false; } diff --git a/Mage.Sets/src/mage/cards/s/SpellboundDragon.java b/Mage.Sets/src/mage/cards/s/SpellboundDragon.java index 8f3bd0e16a99..5e1ea5767c69 100644 --- a/Mage.Sets/src/mage/cards/s/SpellboundDragon.java +++ b/Mage.Sets/src/mage/cards/s/SpellboundDragon.java @@ -54,7 +54,7 @@ class SpellboundDragonEffect extends OneShotEffect { public SpellboundDragonEffect() { super(Outcome.BoostCreature); - staticText = "draw a card, then discard a card. Spellbound Dragon gets +X/+0 until end of turn, where X is the discarded card's converted mana cost"; + staticText = "draw a card, then discard a card. Spellbound Dragon gets +X/+0 until end of turn, where X is the discarded card's mana value"; } public SpellboundDragonEffect(final SpellboundDragonEffect effect) { @@ -76,7 +76,7 @@ public boolean apply(Game game, Ability source) { you.choose(Outcome.Discard, target, source.getSourceId(), game); Card card = you.getHand().get(target.getFirstTarget(), game); if (card != null && you.discard(card, false, source, game)) { - int cmc = card.getConvertedManaCost(); + int cmc = card.getManaValue(); if (dragon != null) { game.addEffect(new BoostSourceEffect(cmc, 0, Duration.EndOfTurn), source); return true; diff --git a/Mage.Sets/src/mage/cards/s/Spellseeker.java b/Mage.Sets/src/mage/cards/s/Spellseeker.java index 2273e3776b3f..24ab0d1121e7 100644 --- a/Mage.Sets/src/mage/cards/s/Spellseeker.java +++ b/Mage.Sets/src/mage/cards/s/Spellseeker.java @@ -11,7 +11,7 @@ import mage.constants.ComparisonType; import mage.constants.SubType; import mage.filter.common.FilterInstantOrSorceryCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; /** @@ -20,10 +20,10 @@ */ public final class Spellseeker extends CardImpl { - private static final FilterInstantOrSorceryCard filter = new FilterInstantOrSorceryCard("an instant or sorcery card with converted mana cost 2 or less"); + private static final FilterInstantOrSorceryCard filter = new FilterInstantOrSorceryCard("an instant or sorcery card with mana value 2 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); } public Spellseeker(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/s/Spellshift.java b/Mage.Sets/src/mage/cards/s/Spellshift.java index 3a088b6c1f5d..af7f49e14987 100644 --- a/Mage.Sets/src/mage/cards/s/Spellshift.java +++ b/Mage.Sets/src/mage/cards/s/Spellshift.java @@ -51,7 +51,7 @@ class SpellshiftEffect extends OneShotEffect { public SpellshiftEffect() { super(Outcome.Detriment); - this.staticText = "Its controller reveals cards from the top of their library until they reveal an instant or sorcery card. That player may cast that card without paying its mana cost. Then they shuffle their library"; + this.staticText = "Its controller reveals cards from the top of their library until they reveal an instant or sorcery card. That player may cast that card without paying its mana cost. Then they shuffle"; } public SpellshiftEffect(final SpellshiftEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SpellstutterSprite.java b/Mage.Sets/src/mage/cards/s/SpellstutterSprite.java index 6e94e1f9333f..ad378de75b59 100644 --- a/Mage.Sets/src/mage/cards/s/SpellstutterSprite.java +++ b/Mage.Sets/src/mage/cards/s/SpellstutterSprite.java @@ -26,7 +26,7 @@ */ public final class SpellstutterSprite extends CardImpl { - private static final FilterSpell filter = new FilterSpell("spell with converted mana cost X or less, where X is the number of Faeries you control"); + private static final FilterSpell filter = new FilterSpell("spell with mana value X or less, where X is the number of Faeries you control"); static { filter.add(SpellstutterSpritePredicate.instance); @@ -72,7 +72,7 @@ enum SpellstutterSpritePredicate implements ObjectSourcePlayerPredicate input, Game game) { - return input.getObject().getConvertedManaCost() <= + return input.getObject().getManaValue() <= game.getBattlefield().countAll(filter, game.getControllerId(input.getSourceId()), game); } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/Spelltwine.java b/Mage.Sets/src/mage/cards/s/Spelltwine.java index cb4c857284e2..e7ce2fe74bf7 100644 --- a/Mage.Sets/src/mage/cards/s/Spelltwine.java +++ b/Mage.Sets/src/mage/cards/s/Spelltwine.java @@ -48,7 +48,7 @@ public Spelltwine(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new SpelltwineEffect()); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(filter)); this.getSpellAbility().addTarget(new TargetCardInOpponentsGraveyard(filter2)); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } diff --git a/Mage.Sets/src/mage/cards/s/SphereOfSafety.java b/Mage.Sets/src/mage/cards/s/SphereOfSafety.java index 5852f6c8940b..2c5dbfe31377 100644 --- a/Mage.Sets/src/mage/cards/s/SphereOfSafety.java +++ b/Mage.Sets/src/mage/cards/s/SphereOfSafety.java @@ -45,7 +45,7 @@ class SphereOfSafetyPayManaToAttackAllEffect extends CantAttackYouUnlessPayManaA SphereOfSafetyPayManaToAttackAllEffect() { super(null, true); - staticText = "Creatures can't attack you or a planeswalker you control unless their controller pays {X} for each of those creatures, where X is the number of enchantments you control."; + staticText = "Creatures can't attack you or planeswalkers you control unless their controller pays {X} for each of those creatures, where X is the number of enchantments you control."; } SphereOfSafetyPayManaToAttackAllEffect(SphereOfSafetyPayManaToAttackAllEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SphinxAmbassador.java b/Mage.Sets/src/mage/cards/s/SphinxAmbassador.java index 4e6bd19a7e4e..90c2ee27727d 100644 --- a/Mage.Sets/src/mage/cards/s/SphinxAmbassador.java +++ b/Mage.Sets/src/mage/cards/s/SphinxAmbassador.java @@ -55,7 +55,7 @@ class SphinxAmbassadorEffect extends OneShotEffect { public SphinxAmbassadorEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "search that player's library for a card, then that player names a card. If you searched for a creature card that isn't the named card, you may put it onto the battlefield under your control. Then that player shuffles their library"; + this.staticText = "search that player's library for a card, then that player chooses a card name. If you searched for a creature card that doesn't have that name, you may put it onto the battlefield under your control. Then that player shuffles"; } public SphinxAmbassadorEffect(final SphinxAmbassadorEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SphinxsDecree.java b/Mage.Sets/src/mage/cards/s/SphinxsDecree.java index 5a84437a45ca..3a50cdb9c25f 100644 --- a/Mage.Sets/src/mage/cards/s/SphinxsDecree.java +++ b/Mage.Sets/src/mage/cards/s/SphinxsDecree.java @@ -111,7 +111,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { if (opponentId.equals(event.getPlayerId())) { MageObject object = game.getObject(event.getSourceId()); if (event.getType() == GameEvent.EventType.CAST_SPELL) { - if (object != null && (object.isInstant() || object.isSorcery())) { + if (object != null && object.isInstantOrSorcery()) { return true; } } diff --git a/Mage.Sets/src/mage/cards/s/SphinxsHerald.java b/Mage.Sets/src/mage/cards/s/SphinxsHerald.java index 687a7351205f..1518737ec197 100644 --- a/Mage.Sets/src/mage/cards/s/SphinxsHerald.java +++ b/Mage.Sets/src/mage/cards/s/SphinxsHerald.java @@ -1,9 +1,6 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeTargetCost; @@ -14,32 +11,26 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.mageobject.ColorPredicate; import mage.filter.predicate.mageobject.NamePredicate; import mage.target.common.TargetCardInLibrary; -import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetControlledCreatureEachColor; + +import java.util.UUID; /** - * * @author North */ public final class SphinxsHerald extends CardImpl { + private static final FilterCard filter = new FilterCard("card named Sphinx Sovereign"); - private static final FilterControlledCreaturePermanent filterWhite = new FilterControlledCreaturePermanent("a white creature"); - private static final FilterControlledCreaturePermanent filterBlue = new FilterControlledCreaturePermanent("a blue creature"); - private static final FilterControlledCreaturePermanent filterBlack = new FilterControlledCreaturePermanent("a black creature"); static { filter.add(new NamePredicate("Sphinx Sovereign")); - filterWhite.add(new ColorPredicate(ObjectColor.WHITE)); - filterBlue.add(new ColorPredicate(ObjectColor.BLUE)); - filterBlack.add(new ColorPredicate(ObjectColor.BLACK)); } + public SphinxsHerald(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{U}"); this.subtype.add(SubType.VEDALKEN); this.subtype.add(SubType.WIZARD); @@ -48,14 +39,11 @@ public SphinxsHerald(UUID ownerId, CardSetInfo setInfo) { // {2}{U}, {tap}, Sacrifice a white creature, a blue creature, and a black creature: // Search your library for a card named Sphinx Sovereign and put it onto the battlefield. Then shuffle your library. - TargetCardInLibrary target = new TargetCardInLibrary(1, 1, new FilterCard(filter)); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new SearchLibraryPutInPlayEffect(target), - new ManaCostsImpl("{2}{U}")); + Ability ability = new SimpleActivatedAbility(new SearchLibraryPutInPlayEffect( + new TargetCardInLibrary(filter)), new ManaCostsImpl<>("{2}{U}") + ); ability.addCost(new TapSourceCost()); - ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filterWhite, false))); - ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filterBlue, false))); - ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filterBlack, false))); + ability.addCost(new SacrificeTargetCost(new TargetControlledCreatureEachColor("WUB"))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SpinedKarok.java b/Mage.Sets/src/mage/cards/s/SpinedKarok.java new file mode 100644 index 000000000000..19607c5ed2d9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpinedKarok.java @@ -0,0 +1,32 @@ +package mage.cards.s; + +import mage.MageInt; +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 SpinedKarok extends CardImpl { + + public SpinedKarok(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.CROCODILE); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + } + + private SpinedKarok(final SpinedKarok card) { + super(card); + } + + @Override + public SpinedKarok copy() { + return new SpinedKarok(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpiritOfTheAldergard.java b/Mage.Sets/src/mage/cards/s/SpiritOfTheAldergard.java index 5bd8561dc05c..a53b5cd1a527 100644 --- a/Mage.Sets/src/mage/cards/s/SpiritOfTheAldergard.java +++ b/Mage.Sets/src/mage/cards/s/SpiritOfTheAldergard.java @@ -18,7 +18,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterLandCard; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCardInLibrary; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/s/SpiritSummoning.java b/Mage.Sets/src/mage/cards/s/SpiritSummoning.java new file mode 100644 index 000000000000..64cf93766d4e --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpiritSummoning.java @@ -0,0 +1,34 @@ +package mage.cards.s; + +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.LoreholdToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpiritSummoning extends CardImpl { + + public SpiritSummoning(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{R/W}{R/W}"); + + this.subtype.add(SubType.LESSON); + + // Create a 3/2 red and white Spirit creature token. + this.getSpellAbility().addEffect(new CreateTokenEffect(new LoreholdToken())); + } + + private SpiritSummoning(final SpiritSummoning card) { + super(card); + } + + @Override + public SpiritSummoning copy() { + return new SpiritSummoning(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/Spiritualize.java b/Mage.Sets/src/mage/cards/s/Spiritualize.java index faecdc64efa3..27bf9dd81684 100644 --- a/Mage.Sets/src/mage/cards/s/Spiritualize.java +++ b/Mage.Sets/src/mage/cards/s/Spiritualize.java @@ -65,8 +65,7 @@ public SpiritualizeTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || event.getType() == GameEvent.EventType.DAMAGED_CREATURE - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + || event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/s/SpitefulSquad.java b/Mage.Sets/src/mage/cards/s/SpitefulSquad.java new file mode 100644 index 000000000000..f7b642060a23 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpitefulSquad.java @@ -0,0 +1,90 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpitefulSquad extends CardImpl { + + public SpitefulSquad(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // Spiteful Squad enters the battlefield with two +1/+1 counters on it. + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance(2)), + "with two +1/+1 counters on it" + )); + + // When Spiteful Squad dies, put its counters on target creature you control. + Ability ability = new DiesSourceTriggeredAbility(new SpitefulSquadEffect()); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + private SpitefulSquad(final SpitefulSquad card) { + super(card); + } + + @Override + public SpitefulSquad copy() { + return new SpitefulSquad(this); + } +} + +class SpitefulSquadEffect extends OneShotEffect { + + SpitefulSquadEffect() { + super(Outcome.Benefit); + staticText = "put its counters on target creature you control"; + } + + private SpitefulSquadEffect(final SpitefulSquadEffect effect) { + super(effect); + } + + @Override + public SpitefulSquadEffect copy() { + return new SpitefulSquadEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent sourcePermanent = (Permanent) getValue("permanentLeftBattlefield"); + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (sourcePermanent == null || permanent == null) { + return false; + } + sourcePermanent + .getCounters(game) + .values() + .stream() + .forEach(counter -> permanent.addCounters(counter, source.getControllerId(), source, game)); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/Spitemare.java b/Mage.Sets/src/mage/cards/s/Spitemare.java index e03a6b3fcfad..6303fe9bde9f 100644 --- a/Mage.Sets/src/mage/cards/s/Spitemare.java +++ b/Mage.Sets/src/mage/cards/s/Spitemare.java @@ -65,7 +65,7 @@ public SpitemareTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/s/SplitDecision.java b/Mage.Sets/src/mage/cards/s/SplitDecision.java index 97623ae34dfe..e995fd6b871b 100644 --- a/Mage.Sets/src/mage/cards/s/SplitDecision.java +++ b/Mage.Sets/src/mage/cards/s/SplitDecision.java @@ -1,27 +1,27 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CopyTargetSpellEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.choices.TwoChoiceVote; import mage.constants.CardType; import mage.constants.Outcome; import mage.filter.StaticFilters; import mage.game.Game; -import mage.players.Player; +import mage.game.stack.Spell; import mage.target.TargetSpell; +import java.util.UUID; + /** - * - * @author fireshoes + * @author fireshoes, TheElk801 */ public final class SplitDecision extends CardImpl { public SplitDecision(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); // Will of the council - Choose target instant or sorcery spell. Starting with you, each player votes for denial or duplication. If denial gets more votes, counter the spell. If duplication gets more votes or the vote is tied, copy the spell. You may choose new targets for the copy. this.getSpellAbility().addEffect(new SplitDecisionEffect()); @@ -41,11 +41,14 @@ public SplitDecision copy() { class SplitDecisionEffect extends OneShotEffect { SplitDecisionEffect() { - super(Outcome.Benefit); - this.staticText = "Will of the council — Choose target instant or sorcery spell. Starting with you, each player votes for denial or duplication. If denial gets more votes, counter the spell. If duplication gets more votes or the vote is tied, copy the spell. You may choose new targets for the copy"; + super(Outcome.Removal); // cause AI votes for counter all the time so it must it target opponent's spell, not own + this.staticText = "Will of the council — Choose target instant or sorcery spell. " + + "Starting with you, each player votes for denial or duplication. If denial gets more votes, " + + "counter the spell. If duplication gets more votes or the vote is tied, copy the spell. " + + "You may choose new targets for the copy"; } - SplitDecisionEffect(final SplitDecisionEffect effect) { + private SplitDecisionEffect(final SplitDecisionEffect effect) { super(effect); } @@ -56,28 +59,26 @@ public SplitDecisionEffect copy() { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - int denialCount = 0; - int duplicationCount = 0; - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - if (player.chooseUse(Outcome.ExtraTurn, "Choose denial?", source, game)) { - denialCount++; - game.informPlayers(player.getLogName() + " has voted for denial"); - } else { - duplicationCount++; - game.informPlayers(player.getLogName() + " has voted for duplication"); - } - } - } - if (denialCount > duplicationCount) { - return game.getStack().counter(getTargetPointer().getFirst(game, source), source, game); - } else { - return new CopyTargetSpellEffect().apply(game, source); - } + Spell spell = game.getStack().getSpell(getTargetPointer().getFirst(game, source)); + if (spell == null) { + return false; + } + + // Outcome.Benefit - AI will use counter all the time (Denial choice) + // TODO: add AI hint logic in the choice method, see Tyrant's Choice as example + TwoChoiceVote vote = new TwoChoiceVote( + "Denial (counter " + spell.getIdName() + ")", + "Duplication (copy " + spell.getIdName() + ")", + Outcome.Benefit + ); + vote.doVotes(source, game); + + int denialCount = vote.getVoteCount(true); + int duplicationCount = vote.getVoteCount(false); + if (denialCount > duplicationCount) { + return game.getStack().counter(spell.getId(), source, game); + } else { + return new CopyTargetSpellEffect().apply(game, source); } - return false; } } diff --git a/Mage.Sets/src/mage/cards/s/SpringbloomDruid.java b/Mage.Sets/src/mage/cards/s/SpringbloomDruid.java index 253f99fb2571..6ecebc798661 100644 --- a/Mage.Sets/src/mage/cards/s/SpringbloomDruid.java +++ b/Mage.Sets/src/mage/cards/s/SpringbloomDruid.java @@ -37,7 +37,7 @@ public SpringbloomDruid(UUID ownerId, CardSetInfo setInfo) { 0, 2, StaticFilters.FILTER_CARD_BASIC_LAND ), true, Outcome.PutLandInPlay ).setText("search your library for up to two basic land cards, " + - "put them onto the battlefield tapped, then shuffle your library" + "put them onto the battlefield tapped, then shuffle" ), new SacrificeTargetCost(new TargetControlledPermanent(FILTER_CONTROLLED_LAND_SHORT_TEXT)) ))); } diff --git a/Mage.Sets/src/mage/cards/s/SpringmaneCervin.java b/Mage.Sets/src/mage/cards/s/SpringmaneCervin.java new file mode 100644 index 000000000000..f24e417f6a66 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpringmaneCervin.java @@ -0,0 +1,37 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.GainLifeEffect; +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 SpringmaneCervin extends CardImpl { + + public SpringmaneCervin(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.ELK); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // When Springmane Cervin enters the battlefield, you gain 2 life. + this.addAbility(new EntersBattlefieldTriggeredAbility(new GainLifeEffect(2))); + } + + private SpringmaneCervin(final SpringmaneCervin card) { + super(card); + } + + @Override + public SpringmaneCervin copy() { + return new SpringmaneCervin(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SproutbackTrudge.java b/Mage.Sets/src/mage/cards/s/SproutbackTrudge.java new file mode 100644 index 000000000000..bfe4f60e5418 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SproutbackTrudge.java @@ -0,0 +1,96 @@ +package mage.cards.s; + +import mage.ApprovingObject; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.YouGainedLifeCondition; +import mage.abilities.dynamicvalue.common.ControllerGotLifeCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.players.Player; +import mage.watchers.common.PlayerGainedLifeWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SproutbackTrudge extends CardImpl { + + private static final Condition condition = new YouGainedLifeCondition(ComparisonType.MORE_THAN, 0); + + public SproutbackTrudge(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{7}{G}{G}"); + + this.subtype.add(SubType.FUNGUS); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(9); + this.toughness = new MageInt(7); + + // This spell costs {X} less to cast, where X is the amount of life you gained this turn. + this.addAbility(new SimpleStaticAbility( + Zone.ALL, new SpellCostReductionSourceEffect(ControllerGotLifeCount.instance) + ).addHint(ControllerGotLifeCount.getHint()).setRuleAtTheTop(true), new PlayerGainedLifeWatcher()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // At the beginning of your end step, if you gained life this turn, you may cast Sproutback Trudge from your graveyard. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + Zone.GRAVEYARD, new SproutbackTrudgeEffect(), TargetController.YOU, condition, true + )); + } + + private SproutbackTrudge(final SproutbackTrudge card) { + super(card); + } + + @Override + public SproutbackTrudge copy() { + return new SproutbackTrudge(this); + } +} + +class SproutbackTrudgeEffect extends OneShotEffect { + + SproutbackTrudgeEffect() { + super(Outcome.Benefit); + staticText = "cast {this} from your graveyard"; + } + + private SproutbackTrudgeEffect(final SproutbackTrudgeEffect effect) { + super(effect); + } + + @Override + public SproutbackTrudgeEffect copy() { + return new SproutbackTrudgeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObjectIfItStillExists(game); + if (player == null || !(sourceObject instanceof Card)) { + return false; + } + Card card = (Card) sourceObject; + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); + player.cast( + player.chooseAbilityForCast(card, game, false), + game, false, new ApprovingObject(source, game) + ); + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpyNetwork.java b/Mage.Sets/src/mage/cards/s/SpyNetwork.java index 5e15c4be94bf..abb405b5e970 100644 --- a/Mage.Sets/src/mage/cards/s/SpyNetwork.java +++ b/Mage.Sets/src/mage/cards/s/SpyNetwork.java @@ -14,7 +14,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.other.FaceDownPredicate; +import mage.filter.predicate.card.FaceDownPredicate; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/s/SquareUp.java b/Mage.Sets/src/mage/cards/s/SquareUp.java new file mode 100644 index 000000000000..5d83a05ee81a --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SquareUp.java @@ -0,0 +1,33 @@ +package mage.cards.s; + +import mage.abilities.effects.common.continuous.SetPowerToughnessTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SquareUp extends CardImpl { + + public SquareUp(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G/U}"); + + // Target creature has base power and toughness 4/4 until end of turn. + this.getSpellAbility().addEffect(new SetPowerToughnessTargetEffect(4, 4, Duration.EndOfTurn)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private SquareUp(final SquareUp card) { + super(card); + } + + @Override + public SquareUp copy() { + return new SquareUp(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/StaffOfTheLetterMagus.java b/Mage.Sets/src/mage/cards/s/StaffOfTheLetterMagus.java index 8b2120bd93c9..806541bd5b29 100644 --- a/Mage.Sets/src/mage/cards/s/StaffOfTheLetterMagus.java +++ b/Mage.Sets/src/mage/cards/s/StaffOfTheLetterMagus.java @@ -12,6 +12,7 @@ import mage.constants.Outcome; import mage.constants.SetTargetPointer; import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.Spell; @@ -35,7 +36,7 @@ public StaffOfTheLetterMagus(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new AsEntersBattlefieldAbility(new StaffOfTheLetterMagusChooseLetterEffect())); // Whenever a player casts a spell, you gain 1 life for each time the chosen letter appears in that spell’s name. - this.addAbility(new SpellCastAllTriggeredAbility(new StaffOfTheLetterMagusEffect(), new FilterSpell("a spell"), false, SetTargetPointer.SPELL)); + this.addAbility(new SpellCastAllTriggeredAbility(new StaffOfTheLetterMagusEffect(), StaticFilters.FILTER_SPELL_A, false, SetTargetPointer.SPELL)); } private StaffOfTheLetterMagus(final StaffOfTheLetterMagus card) { diff --git a/Mage.Sets/src/mage/cards/s/StagBeetle.java b/Mage.Sets/src/mage/cards/s/StagBeetle.java index 37ceafab538d..ed936e859c98 100644 --- a/Mage.Sets/src/mage/cards/s/StagBeetle.java +++ b/Mage.Sets/src/mage/cards/s/StagBeetle.java @@ -12,7 +12,7 @@ import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/s/StalkingLeonin.java b/Mage.Sets/src/mage/cards/s/StalkingLeonin.java index eba6bb6fca6d..9f47b2abeb57 100644 --- a/Mage.Sets/src/mage/cards/s/StalkingLeonin.java +++ b/Mage.Sets/src/mage/cards/s/StalkingLeonin.java @@ -1,4 +1,3 @@ - package mage.cards.s; import mage.MageInt; @@ -14,20 +13,22 @@ import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreatureAttackingYou; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.filter.common.FilterCreatureAttackingYou; -import mage.target.common.TargetCreaturePermanent; +import mage.target.TargetPermanent; import java.util.UUID; /** - * * @author LevelX2 */ public final class StalkingLeonin extends CardImpl { + private static final FilterPermanent filter = new FilterCreatureAttackingYou(); + public StalkingLeonin(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); @@ -37,9 +38,10 @@ public StalkingLeonin(UUID ownerId, CardSetInfo setInfo) { // When Stalking Leonin enters the battlefield, secretly choose an opponent. this.addAbility(new EntersBattlefieldTriggeredAbility(new ChooseSecretOpponentEffect(), false)); + // Reveal the player you chose: Exile target creature that's attacking you if it's controlled by the chosen player. Activate this ability only once. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new StalkingLeoninEffect(), new RevealSecretOpponentCost()); - ability.addTarget(new TargetCreaturePermanent(new FilterCreatureAttackingYou())); + Ability ability = new SimpleActivatedAbility(new StalkingLeoninEffect(), new RevealSecretOpponentCost()); + ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); } @@ -57,7 +59,7 @@ class StalkingLeoninEffect extends OneShotEffect { public StalkingLeoninEffect() { super(Outcome.Exile); - this.staticText = "Exile target creature that's attacking you if it's controlled by the chosen player. Activate this ability only once"; + this.staticText = "Exile target creature that's attacking you if it's controlled by the chosen player. Activate only once"; } public StalkingLeoninEffect(final StalkingLeoninEffect effect) { @@ -72,16 +74,12 @@ public StalkingLeoninEffect copy() { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (targetCreature != null) { - UUID opponentId = (UUID) game.getState().getValue(source.getSourceId() + ChooseSecretOpponentEffect.SECRET_OPPONENT); - if (opponentId != null && opponentId.equals(targetCreature.getControllerId())) { - controller.moveCards(targetCreature, Zone.EXILED, source, game); - } - } + Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (controller == null || targetCreature == null + || !targetCreature.isControlledBy(ChooseSecretOpponentEffect.getChosenPlayer(source, game))) { return true; } - return false; + controller.moveCards(targetCreature, Zone.EXILED, source, game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/s/StalkingVengeance.java b/Mage.Sets/src/mage/cards/s/StalkingVengeance.java index 33467946b771..13a66a3d40c9 100644 --- a/Mage.Sets/src/mage/cards/s/StalkingVengeance.java +++ b/Mage.Sets/src/mage/cards/s/StalkingVengeance.java @@ -9,7 +9,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetPlayerOrPlaneswalker; diff --git a/Mage.Sets/src/mage/cards/s/StampedingHorncrest.java b/Mage.Sets/src/mage/cards/s/StampedingHorncrest.java index 641d5b98bc3d..85afd557a905 100644 --- a/Mage.Sets/src/mage/cards/s/StampedingHorncrest.java +++ b/Mage.Sets/src/mage/cards/s/StampedingHorncrest.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/s/StarPupil.java b/Mage.Sets/src/mage/cards/s/StarPupil.java new file mode 100644 index 000000000000..544edcb67748 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StarPupil.java @@ -0,0 +1,86 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class StarPupil extends CardImpl { + + public StarPupil(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // Star Pupil enters the battlefield with a +1/+1 counter on it. + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), + "with a +1/+1 counter on it" + )); + + // When Star Pupil dies, put its counters on target creature you control. + Ability ability = new DiesSourceTriggeredAbility(new StarPupilEffect()); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + private StarPupil(final StarPupil card) { + super(card); + } + + @Override + public StarPupil copy() { + return new StarPupil(this); + } +} + +class StarPupilEffect extends OneShotEffect { + + StarPupilEffect() { + super(Outcome.Benefit); + staticText = "put its counters on target creature you control"; + } + + private StarPupilEffect(final StarPupilEffect effect) { + super(effect); + } + + @Override + public StarPupilEffect copy() { + return new StarPupilEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent sourcePermanent = (Permanent) getValue("permanentLeftBattlefield"); + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (sourcePermanent == null || permanent == null) { + return false; + } + sourcePermanent + .getCounters(game) + .values() + .stream() + .forEach(counter -> permanent.addCounters(counter, source.getControllerId(), source, game)); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/StarfieldOfNyx.java b/Mage.Sets/src/mage/cards/s/StarfieldOfNyx.java index b189985bdc78..76f698f279cc 100644 --- a/Mage.Sets/src/mage/cards/s/StarfieldOfNyx.java +++ b/Mage.Sets/src/mage/cards/s/StarfieldOfNyx.java @@ -15,10 +15,11 @@ import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterEnchantmentPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCardInGraveyard; +import mage.target.common.TargetCardInYourGraveyard; /** * @@ -28,7 +29,7 @@ public final class StarfieldOfNyx extends CardImpl { private static final String rule1 = "As long as you control five or more enchantments, " + "each other non-Aura enchantment you control is a creature in addition to its other types " - + "and has base power and base toughness each equal to its converted mana cost."; + + "and has base power and base toughness each equal to its mana value."; private static final FilterCard filterGraveyardEnchantment = new FilterCard("enchantment card from your graveyard"); @@ -41,7 +42,6 @@ public final class StarfieldOfNyx extends CardImpl { static { filterGraveyardEnchantment.add(CardType.ENCHANTMENT.getPredicate()); - filterGraveyardEnchantment.add(TargetController.YOU.getOwnerPredicate()); } public StarfieldOfNyx(UUID ownerId, CardSetInfo setInfo) { @@ -51,7 +51,7 @@ public StarfieldOfNyx(UUID ownerId, CardSetInfo setInfo) { // from your graveyard to the battlefield. Ability ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new ReturnFromGraveyardToBattlefieldTargetEffect(), TargetController.YOU, true); - ability.addTarget(new TargetCardInGraveyard(filterGraveyardEnchantment)); + ability.addTarget(new TargetCardInYourGraveyard(filterGraveyardEnchantment)); this.addAbility(ability); // As long as you control five or more enchantments, each other non-Aura enchantment @@ -87,7 +87,7 @@ public StarfieldOfNyxEffect() { super(Duration.WhileOnBattlefield, Outcome.BecomeCreature); staticText = "Each other non-Aura enchantment you control is a creature " + "in addition to its other types and has base power and " - + "toughness each equal to its converted mana cost"; + + "toughness each equal to its mana value"; this.dependendToTypes.add(DependencyType.EnchantmentAddingRemoving); // Enchanted Evening this.dependendToTypes.add(DependencyType.AuraAddingRemoving); // Cloudform @@ -128,7 +128,7 @@ public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) if (sublayer == SubLayer.SetPT_7b && permanent.isCreature() && !permanent.hasSubtype(SubType.AURA, game)) { - int manaCost = permanent.getConvertedManaCost(); + int manaCost = permanent.getManaValue(); permanent.getPower().setValue(manaCost); permanent.getToughness().setValue(manaCost); } diff --git a/Mage.Sets/src/mage/cards/s/StartFromScratch.java b/Mage.Sets/src/mage/cards/s/StartFromScratch.java new file mode 100644 index 000000000000..e3ae1985e252 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StartFromScratch.java @@ -0,0 +1,44 @@ +package mage.cards.s; + +import mage.abilities.Mode; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetAnyTarget; +import mage.target.common.TargetArtifactPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class StartFromScratch extends CardImpl { + + public StartFromScratch(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}"); + + this.subtype.add(SubType.LESSON); + + // Choose one — + // • Start from Scratch deals 1 damage to any target. + this.getSpellAbility().addEffect(new DamageTargetEffect(1)); + this.getSpellAbility().addTarget(new TargetAnyTarget()); + + // • Destroy target artifact. + Mode mode = new Mode(new DestroyTargetEffect()); + mode.addTarget(new TargetArtifactPermanent()); + this.getSpellAbility().addMode(mode); + } + + private StartFromScratch(final StartFromScratch card) { + super(card); + } + + @Override + public StartFromScratch copy() { + return new StartFromScratch(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/StealStrength.java b/Mage.Sets/src/mage/cards/s/StealStrength.java index e758f5ff55d3..74584aa93919 100644 --- a/Mage.Sets/src/mage/cards/s/StealStrength.java +++ b/Mage.Sets/src/mage/cards/s/StealStrength.java @@ -12,7 +12,7 @@ import mage.constants.Outcome; import mage.constants.SubLayer; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/s/SteamAugury.java b/Mage.Sets/src/mage/cards/s/SteamAugury.java index 13f2de10fec5..af6f005a5772 100644 --- a/Mage.Sets/src/mage/cards/s/SteamAugury.java +++ b/Mage.Sets/src/mage/cards/s/SteamAugury.java @@ -1,7 +1,7 @@ - package mage.cards.s; import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.UUID; @@ -67,6 +67,8 @@ public SteamAuguryEffect copy() { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = game.getObject(source.getSourceId()); + Set cardsToGraveyard = new LinkedHashSet<>(); + Set cardsToHand = new LinkedHashSet<>(); if (controller == null || sourceObject == null) { return false; } @@ -127,9 +129,10 @@ public boolean apply(Game game, Ability source) { if (i < pile1CardsIds.size()) { sb.append(", "); } - card.moveToZone(pile1Zone, source, game, false); + cardsToGraveyard.add(card); } } + controller.moveCards(cardsToGraveyard, pile1Zone, source, game); game.informPlayers(sb.toString()); sb = new StringBuilder(sourceObject.getLogName() + ": Pile 2, going to ").append(pile2Zone == Zone.HAND ? "Hand" : "Graveyard").append(':'); @@ -142,9 +145,10 @@ public boolean apply(Game game, Ability source) { if (i < pile2CardsIds.size()) { sb.append(", "); } - card.moveToZone(pile2Zone, source, game, false); + cardsToHand.add(card); } } + controller.moveCards(cardsToHand, pile2Zone, source, game); game.informPlayers(sb.toString()); } diff --git a/Mage.Sets/src/mage/cards/s/SteelHellkite.java b/Mage.Sets/src/mage/cards/s/SteelHellkite.java index 64a7520e1ed7..4cc764abbe6f 100644 --- a/Mage.Sets/src/mage/cards/s/SteelHellkite.java +++ b/Mage.Sets/src/mage/cards/s/SteelHellkite.java @@ -18,7 +18,7 @@ import mage.filter.common.FilterNonlandPermanent; import mage.filter.predicate.Predicate; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; import mage.game.events.DamagedEvent; @@ -70,7 +70,7 @@ class SteelHellkiteDestroyEffect extends OneShotEffect { public SteelHellkiteDestroyEffect() { super(Outcome.DestroyPermanent); - staticText = "Destroy each nonland permanent with converted mana cost X whose controller was dealt combat damage by {this} this turn"; + staticText = "Destroy each nonland permanent with mana value X whose controller was dealt combat damage by {this} this turn"; } public SteelHellkiteDestroyEffect(final SteelHellkiteDestroyEffect effect) { @@ -93,7 +93,7 @@ public boolean apply(Game game, Ability source) { predicateSet.add(new ControllerIdPredicate(playerId)); } FilterPermanent filter = new FilterNonlandPermanent(); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, source.getManaCostsToPay().getX())); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, source.getManaCostsToPay().getX())); filter.add(Predicates.or(predicateSet)); return new DestroyAllEffect(filter).apply(game, source); } diff --git a/Mage.Sets/src/mage/cards/s/SteelcladSerpent.java b/Mage.Sets/src/mage/cards/s/SteelcladSerpent.java index 7228dde43ad4..00e88ddd5a1a 100644 --- a/Mage.Sets/src/mage/cards/s/SteelcladSerpent.java +++ b/Mage.Sets/src/mage/cards/s/SteelcladSerpent.java @@ -11,7 +11,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/s/SteelshapersGift.java b/Mage.Sets/src/mage/cards/s/SteelshapersGift.java index 6da393e44095..c2fd1457c943 100644 --- a/Mage.Sets/src/mage/cards/s/SteelshapersGift.java +++ b/Mage.Sets/src/mage/cards/s/SteelshapersGift.java @@ -28,7 +28,7 @@ public SteelshapersGift(UUID ownerId, CardSetInfo setInfo) { // Search your library for an Equipment card, reveal that card, and put it into your hand. Then shuffle your library. - this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(1, 1, filter), true)); + this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true)); } private SteelshapersGift(final SteelshapersGift card) { diff --git a/Mage.Sets/src/mage/cards/s/StingingStudy.java b/Mage.Sets/src/mage/cards/s/StingingStudy.java new file mode 100644 index 000000000000..250ed39c8cde --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StingingStudy.java @@ -0,0 +1,86 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.choices.Choice; +import mage.choices.ChoiceImpl; +import mage.constants.CardType; +import mage.constants.CommanderCardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class StingingStudy extends CardImpl { + + public StingingStudy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{B}"); + + // You draw X cards and you lose X life, where X is the mana value of a commander you own on the battlefield or in the command zone. + this.getSpellAbility().addEffect(new StingingStudyEffect()); + } + + private StingingStudy(final StingingStudy card) { + super(card); + } + + @Override + public StingingStudy copy() { + return new StingingStudy(this); + } +} + +class StingingStudyEffect extends OneShotEffect { + + StingingStudyEffect() { + super(Outcome.Benefit); + staticText = "you draw X cards and you lose X life, where X is " + + "the mana value of a commander you own on the battlefield or in the command zone"; + } + + private StingingStudyEffect(final StingingStudyEffect effect) { + super(effect); + } + + @Override + public StingingStudyEffect copy() { + return new StingingStudyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Set manaValues = new HashSet<>(); + for (Card commander : game.getCommanderCardsFromAnyZones(player, CommanderCardType.ANY)) { + manaValues.add(commander.getManaValue()); + } + int chosenValue = 0; + if (manaValues.size() > 1) { + Choice choice = new ChoiceImpl(true); + choice.setChoices(manaValues.stream().map(x -> "" + x).collect(Collectors.toSet())); + player.choose(outcome, choice, game); + chosenValue = Integer.parseInt(choice.getChoice()); + } else { + chosenValue = manaValues.stream().findFirst().orElse(0); + } + if (chosenValue == 0) { + return false; + } + player.drawCards(chosenValue, source, game); + player.loseLife(chosenValue, game, source, false); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/StinkdrinkerBandit.java b/Mage.Sets/src/mage/cards/s/StinkdrinkerBandit.java index 28abcb11899f..1631782334a4 100644 --- a/Mage.Sets/src/mage/cards/s/StinkdrinkerBandit.java +++ b/Mage.Sets/src/mage/cards/s/StinkdrinkerBandit.java @@ -1,12 +1,8 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.continuous.BoostSourceEffect; -import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.keyword.ProwlAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -16,13 +12,14 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.Game; -import mage.game.combat.CombatGroup; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; /** - * * @author BursegSardaukar */ public final class StinkdrinkerBandit extends CardImpl { @@ -45,9 +42,7 @@ public StinkdrinkerBandit(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new ProwlAbility(this, "{1}{B}")); // Whenever a Rogue you control attacks and isn't blocked, it gets +2/+1 until end of turn. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect( - new StinkdrinkerBanditTriggeredAbility(), Duration.WhileOnBattlefield, - filter, "Whenever a Rogue you control attacks and isn't blocked, it gets +2/+1 until end of turn"))); + this.addAbility(new StinkdrinkerBanditTriggeredAbility()); } private StinkdrinkerBandit(final StinkdrinkerBandit card) { @@ -62,11 +57,11 @@ public StinkdrinkerBandit copy() { class StinkdrinkerBanditTriggeredAbility extends TriggeredAbilityImpl { - public StinkdrinkerBanditTriggeredAbility() { - super(Zone.BATTLEFIELD, new BoostSourceEffect(2, 1, Duration.EndOfTurn)); + StinkdrinkerBanditTriggeredAbility() { + super(Zone.BATTLEFIELD, new BoostTargetEffect(2, 1, Duration.EndOfTurn)); } - public StinkdrinkerBanditTriggeredAbility(final StinkdrinkerBanditTriggeredAbility ability) { + private StinkdrinkerBanditTriggeredAbility(final StinkdrinkerBanditTriggeredAbility ability) { super(ability); } @@ -77,20 +72,17 @@ public StinkdrinkerBanditTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARED_BLOCKERS; + return event.getType() == EventType.UNBLOCKED_ATTACKER; } @Override public boolean checkTrigger(GameEvent event, Game game) { - Permanent sourcePermanent = game.getPermanent(getSourceId()); - if (sourcePermanent.isAttacking()) { - for (CombatGroup combatGroup : game.getCombat().getGroups()) { - if (combatGroup.getBlockers().isEmpty() - && combatGroup.getDefenderId().equals(event.getTargetId()) - && combatGroup.getAttackers().contains(getSourceId())) { - return true; - } - } + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null + && isControlledBy(permanent.getControllerId()) + && permanent.hasSubtype(SubType.ROGUE, game)) { + getEffects().setTargetPointer(new FixedTarget(permanent, game)); + return true; } return false; } diff --git a/Mage.Sets/src/mage/cards/s/StirTheGrave.java b/Mage.Sets/src/mage/cards/s/StirTheGrave.java index ea58debdae0a..4e1abc9ec2a9 100644 --- a/Mage.Sets/src/mage/cards/s/StirTheGrave.java +++ b/Mage.Sets/src/mage/cards/s/StirTheGrave.java @@ -9,7 +9,7 @@ import mage.constants.ComparisonType; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetadjustment.TargetAdjuster; @@ -25,7 +25,7 @@ public StirTheGrave(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{B}"); // Return target creature card with converted mana cost X or less from your graveyard to the battlefield. - this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect().setText("return target creature card with converted mana cost X or less from your graveyard to the battlefield")); + this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect().setText("return target creature card with mana value X or less from your graveyard to the battlefield")); this.getSpellAbility().setTargetAdjuster(StirTheGraveAdjuster.instance); } @@ -46,8 +46,8 @@ enum StirTheGraveAdjuster implements TargetAdjuster { public void adjustTargets(Ability ability, Game game) { ability.getTargets().clear(); int xValue = ability.getManaCostsToPay().getX(); - FilterCard filter = new FilterCreatureCard("creature card with converted mana cost " + xValue + " or less from your graveyard"); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, xValue + 1)); + FilterCard filter = new FilterCreatureCard("creature card with mana value " + xValue + " or less from your graveyard"); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); ability.getTargets().add(new TargetCardInYourGraveyard(filter)); } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/StoicFarmer.java b/Mage.Sets/src/mage/cards/s/StoicFarmer.java index 6d62640a42cd..115067f3179b 100644 --- a/Mage.Sets/src/mage/cards/s/StoicFarmer.java +++ b/Mage.Sets/src/mage/cards/s/StoicFarmer.java @@ -48,7 +48,7 @@ public StoicFarmer(UUID ownerId, CardSetInfo setInfo) { new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter)), condition, "search your library for a basic Plains card and reveal it. " + "If an opponent controls more lands than you, put it onto the battlefield tapped. " + - "Otherwise put it into your hand. Then shuffle your library" + "Otherwise put it into your hand. Then shuffle" ) )); diff --git a/Mage.Sets/src/mage/cards/s/StolenByTheFae.java b/Mage.Sets/src/mage/cards/s/StolenByTheFae.java index 9c6668ab56aa..9f3dc2733d21 100644 --- a/Mage.Sets/src/mage/cards/s/StolenByTheFae.java +++ b/Mage.Sets/src/mage/cards/s/StolenByTheFae.java @@ -9,7 +9,7 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.token.FaerieToken; import mage.target.common.TargetCreaturePermanent; @@ -27,7 +27,7 @@ public StolenByTheFae(UUID ownerId, CardSetInfo setInfo) { // Return target creature with converted mana cost X to its owner's hand. You create X 1/1 blue Faerie creature tokens with flying. this.getSpellAbility().addEffect(new ReturnToHandTargetEffect() - .setText("Return target creature with converted mana cost X to its owner's hand")); + .setText("Return target creature with mana value X to its owner's hand")); this.getSpellAbility().addEffect(new CreateTokenEffect(new FaerieToken(), ManacostVariableValue.instance) .setText("You create X 1/1 blue Faerie creature tokens with flying")); this.getSpellAbility().setTargetAdjuster(StolenByTheFaeAdjuster.instance); @@ -50,8 +50,8 @@ enum StolenByTheFaeAdjuster implements TargetAdjuster { public void adjustTargets(Ability ability, Game game) { ability.getTargets().clear(); int xValue = ability.getManaCostsToPay().getX(); - FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with converted mana cost " + xValue); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, xValue)); + FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with mana value " + xValue); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, xValue)); ability.addTarget(new TargetCreaturePermanent(filter)); } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/StolenStrategy.java b/Mage.Sets/src/mage/cards/s/StolenStrategy.java index 79b6ad76651c..33f5156eaa65 100644 --- a/Mage.Sets/src/mage/cards/s/StolenStrategy.java +++ b/Mage.Sets/src/mage/cards/s/StolenStrategy.java @@ -3,13 +3,11 @@ import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.game.ExileZone; import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; @@ -80,7 +78,7 @@ public boolean apply(Game game, Ability source) { if (!card.isLand() && card.getSpellAbility() != null) { // allow to cast the card // and you may spend mana as though it were mana of any color to cast it - CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.EndOfTurn); + CardUtil.makeCardPlayable(game, source, card, Duration.EndOfTurn, true); } } } diff --git a/Mage.Sets/src/mage/cards/s/StonebindersFamiliar.java b/Mage.Sets/src/mage/cards/s/StonebindersFamiliar.java new file mode 100644 index 000000000000..ba7c4f801088 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StonebindersFamiliar.java @@ -0,0 +1,81 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +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.constants.Zone; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.PermanentToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class StonebindersFamiliar extends CardImpl { + + public StonebindersFamiliar(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); + + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.DOG); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Whenever one or more cards are put into exile during your turn, put a +1/+1 counter on Stonebinder's Familiar. This ability triggers only once each turn. + this.addAbility(new StonebindersFamiliarTriggeredAbility()); + } + + private StonebindersFamiliar(final StonebindersFamiliar card) { + super(card); + } + + @Override + public StonebindersFamiliar copy() { + return new StonebindersFamiliar(this); + } +} + +class StonebindersFamiliarTriggeredAbility extends TriggeredAbilityImpl { + + StonebindersFamiliarTriggeredAbility() { + super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance())); + this.setTriggersOnce(true); + } + + private StonebindersFamiliarTriggeredAbility(final StonebindersFamiliarTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + ZoneChangeEvent zEvent = ((ZoneChangeEvent) event); + return zEvent.getToZone() == Zone.EXILED + && isControlledBy(game.getActivePlayerId()) + && (zEvent.getFromZone() != Zone.BATTLEFIELD + || !(zEvent.getTarget() instanceof PermanentToken)); + } + + @Override + public String getRule() { + return "Whenever one or more cards are put into exile during your turn, " + + "put a +1/+1 counter on {this}. This ability triggers only once each turn."; + } + + @Override + public StonebindersFamiliarTriggeredAbility copy() { + return new StonebindersFamiliarTriggeredAbility(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/StoneboundMentor.java b/Mage.Sets/src/mage/cards/s/StoneboundMentor.java new file mode 100644 index 000000000000..a51001f56fb8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StoneboundMentor.java @@ -0,0 +1,38 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.CardsLeaveGraveyardTriggeredAbility; +import mage.abilities.effects.keyword.ScryEffect; +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 StoneboundMentor extends CardImpl { + + public StoneboundMentor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{W}"); + + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.ADVISOR); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Whenever one or more cards leave your graveyard, scry 1. + this.addAbility(new CardsLeaveGraveyardTriggeredAbility(new ScryEffect(1))); + } + + private StoneboundMentor(final StoneboundMentor card) { + super(card); + } + + @Override + public StoneboundMentor copy() { + return new StoneboundMentor(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/StonehewerGiant.java b/Mage.Sets/src/mage/cards/s/StonehewerGiant.java index 22a3685ff8c4..47cf1c89c323 100644 --- a/Mage.Sets/src/mage/cards/s/StonehewerGiant.java +++ b/Mage.Sets/src/mage/cards/s/StonehewerGiant.java @@ -65,7 +65,7 @@ class StonehewerGiantEffect extends OneShotEffect { public StonehewerGiantEffect() { super(Outcome.PutCardInPlay); - this.staticText = "Search your library for an Equipment card and put it onto the battlefield. Attach it to a creature you control. Then shuffle your library"; + this.staticText = "search your library for an Equipment card, put it onto the battlefield, attach it to a creature you control, then shuffle"; } public StonehewerGiantEffect(final StonehewerGiantEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/StonehoofChieftain.java b/Mage.Sets/src/mage/cards/s/StonehoofChieftain.java index 67e60abc6fc6..a7b8fb875880 100644 --- a/Mage.Sets/src/mage/cards/s/StonehoofChieftain.java +++ b/Mage.Sets/src/mage/cards/s/StonehoofChieftain.java @@ -16,7 +16,7 @@ import mage.constants.SetTargetPointer; import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/s/StoneriseSpirit.java b/Mage.Sets/src/mage/cards/s/StoneriseSpirit.java new file mode 100644 index 000000000000..cc4869207acc --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StoneriseSpirit.java @@ -0,0 +1,56 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.ExileFromGraveCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class StoneriseSpirit extends CardImpl { + + public StoneriseSpirit(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.BIRD); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // {4}, Exile a card from your graveyard: Target creature gains flying until end of turn. + Ability ability = new SimpleActivatedAbility(new GainAbilityTargetEffect( + FlyingAbility.getInstance(), Duration.EndOfTurn + ), new GenericManaCost(4)); + ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard( + StaticFilters.FILTER_CARD_FROM_YOUR_GRAVEYARD + ))); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private StoneriseSpirit(final StoneriseSpirit card) { + super(card); + } + + @Override + public StoneriseSpirit copy() { + return new StoneriseSpirit(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/StormKilnArtist.java b/Mage.Sets/src/mage/cards/s/StormKilnArtist.java new file mode 100644 index 000000000000..c6eae9a9d43d --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StormKilnArtist.java @@ -0,0 +1,50 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.ArtifactYouControlCount; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.common.ArtifactYouControlHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.game.permanent.token.TreasureToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class StormKilnArtist extends CardImpl { + + public StormKilnArtist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.DWARF); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Storm-Kiln Artist gets +1/+0 for each artifact you control. + this.addAbility(new SimpleStaticAbility(new BoostSourceEffect( + ArtifactYouControlCount.instance, StaticValue.get(0), Duration.WhileOnBattlefield + ).setText("{this} gets +1/+0 for each artifact you control")).addHint(ArtifactYouControlHint.instance)); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, create a Treasure token. + this.addAbility(new MagecraftAbility(new CreateTokenEffect(new TreasureToken()))); + } + + private StormKilnArtist(final StormKilnArtist card) { + super(card); + } + + @Override + public StormKilnArtist copy() { + return new StormKilnArtist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/StormchaserChimera.java b/Mage.Sets/src/mage/cards/s/StormchaserChimera.java index 46d3a0d13758..bab134f80610 100644 --- a/Mage.Sets/src/mage/cards/s/StormchaserChimera.java +++ b/Mage.Sets/src/mage/cards/s/StormchaserChimera.java @@ -59,7 +59,7 @@ class StormchaserChimeraEffect extends OneShotEffect { public StormchaserChimeraEffect() { super(Outcome.Benefit); - this.staticText = ", then reveal the top card of your library. Stormchaser Chimera gets +X/+0 until end of turn, where X is that card's converted mana cost"; + this.staticText = ", then reveal the top card of your library. Stormchaser Chimera gets +X/+0 until end of turn, where X is that card's mana value"; } public StormchaserChimeraEffect(final StormchaserChimeraEffect effect) { @@ -79,7 +79,7 @@ public boolean apply(Game game, Ability source) { player.revealCards(sourcePermanent.getName(), cards, game); if (card != null) { - game.addEffect(new BoostSourceEffect(card.getConvertedManaCost(), 0, Duration.EndOfTurn), source); + game.addEffect(new BoostSourceEffect(card.getManaValue(), 0, Duration.EndOfTurn), source); return true; } } diff --git a/Mage.Sets/src/mage/cards/s/StormwildCapridor.java b/Mage.Sets/src/mage/cards/s/StormwildCapridor.java index 0c9e257ec273..2ff429f95d17 100644 --- a/Mage.Sets/src/mage/cards/s/StormwildCapridor.java +++ b/Mage.Sets/src/mage/cards/s/StormwildCapridor.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.counters.CounterType; import mage.game.Game; -import mage.game.events.DamageCreatureEvent; +import mage.game.events.DamageEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -91,7 +91,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { || !super.applies(event, source, game)) { return false; } - DamageCreatureEvent damageEvent = (DamageCreatureEvent) event; + DamageEvent damageEvent = (DamageEvent) event; return !damageEvent.isCombatDamage(); } diff --git a/Mage.Sets/src/mage/cards/s/StormwingDragon.java b/Mage.Sets/src/mage/cards/s/StormwingDragon.java index 86336d9ea84a..2ab90cea19f3 100644 --- a/Mage.Sets/src/mage/cards/s/StormwingDragon.java +++ b/Mage.Sets/src/mage/cards/s/StormwingDragon.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/s/StrataScythe.java b/Mage.Sets/src/mage/cards/s/StrataScythe.java index 4a7f01662c37..c9bb0eec6077 100644 --- a/Mage.Sets/src/mage/cards/s/StrataScythe.java +++ b/Mage.Sets/src/mage/cards/s/StrataScythe.java @@ -55,7 +55,7 @@ class StrataScytheImprintEffect extends OneShotEffect { StrataScytheImprintEffect() { super(Outcome.Exile); - staticText = "search your library for a land card, exile it, then shuffle your library"; + staticText = "search your library for a land card, exile it, then shuffle"; } StrataScytheImprintEffect(final StrataScytheImprintEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/Stratadon.java b/Mage.Sets/src/mage/cards/s/Stratadon.java index eae618e2eb87..3fbe22ffc025 100644 --- a/Mage.Sets/src/mage/cards/s/Stratadon.java +++ b/Mage.Sets/src/mage/cards/s/Stratadon.java @@ -51,7 +51,7 @@ class StratadonCostReductionEffect extends CostModificationEffectImpl { public StratadonCostReductionEffect() { super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "Domain — {this} costs {1} less to cast for each basic land type among lands you control."; + staticText = "Domain — This spell costs {1} less to cast for each basic land type among lands you control."; } protected StratadonCostReductionEffect(final StratadonCostReductionEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/StreamOfConsciousness.java b/Mage.Sets/src/mage/cards/s/StreamOfConsciousness.java index 44922530e43a..51ba8deb1cdc 100644 --- a/Mage.Sets/src/mage/cards/s/StreamOfConsciousness.java +++ b/Mage.Sets/src/mage/cards/s/StreamOfConsciousness.java @@ -1,25 +1,16 @@ package mage.cards.s; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; +import mage.abilities.effects.common.TargetPlayerShufflesTargetCardsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.game.Game; -import mage.players.Player; import mage.target.TargetPlayer; -import mage.target.common.TargetCardInGraveyard; +import mage.target.common.TargetCardInTargetPlayersGraveyard; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class StreamOfConsciousness extends CardImpl { @@ -29,10 +20,9 @@ public StreamOfConsciousness(UUID ownerId, CardSetInfo setInfo) { this.subtype.add(SubType.ARCANE); // Target player shuffles up to four target cards from their graveyard into their library. - this.getSpellAbility().addEffect(new StreamOfConsciousnessEffect()); + this.getSpellAbility().addEffect(new TargetPlayerShufflesTargetCardsEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); - this.getSpellAbility().addTarget(new StreamOfConsciousnessTarget()); - + this.getSpellAbility().addTarget(new TargetCardInTargetPlayersGraveyard(4)); } private StreamOfConsciousness(final StreamOfConsciousness card) { @@ -44,59 +34,3 @@ public StreamOfConsciousness copy() { return new StreamOfConsciousness(this); } } - -class StreamOfConsciousnessEffect extends OneShotEffect { - - public StreamOfConsciousnessEffect() { - super(Outcome.Neutral); - this.staticText = "Target player shuffles up to four target cards from their graveyard into their library"; - } - - public StreamOfConsciousnessEffect(final StreamOfConsciousnessEffect effect) { - super(effect); - } - - @Override - public StreamOfConsciousnessEffect copy() { - return new StreamOfConsciousnessEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); - if (player != null) { - Cards targets = new CardsImpl(source.getTargets().get(1).getTargets()); - targets.retainAll(player.getGraveyard()); - return player.shuffleCardsToLibrary(targets, game, source); - } - return false; - } -} - -class StreamOfConsciousnessTarget extends TargetCardInGraveyard { - - public StreamOfConsciousnessTarget() { - super(0, 4, new FilterCard("cards from target player's graveyard")); - } - - public StreamOfConsciousnessTarget(final StreamOfConsciousnessTarget target) { - super(target); - } - - @Override - public boolean canTarget(UUID id, Ability source, Game game) { - Card card = game.getCard(id); - if (card != null && game.getState().getZone(card.getId()) == Zone.GRAVEYARD) { - UUID firstTarget = source.getFirstTarget(); - if (firstTarget != null && game.getPlayer(firstTarget).getGraveyard().contains(id)) { - return filter.match(card, game); - } - } - return false; - } - - @Override - public StreamOfConsciousnessTarget copy() { - return new StreamOfConsciousnessTarget(this); - } -} diff --git a/Mage.Sets/src/mage/cards/s/StrengthInNumbers.java b/Mage.Sets/src/mage/cards/s/StrengthInNumbers.java index 0f9517387ad0..e855988473f7 100644 --- a/Mage.Sets/src/mage/cards/s/StrengthInNumbers.java +++ b/Mage.Sets/src/mage/cards/s/StrengthInNumbers.java @@ -1,7 +1,6 @@ - package mage.cards.s; -import java.util.UUID; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.AttackingCreatureCount; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; @@ -12,18 +11,25 @@ import mage.constants.Duration; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ public final class StrengthInNumbers extends CardImpl { + private static final DynamicValue xValue = new AttackingCreatureCount(); + public StrengthInNumbers(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); // Until end of turn, target creature gains trample and gets +X/+X, where X is the number of attacking creatures. - this.getSpellAbility().addEffect(new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn)); - this.getSpellAbility().addEffect(new BoostTargetEffect(new AttackingCreatureCount("the number of attacking creatures"), new AttackingCreatureCount(), Duration.EndOfTurn, true)); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn + ).setText("Until end of turn, target creature gains trample")); + this.getSpellAbility().addEffect(new BoostTargetEffect( + xValue, xValue, Duration.EndOfTurn, true + ).setText("and gets +X/+X, where X is the number of attacking creatures")); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/cards/s/StrictProctor.java b/Mage.Sets/src/mage/cards/s/StrictProctor.java new file mode 100644 index 000000000000..b6b6034074ad --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StrictProctor.java @@ -0,0 +1,84 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CounterUnlessPaysEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class StrictProctor extends CardImpl { + + public StrictProctor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever a permanent entering the battlefield causes a triggered ability to trigger, counter that ability unless its controller pays {2}. + this.addAbility(new StrictProctorTriggeredAbility()); + } + + private StrictProctor(final StrictProctor card) { + super(card); + } + + @Override + public StrictProctor copy() { + return new StrictProctor(this); + } +} + +class StrictProctorTriggeredAbility extends TriggeredAbilityImpl { + + StrictProctorTriggeredAbility() { + super(Zone.BATTLEFIELD, new CounterUnlessPaysEffect(new GenericManaCost(2))); + } + + private StrictProctorTriggeredAbility(final StrictProctorTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ABILITY_TRIGGERED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + GameEvent triggeringEvent = (GameEvent) game.getState().getValue(event.getId().toString()); + if (triggeringEvent == null || triggeringEvent.getType() != GameEvent.EventType.ENTERS_THE_BATTLEFIELD) { + return false; + } + getEffects().setTargetPointer(new FixedTarget(triggeringEvent.getTargetId(), game)); + return true; + } + + @Override + public StrictProctorTriggeredAbility copy() { + return new StrictProctorTriggeredAbility(this); + } + + @Override + public String getRule() { + return "Whenever a permanent entering the battlefield causes a triggered ability to trigger, " + + "counter that ability unless its controller pays {2}."; + } +} diff --git a/Mage.Sets/src/mage/cards/s/StringOfDisappearances.java b/Mage.Sets/src/mage/cards/s/StringOfDisappearances.java index bd98212c243b..ecbe6e9db020 100644 --- a/Mage.Sets/src/mage/cards/s/StringOfDisappearances.java +++ b/Mage.Sets/src/mage/cards/s/StringOfDisappearances.java @@ -82,7 +82,6 @@ public boolean apply(Game game, Ability source) { return true; } spell.createCopyOnStack(game, source, affectedPlayer.getId(), true); - game.informPlayers(affectedPlayer.getLogName() + " copies " + spell.getName() + '.'); return true; } } diff --git a/Mage.Sets/src/mage/cards/s/StrionicResonator.java b/Mage.Sets/src/mage/cards/s/StrionicResonator.java index ebc7416e5055..23817a78163b 100644 --- a/Mage.Sets/src/mage/cards/s/StrionicResonator.java +++ b/Mage.Sets/src/mage/cards/s/StrionicResonator.java @@ -63,7 +63,6 @@ public boolean apply(Game game, Ability source) { Permanent sourcePermanent = game.getPermanent(source.getSourceId()); if (controller != null && sourcePermanent != null) { stackAbility.createCopyOnStack(game, source, source.getControllerId(), true); - game.informPlayers(sourcePermanent.getIdName() + ": " + controller.getLogName() + " copied triggered ability"); return true; } } diff --git a/Mage.Sets/src/mage/cards/s/StrixhavenStadium.java b/Mage.Sets/src/mage/cards/s/StrixhavenStadium.java new file mode 100644 index 000000000000..9d7c32838761 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StrixhavenStadium.java @@ -0,0 +1,134 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; +import mage.abilities.mana.ColorlessManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.DamagedPlayerEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class StrixhavenStadium extends CardImpl { + + public StrixhavenStadium(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + // {T}: Add {C}. Put a point counter on Strixhaven Stadium. + Ability ability = new ColorlessManaAbility(); + ability.addEffect(new AddCountersSourceEffect(CounterType.POINT.createInstance())); + this.addAbility(ability); + + // Whenever a creature deals combat damage to you, remove a point counter from Strixhaven Stadium. + this.addAbility(new StrixhavenStadiumTriggeredAbility()); + + // Whenever a creature you control deals combat damage to an opponent, put a point counter on Strixhaven Stadium. Then if it has ten or more point counters on it, remove them all and that player loses the game. + ability = new DealsDamageToAPlayerAllTriggeredAbility( + Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.POINT.createInstance()), + StaticFilters.FILTER_CONTROLLED_A_CREATURE, false, SetTargetPointer.PLAYER, + true, true, TargetController.OPPONENT + ); + ability.addEffect(new StrixhavenStadiumEffect()); + this.addAbility(ability); + } + + private StrixhavenStadium(final StrixhavenStadium card) { + super(card); + } + + @Override + public StrixhavenStadium copy() { + return new StrixhavenStadium(this); + } +} + +class StrixhavenStadiumTriggeredAbility extends TriggeredAbilityImpl { + + StrixhavenStadiumTriggeredAbility() { + super(Zone.BATTLEFIELD, new RemoveCounterSourceEffect(CounterType.POINT.createInstance())); + } + + private StrixhavenStadiumTriggeredAbility(final StrixhavenStadiumTriggeredAbility ability) { + super(ability); + } + + @Override + public StrixhavenStadiumTriggeredAbility copy() { + return new StrixhavenStadiumTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + DamagedPlayerEvent damageEvent = (DamagedPlayerEvent) event; + Permanent sourcePermanent = game.getPermanent(event.getSourceId()); + return isControlledBy(damageEvent.getTargetId()) + && damageEvent.isCombatDamage() + && sourcePermanent != null + && sourcePermanent.isCreature(); + } + + @Override + public String getRule() { + return "Whenever a creature deals combat damage to you, remove a point counter from {this}."; + } +} + +class StrixhavenStadiumEffect extends OneShotEffect { + + StrixhavenStadiumEffect() { + super(Outcome.Benefit); + staticText = "Then if it has ten or more point counters on it, remove them all and that player loses the game"; + } + + private StrixhavenStadiumEffect(final StrixhavenStadiumEffect effect) { + super(effect); + } + + @Override + public StrixhavenStadiumEffect copy() { + return new StrixhavenStadiumEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + boolean lki = false; + if (permanent == null) { + lki = true; + permanent = source.getSourcePermanentOrLKI(game); + } + if (permanent == null || permanent.getCounters(game).getCount(CounterType.POINT) < 10) { + return false; + } + if (!lki) { + permanent.removeCounters(CounterType.POINT.createInstance( + permanent.getCounters(game).getCount(CounterType.POINT) + ), source, game); + } + Player player = game.getPlayer(targetPointer.getFirst(game, source)); + if (player != null) { + player.lost(game); + return true; + } + return !lki; + } +} diff --git a/Mage.Sets/src/mage/cards/s/StromkirkMentor.java b/Mage.Sets/src/mage/cards/s/StromkirkMentor.java index 834d31f71477..679f3672eedc 100644 --- a/Mage.Sets/src/mage/cards/s/StromkirkMentor.java +++ b/Mage.Sets/src/mage/cards/s/StromkirkMentor.java @@ -12,7 +12,7 @@ import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledPermanent; /** diff --git a/Mage.Sets/src/mage/cards/s/StrongholdGambit.java b/Mage.Sets/src/mage/cards/s/StrongholdGambit.java index d70c68cdbe1f..33c9d58f8125 100644 --- a/Mage.Sets/src/mage/cards/s/StrongholdGambit.java +++ b/Mage.Sets/src/mage/cards/s/StrongholdGambit.java @@ -46,7 +46,7 @@ class StrongholdGambitEffect extends OneShotEffect { public StrongholdGambitEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "Each player chooses a card in their hand. Then each player reveals their chosen card. The owner of each creature card revealed this way with the lowest converted mana cost puts it onto the battlefield"; + this.staticText = "Each player chooses a card in their hand. Then each player reveals their chosen card. The owner of each creature card revealed this way with the lowest mana value puts it onto the battlefield"; } public StrongholdGambitEffect(final StrongholdGambitEffect effect) { @@ -82,8 +82,8 @@ public boolean apply(Game game, Ability source) { Cards cardsToReveal = new CardsImpl(card); player.revealCards(sourceObject.getIdName() + " (" + player.getName() + ')', cardsToReveal, game); if (card.isCreature() - && lowestCMC > card.getConvertedManaCost()) { - lowestCMC = card.getConvertedManaCost(); + && lowestCMC > card.getManaValue()) { + lowestCMC = card.getManaValue(); } } } @@ -96,7 +96,7 @@ public boolean apply(Game game, Ability source) { Card card = game.getCard(choosenCard.get(playerId)); if (card != null) { if (card.isCreature() - && lowestCMC == card.getConvertedManaCost()) { + && lowestCMC == card.getManaValue()) { creaturesToBattlefield.add(card); } } diff --git a/Mage.Sets/src/mage/cards/s/StudyBreak.java b/Mage.Sets/src/mage/cards/s/StudyBreak.java new file mode 100644 index 000000000000..7ee654547fdc --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StudyBreak.java @@ -0,0 +1,36 @@ +package mage.cards.s; + +import mage.abilities.effects.common.LearnEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class StudyBreak extends CardImpl { + + public StudyBreak(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); + + // Tap up to two target creatures. + this.getSpellAbility().addEffect(new TapTargetEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 2)); + + // Learn. + this.getSpellAbility().addEffect(new LearnEffect().concatBy("
")); + } + + private StudyBreak(final StudyBreak card) { + super(card); + } + + @Override + public StudyBreak copy() { + return new StudyBreak(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/StudyHall.java b/Mage.Sets/src/mage/cards/s/StudyHall.java new file mode 100644 index 000000000000..01ad66aef5d9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StudyHall.java @@ -0,0 +1,125 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.mana.AnyColorManaAbility; +import mage.abilities.mana.ColorlessManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.players.ManaPoolItem; +import mage.players.Player; +import mage.watchers.common.CommanderPlaysCountWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class StudyHall extends CardImpl { + + public StudyHall(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // {T}: Add {C}. + this.addAbility(new ColorlessManaAbility()); + + // {1}, {T}: Add one mana of any color. When you spend this mana to cast your commander, scry X, where X is the number of times it's been cast from the command zone this game. + AnyColorManaAbility ability = new AnyColorManaAbility(new GenericManaCost(1)); + ability.addCost(new TapSourceCost()); + ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new StudyHallTriggeredAbility())); + ability.setUndoPossible(false); + this.addAbility(ability); + } + + private StudyHall(final StudyHall card) { + super(card); + } + + @Override + public StudyHall copy() { + return new StudyHall(this); + } +} + +class StudyHallTriggeredAbility extends DelayedTriggeredAbility { + + StudyHallTriggeredAbility() { + super(null, Duration.Custom, true, false); + } + + private StudyHallTriggeredAbility(final StudyHallTriggeredAbility ability) { + super(ability); + } + + @Override + public StudyHallTriggeredAbility copy() { + return new StudyHallTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.MANA_PAID; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!getSourceId().equals(event.getSourceId())) { + return false; + } + Permanent sourcePermanent = getSourcePermanentOrLKI(game); + if (sourcePermanent == null + || sourcePermanent + .getAbilities(game) + .stream() + .map(Ability::getOriginalId) + .map(UUID::toString) + .noneMatch(event.getData()::equals)) { + return false; + } + Player player = game.getPlayer(getControllerId()); + Spell spell = game.getStack().getSpell(event.getTargetId()); + CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class); + if (player == null + || spell == null + || watcher == null + || !game.isCommanderObject(player, spell)) { + return false; + } + getEffects().clear(); + addEffect(new ScryEffect(watcher.getPlaysCount(spell.getMainCard().getId()))); + return true; + } + + @Override + public boolean isInactive(Game game) { + if (super.isInactive(game)) { + return true; + } + // must remove effect on empty mana pool to fix accumulate bug + // if no mana in pool then it can be discarded + Player player = game.getPlayer(this.getControllerId()); + return player == null + || player + .getManaPool() + .getManaItems() + .stream() + .map(ManaPoolItem::getSourceId) + .noneMatch(getSourceId()::equals); + } + + @Override + public String getRule() { + return "When you spend this mana to cast your commander, scry X, " + + "where X is the number of times it's been cast from the command zone this game."; + } +} diff --git a/Mage.Sets/src/mage/cards/s/StuffyDoll.java b/Mage.Sets/src/mage/cards/s/StuffyDoll.java index fe0380d109b6..9110c0178073 100644 --- a/Mage.Sets/src/mage/cards/s/StuffyDoll.java +++ b/Mage.Sets/src/mage/cards/s/StuffyDoll.java @@ -71,7 +71,7 @@ public StuffyDollTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/s/StumpsquallHydra.java b/Mage.Sets/src/mage/cards/s/StumpsquallHydra.java index 2d17fa4d45a2..f9cb1084411e 100644 --- a/Mage.Sets/src/mage/cards/s/StumpsquallHydra.java +++ b/Mage.Sets/src/mage/cards/s/StumpsquallHydra.java @@ -15,7 +15,7 @@ import mage.filter.FilterPermanent; import mage.filter.predicate.ObjectSourcePlayer; import mage.filter.predicate.ObjectSourcePlayerPredicate; -import mage.filter.predicate.permanent.CommanderPredicate; +import mage.filter.predicate.mageobject.CommanderPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/s/StunningReversal.java b/Mage.Sets/src/mage/cards/s/StunningReversal.java index f30461c1fbfb..f1b6a14ab597 100644 --- a/Mage.Sets/src/mage/cards/s/StunningReversal.java +++ b/Mage.Sets/src/mage/cards/s/StunningReversal.java @@ -27,7 +27,7 @@ public StunningReversal(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new StunningReversalEffect()); // Exile Stunning Reversal. - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private StunningReversal(final StunningReversal card) { diff --git a/Mage.Sets/src/mage/cards/s/Subdue.java b/Mage.Sets/src/mage/cards/s/Subdue.java index 92c81de6580e..18345d5ae9e0 100644 --- a/Mage.Sets/src/mage/cards/s/Subdue.java +++ b/Mage.Sets/src/mage/cards/s/Subdue.java @@ -3,7 +3,7 @@ import java.util.UUID; import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.dynamicvalue.common.TargetConvertedManaCost; +import mage.abilities.dynamicvalue.common.TargetManaValue; import mage.abilities.effects.common.PreventDamageByTargetEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; @@ -24,8 +24,8 @@ public Subdue(UUID ownerId, CardSetInfo setInfo) { // Prevent all combat damage that would be dealt by target creature this turn. That creature gets +0/+X until end of turn, where X is its converted mana cost. this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addEffect(new PreventDamageByTargetEffect(Duration.EndOfTurn, true)); - this.getSpellAbility().addEffect(new BoostTargetEffect(StaticValue.get(0), TargetConvertedManaCost.instance, Duration.EndOfTurn, true) - .setText("That creature gets +0/+X until end of turn, where X is its converted mana cost")); + this.getSpellAbility().addEffect(new BoostTargetEffect(StaticValue.get(0), TargetManaValue.instance, Duration.EndOfTurn, true) + .setText("That creature gets +0/+X until end of turn, where X is its mana value")); } private Subdue(final Subdue card) { diff --git a/Mage.Sets/src/mage/cards/s/SubiraTulzidiCaravanner.java b/Mage.Sets/src/mage/cards/s/SubiraTulzidiCaravanner.java index 15a359a4fd7e..abc8cb8022c3 100644 --- a/Mage.Sets/src/mage/cards/s/SubiraTulzidiCaravanner.java +++ b/Mage.Sets/src/mage/cards/s/SubiraTulzidiCaravanner.java @@ -18,7 +18,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.DamagedPlayerEvent; import mage.game.events.GameEvent; diff --git a/Mage.Sets/src/mage/cards/s/SuddenBreakthrough.java b/Mage.Sets/src/mage/cards/s/SuddenBreakthrough.java new file mode 100644 index 000000000000..81847864e5e7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SuddenBreakthrough.java @@ -0,0 +1,44 @@ +package mage.cards.s; + +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.game.permanent.token.TreasureToken; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SuddenBreakthrough extends CardImpl { + + public SuddenBreakthrough(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); + + // Target creature gets +2/+0 and gains first strike until end of turn. + this.getSpellAbility().addEffect(new BoostTargetEffect(2, 0) + .setText("target creature gets +2/+0")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + FirstStrikeAbility.getInstance(), Duration.EndOfTurn + ).setText("and gains first strike until end of turn.
")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + + // Create a Treasure token. + this.getSpellAbility().addEffect(new CreateTokenEffect(new TreasureToken())); + } + + private SuddenBreakthrough(final SuddenBreakthrough card) { + super(card); + } + + @Override + public SuddenBreakthrough copy() { + return new SuddenBreakthrough(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SulfuricVapors.java b/Mage.Sets/src/mage/cards/s/SulfuricVapors.java index acf4f9fba355..72c553101071 100644 --- a/Mage.Sets/src/mage/cards/s/SulfuricVapors.java +++ b/Mage.Sets/src/mage/cards/s/SulfuricVapors.java @@ -59,8 +59,7 @@ public SulfuricVaporsEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE || - event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER || + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT || event.getType() == GameEvent.EventType.DAMAGE_PLAYER; } diff --git a/Mage.Sets/src/mage/cards/s/SunCrestedPterodon.java b/Mage.Sets/src/mage/cards/s/SunCrestedPterodon.java index d3f70b0cb39e..6b4853522343 100644 --- a/Mage.Sets/src/mage/cards/s/SunCrestedPterodon.java +++ b/Mage.Sets/src/mage/cards/s/SunCrestedPterodon.java @@ -19,7 +19,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * @author JayDi85 diff --git a/Mage.Sets/src/mage/cards/s/SunDroplet.java b/Mage.Sets/src/mage/cards/s/SunDroplet.java index c97c95e4e05f..2d53bef3c4ef 100644 --- a/Mage.Sets/src/mage/cards/s/SunDroplet.java +++ b/Mage.Sets/src/mage/cards/s/SunDroplet.java @@ -34,7 +34,7 @@ public SunDroplet(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new BeginningOfUpkeepTriggeredAbility( new DoIfCostPaid( new GainLifeEffect(1), new RemoveCountersSourceCost(CounterType.CHARGE.createInstance()) - ), TargetController.ANY, false + ), TargetController.EACH_PLAYER, false )); } diff --git a/Mage.Sets/src/mage/cards/s/SunTitan.java b/Mage.Sets/src/mage/cards/s/SunTitan.java index eda171b247b8..4d1ba7902629 100644 --- a/Mage.Sets/src/mage/cards/s/SunTitan.java +++ b/Mage.Sets/src/mage/cards/s/SunTitan.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.constants.ComparisonType; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInYourGraveyard; /** @@ -22,10 +22,10 @@ */ public final class SunTitan extends CardImpl { - private static final FilterPermanentCard filter = new FilterPermanentCard("permanent card with converted mana cost 3 or less from your graveyard"); + private static final FilterPermanentCard filter = new FilterPermanentCard("permanent card with mana value 3 or less from your graveyard"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public SunTitan(UUID ownerId, CardSetInfo setInfo) { @@ -39,7 +39,7 @@ public SunTitan(UUID ownerId, CardSetInfo setInfo) { this.addAbility(VigilanceAbility.getInstance()); // Whenever Sun Titan enters the battlefield or attacks, you may return target permanent card with converted mana cost 3 or less from your graveyard to the battlefield. - Ability ability = new EntersBattlefieldOrAttacksSourceTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(false, false), true); + Ability ability = new EntersBattlefieldOrAttacksSourceTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(), true); ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SunbirdsInvocation.java b/Mage.Sets/src/mage/cards/s/SunbirdsInvocation.java index fdd8e5eec1c5..83d6e0e03218 100644 --- a/Mage.Sets/src/mage/cards/s/SunbirdsInvocation.java +++ b/Mage.Sets/src/mage/cards/s/SunbirdsInvocation.java @@ -18,7 +18,7 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.common.FilterNonlandCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; @@ -89,9 +89,9 @@ public SunbirdsInvocationTriggeredAbility copy() { public String getRule() { return "Whenever you cast a spell from your hand, " + "reveal the top X cards of your library, " - + "where X is that spell's converted mana cost. " + + "where X is that spell's mana value. " + "You may cast a card revealed this way with " - + "converted mana cost X or less without paying its mana cost." + + "mana value X or less without paying its mana cost." + " Put the rest on the bottom of your library in a random order."; } } @@ -101,8 +101,8 @@ class SunbirdsInvocationEffect extends OneShotEffect { public SunbirdsInvocationEffect() { super(Outcome.PutCardInPlay); staticText = "reveal the top X cards of your library, where X is that " - + "spell's converted mana cost. You may cast a card revealed this " - + "way with converted mana cost X or less without paying its mana cost. " + + "spell's mana value. You may cast a card revealed this " + + "way with mana value X or less without paying its mana cost. " + "Put the rest on the bottom of your library in a random order"; } @@ -122,13 +122,13 @@ public boolean apply(Game game, Ability source) { if (spell == null) { return false; } - int xValue = spell.getConvertedManaCost(); + int xValue = spell.getManaValue(); Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, xValue)); if (!cards.isEmpty()) { controller.revealCards(sourceObject.getIdName(), cards, game); - FilterCard filter = new FilterNonlandCard("card revealed this way with converted mana cost " + xValue + " or less"); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, xValue + 1)); + FilterCard filter = new FilterNonlandCard("card revealed this way with mana value " + xValue + " or less"); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); TargetCard target = new TargetCard(1, Zone.LIBRARY, filter); if (controller.chooseTarget(Outcome.PlayForFree, cards, target, source, game)) { diff --git a/Mage.Sets/src/mage/cards/s/Sunforger.java b/Mage.Sets/src/mage/cards/s/Sunforger.java index ed391e9268da..faa0b72a91ec 100644 --- a/Mage.Sets/src/mage/cards/s/Sunforger.java +++ b/Mage.Sets/src/mage/cards/s/Sunforger.java @@ -27,7 +27,7 @@ import mage.filter.predicate.Predicate; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -75,8 +75,8 @@ class SunforgerEffect extends OneShotEffect { public SunforgerEffect() { super(Outcome.PlayForFree); staticText = "Search your library for a red or white instant " - + "card with converted mana cost 4 or less and cast that " - + "card without paying its mana cost. Then shuffle your library"; + + "card with mana value 4 or less and cast that " + + "card without paying its mana cost. Then shuffle"; } public SunforgerEffect(final SunforgerEffect effect) { @@ -99,13 +99,13 @@ public boolean apply(Game game, Ability source) { * it). If you can't find a cast-able card (or choose not to), * nothing happens and you shuffle your library. */ - FilterCard filter = new FilterCard("red or white instant card with converted mana cost 4 or less"); + FilterCard filter = new FilterCard("red or white instant card with mana value 4 or less"); TargetCardInLibrary target = new TargetCardInLibrary(filter); filter.add(Predicates.or( new ColorPredicate(ObjectColor.RED), new ColorPredicate(ObjectColor.WHITE))); filter.add(CardType.INSTANT.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 5)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 5)); filter.add(new CardCanBeCastPredicate(source.getControllerId())); if (controller.searchLibrary(target, source, game, controller.getId())) { UUID targetId = target.getFirstTarget(); @@ -179,7 +179,7 @@ public boolean apply(Card input, Game game) { SpellAbility ability = input.getSpellAbility().copy(); ability.setControllerId(controllerId); input.adjustTargets(ability, game); - return ability.canChooseTarget(game); + return ability.canChooseTarget(game, controllerId); } @Override diff --git a/Mage.Sets/src/mage/cards/s/SunglassesOfUrza.java b/Mage.Sets/src/mage/cards/s/SunglassesOfUrza.java index 7b5458a7d184..d60f2ef88b85 100644 --- a/Mage.Sets/src/mage/cards/s/SunglassesOfUrza.java +++ b/Mage.Sets/src/mage/cards/s/SunglassesOfUrza.java @@ -1,30 +1,24 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.AsThoughManaEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AsThoughEffectType; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.ManaType; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.players.ManaPoolItem; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class SunglassesOfUrza extends CardImpl { public SunglassesOfUrza(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); // You may spend white mana as though it were red mana. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SunglassesOfUrzaManaAsThoughtEffect())); @@ -62,7 +56,7 @@ public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID a if (mana.getWhite() > 0 && ManaType.RED == manaType) { return ManaType.WHITE; } - return manaType; + return null; } @Override diff --git a/Mage.Sets/src/mage/cards/s/SunhomeEnforcer.java b/Mage.Sets/src/mage/cards/s/SunhomeEnforcer.java index 577ad55e27a0..3c794f83ec43 100644 --- a/Mage.Sets/src/mage/cards/s/SunhomeEnforcer.java +++ b/Mage.Sets/src/mage/cards/s/SunhomeEnforcer.java @@ -66,9 +66,8 @@ public SunhomeEnforcerTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE - || event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT + || event.getType() == GameEvent.EventType.DAMAGED_PLAYER; } @Override diff --git a/Mage.Sets/src/mage/cards/s/SunkenHope.java b/Mage.Sets/src/mage/cards/s/SunkenHope.java index d6d4141e02a9..52d5b2b62242 100644 --- a/Mage.Sets/src/mage/cards/s/SunkenHope.java +++ b/Mage.Sets/src/mage/cards/s/SunkenHope.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -68,14 +67,15 @@ public boolean apply(Game game, Ability source) { Target target = new TargetControlledPermanent(1, 1, new FilterControlledCreaturePermanent(), true); if (target.canChoose(source.getSourceId(), player.getId(), game)) { - while (player.canRespond() && !target.isChosen() && target.canChoose(source.getSourceId(), player.getId(), game)) { + while (player.canRespond() && !target.isChosen() + && target.canChoose(source.getSourceId(), player.getId(), game)) { player.chooseTarget(Outcome.ReturnToHand, target, source, game); } for (UUID targetId : target.getTargets()) { Permanent permanent = game.getPermanent(targetId); if (permanent != null) { - result |= permanent.moveToZone(Zone.HAND, source, game, false); + result |= player.moveCards(permanent, Zone.HAND, source, game); } } } diff --git a/Mage.Sets/src/mage/cards/s/SunscorchRegent.java b/Mage.Sets/src/mage/cards/s/SunscorchRegent.java index f78c9d89c93b..d4516cf1d960 100644 --- a/Mage.Sets/src/mage/cards/s/SunscorchRegent.java +++ b/Mage.Sets/src/mage/cards/s/SunscorchRegent.java @@ -31,7 +31,7 @@ public SunscorchRegent(UUID ownerId, CardSetInfo setInfo) { // Whenever an opponent casts a spell, put a +1/+1 counter on Sunscorch Regent and you gain 1 life. Ability ability = new SpellCastOpponentTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false); - ability.addEffect(new GainLifeEffect(1)); + ability.addEffect(new GainLifeEffect(1).concatBy("and")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SunstrikeLegionnaire.java b/Mage.Sets/src/mage/cards/s/SunstrikeLegionnaire.java index 3f3525e4b2a1..df4bcfcaec47 100644 --- a/Mage.Sets/src/mage/cards/s/SunstrikeLegionnaire.java +++ b/Mage.Sets/src/mage/cards/s/SunstrikeLegionnaire.java @@ -18,8 +18,8 @@ import mage.constants.ComparisonType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** @@ -29,11 +29,11 @@ public final class SunstrikeLegionnaire extends CardImpl { private static final FilterCreaturePermanent untapFilter = new FilterCreaturePermanent("another creature"); - private static final FilterCreaturePermanent tapFilter = new FilterCreaturePermanent("creature with converted mana cost 3 or less"); + private static final FilterCreaturePermanent tapFilter = new FilterCreaturePermanent("creature with mana value 3 or less"); static { untapFilter.add(AnotherPredicate.instance); - tapFilter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + tapFilter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public SunstrikeLegionnaire(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/s/SuperDuperDeathRay.java b/Mage.Sets/src/mage/cards/s/SuperDuperDeathRay.java index 458ddcbb4b64..b92ddbbf0e4f 100644 --- a/Mage.Sets/src/mage/cards/s/SuperDuperDeathRay.java +++ b/Mage.Sets/src/mage/cards/s/SuperDuperDeathRay.java @@ -1,24 +1,15 @@ package mage.cards.s; -import mage.MageObject; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageWithExcessEffect; import mage.abilities.effects.common.InfoEffect; -import mage.abilities.keyword.DeathtouchAbility; 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.players.Player; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; -import static mage.game.combat.CombatGroup.getLethalDamage; - /** * @author TheElk801 */ @@ -33,7 +24,8 @@ public SuperDuperDeathRay(UUID ownerId, CardSetInfo setInfo) { ))); // Super-Duper Death Ray deals 4 damage to target creature. - this.getSpellAbility().addEffect(new SuperDuperDeathRayEffect()); + this.getSpellAbility().addEffect(new DamageWithExcessEffect(4) + .setText("{this} deals 4 damage to target creature")); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } @@ -46,40 +38,3 @@ public SuperDuperDeathRay copy() { return new SuperDuperDeathRay(this); } } - -class SuperDuperDeathRayEffect extends OneShotEffect { - - SuperDuperDeathRayEffect() { - super(Outcome.Benefit); - staticText = "{this} deals 4 damage to target creature."; - } - - private SuperDuperDeathRayEffect(final SuperDuperDeathRayEffect effect) { - super(effect); - } - - @Override - public SuperDuperDeathRayEffect copy() { - return new SuperDuperDeathRayEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getFirstTarget()); - MageObject sourceObject = source.getSourceObject(game); - if (permanent == null || sourceObject == null) { - return false; - } - int lethal = getLethalDamage(permanent, game); - if (sourceObject.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) { - lethal = Math.min(lethal, 1); - } - lethal = Math.min(lethal, 4); - permanent.damage(lethal, source.getSourceId(), source, game); - Player player = game.getPlayer(permanent.getControllerId()); - if (player != null && lethal < 4) { - player.damage(4 - lethal, source.getSourceId(), source, game); - } - return true; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/SupplyRunners.java b/Mage.Sets/src/mage/cards/s/SupplyRunners.java index 72ed35e23b3e..031346ecbbb6 100644 --- a/Mage.Sets/src/mage/cards/s/SupplyRunners.java +++ b/Mage.Sets/src/mage/cards/s/SupplyRunners.java @@ -10,7 +10,7 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/s/SupremeInquisitor.java b/Mage.Sets/src/mage/cards/s/SupremeInquisitor.java index b8a307c9e1a1..a3bbf50e7883 100644 --- a/Mage.Sets/src/mage/cards/s/SupremeInquisitor.java +++ b/Mage.Sets/src/mage/cards/s/SupremeInquisitor.java @@ -68,7 +68,7 @@ class SupremeInquisitorEffect extends OneShotEffect { public SupremeInquisitorEffect() { super(Outcome.Exile); - staticText = "Search target player's library for up to five cards and exile them. Then that player shuffles their library"; + staticText = "Search target player's library for up to five cards and exile them. Then that player shuffles"; } public SupremeInquisitorEffect(final SupremeInquisitorEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SupremeLeaderSnoke.java b/Mage.Sets/src/mage/cards/s/SupremeLeaderSnoke.java index 368adc6c5660..e8eb5c303de9 100644 --- a/Mage.Sets/src/mage/cards/s/SupremeLeaderSnoke.java +++ b/Mage.Sets/src/mage/cards/s/SupremeLeaderSnoke.java @@ -25,7 +25,7 @@ import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; @@ -55,7 +55,7 @@ public SupremeLeaderSnoke(UUID ownerId, CardSetInfo setInfo) { // -X: Gain control of target creature with converted mana cost X. Untap that creature. It gains haste. Sacrifice that creature at the beginning of the next end step. Ability ability3 = new LoyaltyAbility(new GainControlTargetEffect(Duration.WhileOnBattlefield) - .setText("Gain control of target creature with converted mana cost X")); + .setText("Gain control of target creature with mana value X")); ability3.addEffect(new UntapTargetEffect().setText("Untap that creature")); ability3.addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.WhileOnBattlefield).setText("It gains haste")); ability3.addEffect(new GainAbilityTargetEffect(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new SacrificeSourceEffect()), Duration.WhileOnBattlefield) @@ -86,7 +86,7 @@ public void adjustTargets(Ability ability, Game game) { } } FilterCreaturePermanent newFilter = StaticFilters.FILTER_PERMANENT_CREATURE.copy(); - newFilter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, cmc)); + newFilter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, cmc)); ability.getTargets().clear(); ability.addTarget(new TargetCreaturePermanent(newFilter)); } diff --git a/Mage.Sets/src/mage/cards/s/SureFootedInfiltrator.java b/Mage.Sets/src/mage/cards/s/SureFootedInfiltrator.java index 01c722ddd3ac..f9d93c4b5b9e 100644 --- a/Mage.Sets/src/mage/cards/s/SureFootedInfiltrator.java +++ b/Mage.Sets/src/mage/cards/s/SureFootedInfiltrator.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TappedPredicate; import mage.target.common.TargetControlledPermanent; diff --git a/Mage.Sets/src/mage/cards/s/SurgeOfStrength.java b/Mage.Sets/src/mage/cards/s/SurgeOfStrength.java index 216682623174..561637767901 100644 --- a/Mage.Sets/src/mage/cards/s/SurgeOfStrength.java +++ b/Mage.Sets/src/mage/cards/s/SurgeOfStrength.java @@ -5,7 +5,7 @@ import mage.ObjectColor; import mage.abilities.costs.common.DiscardTargetCost; import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.dynamicvalue.common.TargetConvertedManaCost; +import mage.abilities.dynamicvalue.common.TargetManaValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; @@ -41,8 +41,8 @@ public SurgeOfStrength(UUID ownerId, CardSetInfo setInfo) { Effect effect = new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn); effect.setText("Target creature gains trample"); this.getSpellAbility().addEffect(effect); - effect = new BoostTargetEffect(TargetConvertedManaCost.instance, StaticValue.get(0), Duration.EndOfTurn, true); - effect.setText("and gets +X/+0 until end of turn, where X is that creature's converted mana cost"); + effect = new BoostTargetEffect(TargetManaValue.instance, StaticValue.get(0), Duration.EndOfTurn, true); + effect.setText("and gets +X/+0 until end of turn, where X is that creature's mana value"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/cards/s/SurgeToVictory.java b/Mage.Sets/src/mage/cards/s/SurgeToVictory.java new file mode 100644 index 000000000000..3a38ec5c30cd --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SurgeToVictory.java @@ -0,0 +1,159 @@ +package mage.cards.s; + +import mage.ApprovingObject; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +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.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.DamagedEvent; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SurgeToVictory extends CardImpl { + + public SurgeToVictory(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{R}{R}"); + + // Exile target instant or sorcery card from your graveyard. Creatures you control get +X/+0 until end of turn, where X is that card's mana value. Whenever a creature you control deals combat damage to a player this turn, copy the exiled card. You may cast the copy without paying its mana cost. + this.getSpellAbility().addEffect(new SurgeToVictoryExileEffect()); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard( + StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY_FROM_YOUR_GRAVEYARD + )); + } + + private SurgeToVictory(final SurgeToVictory card) { + super(card); + } + + @Override + public SurgeToVictory copy() { + return new SurgeToVictory(this); + } +} + +class SurgeToVictoryExileEffect extends OneShotEffect { + + SurgeToVictoryExileEffect() { + super(Outcome.Benefit); + staticText = "exile target instant or sorcery card from your graveyard. " + + "Creatures you control get +X/+0 until end of turn, where X is that card's mana value. " + + "Whenever a creature you control deals combat damage to a player this turn, copy the exiled card. " + + "You may cast the copy without paying its mana cost"; + } + + private SurgeToVictoryExileEffect(final SurgeToVictoryExileEffect effect) { + super(effect); + } + + @Override + public SurgeToVictoryExileEffect copy() { + return new SurgeToVictoryExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(source.getFirstTarget()); + if (player == null || card == null) { + return false; + } + player.moveCards(card, Zone.EXILED, source, game); + game.addEffect(new BoostControlledEffect(card.getManaValue(), 0, Duration.EndOfTurn), source); + game.addDelayedTriggeredAbility(new SurgeToVictoryTriggeredAbility(card, game), source); + return true; + } +} + +class SurgeToVictoryTriggeredAbility extends DelayedTriggeredAbility { + + public SurgeToVictoryTriggeredAbility(Card card, Game game) { + super(new SurgeToVictoryCastEffect(card, game), Duration.EndOfTurn, false, false); + } + + private SurgeToVictoryTriggeredAbility(final SurgeToVictoryTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return ((DamagedEvent) event).isCombatDamage() + && isControlledBy(game.getControllerId(event.getSourceId())); + } + + @Override + public SurgeToVictoryTriggeredAbility copy() { + return new SurgeToVictoryTriggeredAbility(this); + } + + @Override + public String getRule() { + return "Whenever a creature you control deals combat damage to a player this turn, " + + "copy the exiled card. You may cast the copy without paying its mana cost."; + } +} + +class SurgeToVictoryCastEffect extends OneShotEffect { + + private final MageObjectReference mor; + + SurgeToVictoryCastEffect(Card card, Game game) { + super(Outcome.Benefit); + this.mor = new MageObjectReference(card, game); + } + + private SurgeToVictoryCastEffect(final SurgeToVictoryCastEffect effect) { + super(effect); + this.mor = effect.mor; + } + + @Override + public SurgeToVictoryCastEffect copy() { + return new SurgeToVictoryCastEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = mor.getCard(game); + if (player == null || card == null) { + return false; + } + + Card copiedCard = game.copyCard(card, source, player.getId()); + if (copiedCard == null) { + return false; + } + player.moveCards(copiedCard, Zone.EXILED, source, game); + if (!player.chooseUse(outcome, "Cast the copy of the exiled card?", source, game)) { + return false; + } + game.getState().setValue("PlayFromNotOwnHandZone" + copiedCard.getId(), Boolean.TRUE); + player.cast( + player.chooseAbilityForCast(copiedCard, game, true), + game, true, new ApprovingObject(source, game) + ); + game.getState().setValue("PlayFromNotOwnHandZone" + copiedCard.getId(), null); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SurgicalExtraction.java b/Mage.Sets/src/mage/cards/s/SurgicalExtraction.java index 7539c63d4ecb..64c3414dee02 100644 --- a/Mage.Sets/src/mage/cards/s/SurgicalExtraction.java +++ b/Mage.Sets/src/mage/cards/s/SurgicalExtraction.java @@ -59,7 +59,7 @@ public SurgicalExtractionEffect() { super(Outcome.Detriment); this.staticText = "Choose target card in a graveyard other than a basic land card. " + "Search its owner's graveyard, hand, and library for any number of cards " - + "with the same name as that card and exile them. Then that player shuffles their library"; + + "with the same name as that card and exile them. Then that player shuffles"; } public SurgicalExtractionEffect(final SurgicalExtractionEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SurrealMemoir.java b/Mage.Sets/src/mage/cards/s/SurrealMemoir.java index 84e5be4b40a4..d5ba7453b882 100644 --- a/Mage.Sets/src/mage/cards/s/SurrealMemoir.java +++ b/Mage.Sets/src/mage/cards/s/SurrealMemoir.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -23,8 +22,7 @@ public final class SurrealMemoir extends CardImpl { public SurrealMemoir(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{R}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}"); // Return an instant card at random from your graveyard to your hand. this.getSpellAbility().addEffect(new SurrealMemoirEffect()); @@ -59,16 +57,18 @@ public SurrealMemoirEffect copy() { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { FilterCard filter = new FilterCard("instant card"); filter.add(CardType.INSTANT.getPredicate()); - Card[] cards = player.getGraveyard().getCards(filter, game).toArray(new Card[0]); + Card[] cards = controller.getGraveyard().getCards(filter, game).toArray(new Card[0]); if (cards.length > 0) { Card card = cards[RandomUtil.nextInt(cards.length)]; - card.moveToZone(Zone.HAND, source, game, true); - game.informPlayers(card.getName() + "returned to the hand of" + player.getLogName()); - return true; + if (card != null + && controller.moveCards(card, Zone.HAND, source, game)) { + game.informPlayers(card.getName() + "returned to the hand of" + controller.getLogName()); + return true; + } } } return false; diff --git a/Mage.Sets/src/mage/cards/s/SurtlandFlinger.java b/Mage.Sets/src/mage/cards/s/SurtlandFlinger.java index d99cea6e6dfd..644ffecf6448 100644 --- a/Mage.Sets/src/mage/cards/s/SurtlandFlinger.java +++ b/Mage.Sets/src/mage/cards/s/SurtlandFlinger.java @@ -86,6 +86,9 @@ public boolean apply(Game game, Ability source) { power *= 2; } power = Math.max(power, 0); + if (!permanent.sacrifice(source, game)) { + return false; + } ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( new DamageTargetEffect(power), false, rule ); diff --git a/Mage.Sets/src/mage/cards/s/SurveyorsScope.java b/Mage.Sets/src/mage/cards/s/SurveyorsScope.java index e18407c34724..9f4961b095c1 100644 --- a/Mage.Sets/src/mage/cards/s/SurveyorsScope.java +++ b/Mage.Sets/src/mage/cards/s/SurveyorsScope.java @@ -49,7 +49,7 @@ class SurveyorsScopeEffect extends OneShotEffect { public SurveyorsScopeEffect() { super(Outcome.PutLandInPlay); - this.staticText = "Search your library for up to X basic land cards, where X is the number of players who control at least two more lands than you. Put those cards onto the battlefield, then shuffle your library"; + this.staticText = "Search your library for up to X basic land cards, where X is the number of players who control at least two more lands than you. Put those cards onto the battlefield, then shuffle"; } public SurveyorsScopeEffect(final SurveyorsScopeEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SwansOfBrynArgoll.java b/Mage.Sets/src/mage/cards/s/SwansOfBrynArgoll.java index f5880c5c329b..5b8bb2b7e1b8 100644 --- a/Mage.Sets/src/mage/cards/s/SwansOfBrynArgoll.java +++ b/Mage.Sets/src/mage/cards/s/SwansOfBrynArgoll.java @@ -119,7 +119,7 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { @Override public boolean applies(GameEvent event, Ability source, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE + return event.getType() == EventType.DAMAGE_PERMANENT && event.getTargetId().equals(source.getSourceId()); } diff --git a/Mage.Sets/src/mage/cards/s/SwayOfTheStars.java b/Mage.Sets/src/mage/cards/s/SwayOfTheStars.java index 001037336c9b..cc9085536897 100644 --- a/Mage.Sets/src/mage/cards/s/SwayOfTheStars.java +++ b/Mage.Sets/src/mage/cards/s/SwayOfTheStars.java @@ -14,7 +14,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/s/Swerve.java b/Mage.Sets/src/mage/cards/s/Swerve.java index 6d72521bdf7f..dd3907f82735 100644 --- a/Mage.Sets/src/mage/cards/s/Swerve.java +++ b/Mage.Sets/src/mage/cards/s/Swerve.java @@ -7,7 +7,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.NumberOfTargetsPredicate; +import mage.filter.predicate.other.NumberOfTargetsPredicate; import mage.target.TargetSpell; /** diff --git a/Mage.Sets/src/mage/cards/s/SwiftWarkite.java b/Mage.Sets/src/mage/cards/s/SwiftWarkite.java index fcd8fa7d4c30..cf410941e495 100644 --- a/Mage.Sets/src/mage/cards/s/SwiftWarkite.java +++ b/Mage.Sets/src/mage/cards/s/SwiftWarkite.java @@ -24,7 +24,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -65,16 +65,16 @@ public SwiftWarkite copy() { class SwiftWarkiteEffect extends OneShotEffect { - private static final FilterCard filter = new FilterCard("creature card with converted mana cost 3 or less from your hand or graveyard"); + private static final FilterCard filter = new FilterCard("creature card with mana value 3 or less from your hand or graveyard"); static { filter.add(CardType.CREATURE.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } SwiftWarkiteEffect() { super(Outcome.PutCardInPlay); - this.staticText = "you may put a creature card with converted mana cost 3 or less from your hand or graveyard onto the battlefield. That creature gains haste. Return it to your hand at the beginning of the next end step"; + this.staticText = "you may put a creature card with mana value 3 or less from your hand or graveyard onto the battlefield. That creature gains haste. Return it to your hand at the beginning of the next end step"; } SwiftWarkiteEffect(final SwiftWarkiteEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SwordOfKaldra.java b/Mage.Sets/src/mage/cards/s/SwordOfKaldra.java index 1a086e628075..6a0e04b51c09 100644 --- a/Mage.Sets/src/mage/cards/s/SwordOfKaldra.java +++ b/Mage.Sets/src/mage/cards/s/SwordOfKaldra.java @@ -1,11 +1,9 @@ package mage.cards.s; -import java.util.UUID; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.effects.common.continuous.BoostEquippedEffect; import mage.abilities.keyword.EquipAbility; @@ -14,18 +12,18 @@ import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class SwordOfKaldra extends CardImpl { public SwordOfKaldra(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.EQUIPMENT); @@ -64,7 +62,7 @@ public SwordOfKaldraTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override @@ -73,9 +71,7 @@ public boolean checkTrigger(GameEvent event, Game game) { if (equipment != null && equipment.getAttachedTo() != null && event.getSourceId().equals(equipment.getAttachedTo())) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); - } + getEffects().setTargetPointer(new FixedTarget(event.getTargetId())); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/s/SydriGalvanicGenius.java b/Mage.Sets/src/mage/cards/s/SydriGalvanicGenius.java index d6b92c3e1d09..9b4a4cd9beee 100644 --- a/Mage.Sets/src/mage/cards/s/SydriGalvanicGenius.java +++ b/Mage.Sets/src/mage/cards/s/SydriGalvanicGenius.java @@ -72,7 +72,7 @@ class SydriGalvanicGeniusEffect extends ContinuousEffectImpl { public SydriGalvanicGeniusEffect() { super(Duration.EndOfTurn, Outcome.BecomeCreature); - staticText = "Target noncreature artifact becomes an artifact creature with power and toughness each equal to its converted mana cost until end of turn"; + staticText = "Target noncreature artifact becomes an artifact creature with power and toughness each equal to its mana value until end of turn"; } public SydriGalvanicGeniusEffect(final SydriGalvanicGeniusEffect effect) { @@ -104,7 +104,7 @@ public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) case PTChangingEffects_7: if (sublayer == SubLayer.SetPT_7b) { - int cmc = artifact.getConvertedManaCost(); + int cmc = artifact.getManaValue(); artifact.getPower().setValue(cmc); artifact.getToughness().setValue(cmc); } diff --git a/Mage.Sets/src/mage/cards/s/SylvanEchoes.java b/Mage.Sets/src/mage/cards/s/SylvanEchoes.java index ea38df297b2d..f181a731563d 100644 --- a/Mage.Sets/src/mage/cards/s/SylvanEchoes.java +++ b/Mage.Sets/src/mage/cards/s/SylvanEchoes.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; @@ -11,14 +9,15 @@ import mage.game.Game; import mage.game.events.GameEvent; +import java.util.UUID; + /** - * * @author Styxo */ public final class SylvanEchoes extends CardImpl { public SylvanEchoes(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{G}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{G}"); // Whenever you clahs and you win, you may draw a card this.addAbility(new SylvanEchoesTriggeredAbility()); @@ -36,11 +35,11 @@ public SylvanEchoes copy() { class SylvanEchoesTriggeredAbility extends TriggeredAbilityImpl { - public SylvanEchoesTriggeredAbility() { + SylvanEchoesTriggeredAbility() { super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), true); } - public SylvanEchoesTriggeredAbility(final SylvanEchoesTriggeredAbility ability) { + private SylvanEchoesTriggeredAbility(final SylvanEchoesTriggeredAbility ability) { super(ability); } @@ -56,12 +55,11 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { - return event.getData().equals("controller") && event.getPlayerId().equals(getControllerId()) - || event.getData().equals("opponent") && event.getTargetId().equals(getControllerId()); + return isControlledBy(event.getPlayerId()) && event.getFlag(); } @Override public String getRule() { - return "Whenever you clash and you win, " + super.getRule(); + return "Whenever you clash and win, you may draw a card"; } } diff --git a/Mage.Sets/src/mage/cards/s/SylvanPrimordial.java b/Mage.Sets/src/mage/cards/s/SylvanPrimordial.java index dca7fe500576..5548badb030b 100644 --- a/Mage.Sets/src/mage/cards/s/SylvanPrimordial.java +++ b/Mage.Sets/src/mage/cards/s/SylvanPrimordial.java @@ -86,7 +86,7 @@ class SylvanPrimordialEffect extends OneShotEffect { public SylvanPrimordialEffect() { super(Outcome.DestroyPermanent); - this.staticText = "for each opponent, destroy target noncreature permanent that player controls. For each permanent destroyed this way, search your library for a Forest card and put that card onto the battlefield tapped. Then shuffle your library"; + this.staticText = "for each opponent, destroy target noncreature permanent that player controls. For each permanent destroyed this way, search your library for a Forest card and put that card onto the battlefield tapped. Then shuffle"; } public SylvanPrimordialEffect(final SylvanPrimordialEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SymmetrySage.java b/Mage.Sets/src/mage/cards/s/SymmetrySage.java new file mode 100644 index 000000000000..79b50b51f12e --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SymmetrySage.java @@ -0,0 +1,50 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.continuous.SetPowerToughnessTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SymmetrySage extends CardImpl { + + public SymmetrySage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(0); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, target creature you control has base power 2 until end of turn. + Ability ability = new MagecraftAbility(new SetPowerToughnessTargetEffect( + StaticValue.get(2), null, Duration.EndOfTurn + ).setText("target creature you control has base power 2 until end of turn")); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + private SymmetrySage(final SymmetrySage card) { + super(card); + } + + @Override + public SymmetrySage copy() { + return new SymmetrySage(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/Syncopate.java b/Mage.Sets/src/mage/cards/s/Syncopate.java index 848564de02dc..b206e221c134 100644 --- a/Mage.Sets/src/mage/cards/s/Syncopate.java +++ b/Mage.Sets/src/mage/cards/s/Syncopate.java @@ -1,23 +1,11 @@ package mage.cards.s; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.Mode; -import mage.abilities.costs.Cost; import mage.abilities.dynamicvalue.common.ManacostVariableValue; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.stack.Spell; -import mage.game.stack.StackObject; -import mage.players.Player; import mage.target.TargetSpell; -import mage.util.ManaUtil; import java.util.UUID; @@ -30,7 +18,7 @@ public Syncopate(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{U}"); // Counter target spell unless its controller pays {X}. If that spell is countered this way, exile it instead of putting it into its owner's graveyard. - this.getSpellAbility().addEffect(new SyncopateCounterUnlessPaysEffect()); + this.getSpellAbility().addEffect(new CounterUnlessPaysEffect(ManacostVariableValue.instance, true)); this.getSpellAbility().addTarget(new TargetSpell()); } @@ -43,50 +31,3 @@ public Syncopate copy() { return new Syncopate(this); } } - -class SyncopateCounterUnlessPaysEffect extends OneShotEffect { - - public SyncopateCounterUnlessPaysEffect() { - super(Outcome.Detriment); - } - - public SyncopateCounterUnlessPaysEffect(final SyncopateCounterUnlessPaysEffect effect) { - super(effect); - } - - @Override - public SyncopateCounterUnlessPaysEffect copy() { - return new SyncopateCounterUnlessPaysEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - StackObject spell = game.getStack().getStackObject(targetPointer.getFirst(game, source)); - MageObject sourceObject = source.getSourceObject(game); - if ((spell instanceof Spell) && sourceObject != null) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - // can be zero cost - Cost cost = ManaUtil.createManaCost(ManacostVariableValue.instance, game, source, this); - if (!cost.pay(source, game, source, spell.getControllerId(), false)) { - StackObject stackObject = game.getStack().getStackObject(source.getFirstTarget()); - if (stackObject != null && !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, source.getFirstTarget(), source, stackObject.getControllerId()))) { - game.informPlayers(sourceObject.getIdName() + ": cost wasn't payed - countering " + stackObject.getName()); - game.rememberLKI(source.getFirstTarget(), Zone.STACK, stackObject); - controller.moveCards((Spell) spell, Zone.EXILED, source, game); - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERED, source.getFirstTarget(), source, stackObject.getControllerId())); - return true; - } - return false; - } - } - } - return false; - } - - @Override - public String getText(Mode mode) { - return "Counter target spell unless its controller pays {X}. If that spell is countered this way, exile it instead of putting it into its owner's graveyard"; - } - -} diff --git a/Mage.Sets/src/mage/cards/s/SyrFarenTheHengehammer.java b/Mage.Sets/src/mage/cards/s/SyrFarenTheHengehammer.java index 85b8461de3d1..adfcc139e8f7 100644 --- a/Mage.Sets/src/mage/cards/s/SyrFarenTheHengehammer.java +++ b/Mage.Sets/src/mage/cards/s/SyrFarenTheHengehammer.java @@ -14,7 +14,7 @@ import mage.constants.SuperType; import mage.filter.FilterPermanent; import mage.filter.common.FilterAttackingCreature; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/t/TIEBomber.java b/Mage.Sets/src/mage/cards/t/TIEBomber.java index b977be83deda..72ef2bae0964 100644 --- a/Mage.Sets/src/mage/cards/t/TIEBomber.java +++ b/Mage.Sets/src/mage/cards/t/TIEBomber.java @@ -37,7 +37,7 @@ public TIEBomber(UUID ownerId, CardSetInfo setInfo) { new LoseAbilitySourceEffect(SpaceflightAbility.getInstance(), Duration.EndOfTurn), new GenericManaCost(1), new IsPhaseCondition(TurnPhase.COMBAT), - "{1}: {this} loses Spaceflight until end od turn. Activate this ability only during combat.")); + "{1}: {this} loses Spaceflight until end od turn. Activate only during combat.")); } private TIEBomber(final TIEBomber card) { diff --git a/Mage.Sets/src/mage/cards/t/TabletOfTheGuilds.java b/Mage.Sets/src/mage/cards/t/TabletOfTheGuilds.java index 31e67afdd425..2173e20e4557 100644 --- a/Mage.Sets/src/mage/cards/t/TabletOfTheGuilds.java +++ b/Mage.Sets/src/mage/cards/t/TabletOfTheGuilds.java @@ -14,6 +14,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.Spell; @@ -32,7 +33,7 @@ public TabletOfTheGuilds(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new AsEntersBattlefieldAbility(new TabletOfTheGuildsEntersBattlefieldEffect())); // Whenever you cast a spell, if it's at least one of the chosen colors, you gain 1 life for each of the chosen colors it is. - this.addAbility(new SpellCastControllerTriggeredAbility(new TabletOfTheGuildsGainLifeEffect(), new FilterSpell("a spell"), false, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new TabletOfTheGuildsGainLifeEffect(), StaticFilters.FILTER_SPELL_A, false, true)); } private TabletOfTheGuilds(final TabletOfTheGuilds card) { diff --git a/Mage.Sets/src/mage/cards/t/TaboraxHopesDemise.java b/Mage.Sets/src/mage/cards/t/TaboraxHopesDemise.java index 7c04df25e711..27009e243dd4 100644 --- a/Mage.Sets/src/mage/cards/t/TaboraxHopesDemise.java +++ b/Mage.Sets/src/mage/cards/t/TaboraxHopesDemise.java @@ -22,7 +22,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/t/TaigamOjutaiMaster.java b/Mage.Sets/src/mage/cards/t/TaigamOjutaiMaster.java index 2403ff9c5385..e35a5ac06267 100644 --- a/Mage.Sets/src/mage/cards/t/TaigamOjutaiMaster.java +++ b/Mage.Sets/src/mage/cards/t/TaigamOjutaiMaster.java @@ -96,7 +96,7 @@ public boolean checkTrigger(GameEvent event, Game game) { Spell spell = game.getStack().getSpell(event.getTargetId()); if (spell != null && spell.getFromZone() == Zone.HAND) { if (spell.getCard() != null - && spell.getCard().isInstant() || spell.getCard().isSorcery()) { + && spell.getCard().isInstantOrSorcery()) { for (Effect effect : getEffects()) { effect.setTargetPointer(new FixedTarget(spell.getId())); } diff --git a/Mage.Sets/src/mage/cards/t/TaintedPact.java b/Mage.Sets/src/mage/cards/t/TaintedPact.java index fd4c38f0c47e..5213e2460344 100644 --- a/Mage.Sets/src/mage/cards/t/TaintedPact.java +++ b/Mage.Sets/src/mage/cards/t/TaintedPact.java @@ -1,4 +1,3 @@ - package mage.cards.t; import java.util.HashSet; @@ -17,13 +16,13 @@ /** * - * @author cbt33, Ad Nauseum (North), Izzet Staticaster (LevelX2), Bane Alley Broker (LevelX2) + * @author cbt33, Ad Nauseum (North), Izzet Staticaster (LevelX2), Bane Alley + * Broker (LevelX2) */ public final class TaintedPact extends CardImpl { public TaintedPact(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{B}"); // Exile the top card of your library. You may put that card into your hand unless it has the same name as another card exiled this way. Repeat this process until you put a card into your hand or you exile two cards with the same name, whichever comes first. this.getSpellAbility().addEffect(new TaintedPactEffect()); @@ -39,9 +38,9 @@ public TaintedPact copy() { } } -class TaintedPactEffect extends OneShotEffect{ - - public TaintedPactEffect() { +class TaintedPactEffect extends OneShotEffect { + + public TaintedPactEffect() { super(Outcome.DrawCard); this.staticText = "Exile the top card of your library. You may put that card into your hand unless it has the same name as another card exiled this way. Repeat this process until you put a card into your hand or you exile two cards with the same name, whichever comes first"; } @@ -54,28 +53,29 @@ public TaintedPactEffect(final TaintedPactEffect effect) { public TaintedPactEffect copy() { return new TaintedPactEffect(this); } - + @Override - public boolean apply(Game game, Ability source) { - Card sourceCard = game.getCard(source.getSourceId()); - Player player = game.getPlayer(source.getControllerId()); - if (player == null || sourceCard == null) { + public boolean apply(Game game, Ability source) { + Card sourceCard = game.getCard(source.getSourceId()); + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null + || sourceCard == null) { return false; } Set names = new HashSet<>(); - while (player.canRespond() && player.getLibrary().hasCards()) { - Card card = player.getLibrary().getFromTop(game); + while (controller.canRespond() + && controller.getLibrary().hasCards()) { + Card card = controller.getLibrary().getFromTop(game); if (card != null) { - - card.moveToExile(null, null, source, game); + controller.moveCards(card, Zone.EXILED, source, game); // Checks if there was already exiled a card with the same name if (names.contains(card.getName())) { break; } names.add(card.getName()); - if (player.chooseUse(outcome, "Put " + card.getName() + " into your hand?", source, game)) { - //Adds the current card to hand if it is chosen. - card.moveToZone(Zone.HAND, source, game, true); + if (controller.chooseUse(outcome, "Put " + card.getName() + " into your hand?", source, game)) { + //Adds the current card to hand if it is chosen. + controller.moveCards(card, Zone.HAND, source, game); break; } } diff --git a/Mage.Sets/src/mage/cards/t/TajNarSwordsmith.java b/Mage.Sets/src/mage/cards/t/TajNarSwordsmith.java index cc070a822ede..184b912135bd 100644 --- a/Mage.Sets/src/mage/cards/t/TajNarSwordsmith.java +++ b/Mage.Sets/src/mage/cards/t/TajNarSwordsmith.java @@ -12,7 +12,7 @@ import mage.constants.Outcome; import mage.constants.SubType; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; @@ -50,7 +50,7 @@ class TajNarSwordsmithEffect extends OneShotEffect { TajNarSwordsmithEffect() { super(Outcome.Benefit); - this.staticText = "you may pay {X}. If you do, search your library for an Equipment card with converted mana cost X or less and put that card onto the battlefield. Then shuffle your library"; + this.staticText = "you may pay {X}. If you do, search your library for an Equipment card with mana value X or less and put that card onto the battlefield. Then shuffle"; } TajNarSwordsmithEffect(final TajNarSwordsmithEffect effect) { @@ -68,9 +68,9 @@ public boolean apply(Game game, Ability source) { if (player != null && player.chooseUse(Outcome.Benefit, "Do you want to pay {X} to search and put Equipment?", source, game)) { // can be zero int payCount = ManaUtil.playerPaysXGenericMana(true, "Taj-Nar Swordsmith", player, source, game); - FilterCard filter = new FilterCard("Equipment card with converted mana cost {" + payCount + "} or less"); + FilterCard filter = new FilterCard("Equipment card with mana value {" + payCount + "} or less"); filter.add(SubType.EQUIPMENT.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, payCount + 1)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, payCount + 1)); new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 1, filter), false, true).apply(game, source); return true; } diff --git a/Mage.Sets/src/mage/cards/t/TajicLegionsEdge.java b/Mage.Sets/src/mage/cards/t/TajicLegionsEdge.java index dbbce78adcb3..6f4eb03c20d1 100644 --- a/Mage.Sets/src/mage/cards/t/TajicLegionsEdge.java +++ b/Mage.Sets/src/mage/cards/t/TajicLegionsEdge.java @@ -19,7 +19,7 @@ import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/t/TajuruParagon.java b/Mage.Sets/src/mage/cards/t/TajuruParagon.java index b0f326514957..33f030c52556 100644 --- a/Mage.Sets/src/mage/cards/t/TajuruParagon.java +++ b/Mage.Sets/src/mage/cards/t/TajuruParagon.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.permanent.SharesCreatureTypePredicate; +import mage.filter.predicate.mageobject.SharesCreatureTypePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/t/Tamanoa.java b/Mage.Sets/src/mage/cards/t/Tamanoa.java index 5390e72df5be..4712488b9017 100644 --- a/Mage.Sets/src/mage/cards/t/Tamanoa.java +++ b/Mage.Sets/src/mage/cards/t/Tamanoa.java @@ -63,9 +63,8 @@ public TamanoaDealsDamageTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE - || event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT + || event.getType() == GameEvent.EventType.DAMAGED_PLAYER; } @Override diff --git a/Mage.Sets/src/mage/cards/t/TamiyosJournal.java b/Mage.Sets/src/mage/cards/t/TamiyosJournal.java index 354892deb4e1..4a20c0daefed 100644 --- a/Mage.Sets/src/mage/cards/t/TamiyosJournal.java +++ b/Mage.Sets/src/mage/cards/t/TamiyosJournal.java @@ -22,7 +22,7 @@ */ public final class TamiyosJournal extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("three Clues"); + private static final FilterControlledPermanent filter = new FilterControlledPermanent("Clues"); static { filter.add(SubType.CLUE.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/t/TanazirQuandrix.java b/Mage.Sets/src/mage/cards/t/TanazirQuandrix.java new file mode 100644 index 000000000000..88058f09adc3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TanazirQuandrix.java @@ -0,0 +1,97 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount; +import mage.abilities.dynamicvalue.common.SourcePermanentToughnessValue; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.SetPowerToughnessAllEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TanazirQuandrix extends CardImpl { + + private static final DynamicValue xValue = new SourcePermanentPowerCount(true); + + public TanazirQuandrix(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ELDER); + this.subtype.add(SubType.DRAGON); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // When Tanazir Quandrix enters the battlefield, double the number of +1/+1 counters on target creature you control. + Ability ability = new EntersBattlefieldTriggeredAbility(new TanazirQuandrixEffect()); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + + // Whenever Tanazir Quandrix attacks, you may have the base power and toughness of other creatures you control become equal to Tanazir Quandrix's power and toughness until end of turn. + this.addAbility(new AttacksTriggeredAbility(new SetPowerToughnessAllEffect( + xValue, SourcePermanentToughnessValue.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_CONTROLLED_CREATURE, true + ).setText("have the base power and toughness of other creatures you control " + + "become equal to {this}'s power and toughness until end of turn"), true)); + } + + private TanazirQuandrix(final TanazirQuandrix card) { + super(card); + } + + @Override + public TanazirQuandrix copy() { + return new TanazirQuandrix(this); + } +} + +class TanazirQuandrixEffect extends OneShotEffect { + + TanazirQuandrixEffect() { + super(Outcome.Benefit); + staticText = "double the number of +1/+1 counters on target creature you control"; + } + + private TanazirQuandrixEffect(final TanazirQuandrixEffect effect) { + super(effect); + } + + @Override + public TanazirQuandrixEffect copy() { + return new TanazirQuandrixEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + int counterCount = permanent.getCounters(game).getCount(CounterType.P1P1); + return counterCount > 0 && permanent.addCounters( + CounterType.P1P1.createInstance(counterCount), source.getControllerId(), source, game + ); + } +} diff --git a/Mage.Sets/src/mage/cards/t/Tangletrap.java b/Mage.Sets/src/mage/cards/t/Tangletrap.java new file mode 100644 index 000000000000..b053c3a75c6d --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/Tangletrap.java @@ -0,0 +1,51 @@ +package mage.cards.t; + +import mage.abilities.Mode; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.TargetPermanent; +import mage.target.common.TargetArtifactPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Tangletrap extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("creature with flying"); + + static { + filter.add(new AbilityPredicate(FlyingAbility.class)); + } + + public Tangletrap(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); + + // Choose one — + // • Tangletrap deals 5 damage to target creature with flying. + this.getSpellAbility().addEffect(new DamageTargetEffect(5)); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + + // • Destroy target artifact. + Mode mode = new Mode(new DestroyTargetEffect()); + mode.addTarget(new TargetArtifactPermanent()); + this.getSpellAbility().addMode(mode); + } + + private Tangletrap(final Tangletrap card) { + super(card); + } + + @Override + public Tangletrap copy() { + return new Tangletrap(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TaranikaAkroanVeteran.java b/Mage.Sets/src/mage/cards/t/TaranikaAkroanVeteran.java index 0dcd4d2fb613..f1f4e9ee4b24 100644 --- a/Mage.Sets/src/mage/cards/t/TaranikaAkroanVeteran.java +++ b/Mage.Sets/src/mage/cards/t/TaranikaAkroanVeteran.java @@ -16,7 +16,7 @@ import mage.constants.SuperType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/t/Tariff.java b/Mage.Sets/src/mage/cards/t/Tariff.java index 21d02bca5d5a..1eb0f2795142 100644 --- a/Mage.Sets/src/mage/cards/t/Tariff.java +++ b/Mage.Sets/src/mage/cards/t/Tariff.java @@ -51,7 +51,7 @@ class TariffEffect extends OneShotEffect { public TariffEffect() { super(Outcome.DestroyPermanent); - this.staticText = "Each player sacrifices the creature they control with the highest converted mana cost unless they pay that creature's mana cost. If two or more creatures a player controls are tied for highest cost, that player chooses one."; + this.staticText = "Each player sacrifices the creature they control with the highest mana value unless they pay that creature's mana cost. If two or more creatures a player controls are tied for highest cost, that player chooses one."; } public TariffEffect(final TariffEffect effect) { @@ -103,13 +103,13 @@ private List getPermanentsWithTheHighestCMC(Game game, UUID playerId, List permanents = game.getBattlefield().getAllActivePermanents(filter, playerId, game); int highestCMC = -1; for (Permanent permanent : permanents) { - if (highestCMC < permanent.getConvertedManaCost()) { - highestCMC = permanent.getConvertedManaCost(); + if (highestCMC < permanent.getManaValue()) { + highestCMC = permanent.getManaValue(); } } List result = new ArrayList<>(); for (Permanent permanent : permanents) { - if (permanent.getConvertedManaCost() == highestCMC) { + if (permanent.getManaValue() == highestCMC) { result.add(permanent); } } diff --git a/Mage.Sets/src/mage/cards/t/TasigurTheGoldenFang.java b/Mage.Sets/src/mage/cards/t/TasigurTheGoldenFang.java index b0c56f90fcd8..ff96c8e49967 100644 --- a/Mage.Sets/src/mage/cards/t/TasigurTheGoldenFang.java +++ b/Mage.Sets/src/mage/cards/t/TasigurTheGoldenFang.java @@ -12,7 +12,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterNonlandCard; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.players.Player; import mage.target.Target; diff --git a/Mage.Sets/src/mage/cards/t/TawnosUrzasApprentice.java b/Mage.Sets/src/mage/cards/t/TawnosUrzasApprentice.java index 1f6987b9c9a8..df79e5c7d40e 100644 --- a/Mage.Sets/src/mage/cards/t/TawnosUrzasApprentice.java +++ b/Mage.Sets/src/mage/cards/t/TawnosUrzasApprentice.java @@ -17,7 +17,7 @@ import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterStackObject; -import mage.filter.predicate.ability.ArtifactSourcePredicate; +import mage.filter.predicate.other.ArtifactSourcePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.StackAbility; @@ -85,7 +85,6 @@ public boolean apply(Game game, Ability source) { Permanent sourcePermanent = game.getPermanent(source.getSourceId()); if (controller != null && sourcePermanent != null) { stackAbility.createCopyOnStack(game, source, source.getControllerId(), true); - game.informPlayers(sourcePermanent.getIdName() + ": " + controller.getLogName() + " copied an ability"); return true; } } diff --git a/Mage.Sets/src/mage/cards/t/TayamLuminousEnigma.java b/Mage.Sets/src/mage/cards/t/TayamLuminousEnigma.java index 7f2977ecff8d..7f3b35592dd9 100644 --- a/Mage.Sets/src/mage/cards/t/TayamLuminousEnigma.java +++ b/Mage.Sets/src/mage/cards/t/TayamLuminousEnigma.java @@ -20,7 +20,7 @@ import mage.filter.FilterCard; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.filter.predicate.permanent.CounterAnyPredicate; import mage.game.Game; import mage.game.events.EntersTheBattlefieldEvent; @@ -160,15 +160,15 @@ public String getText() { class TayamLuminousEnigmaEffect extends OneShotEffect { - private static final FilterCard filter = new FilterPermanentCard("permanent card in your graveyard with converted mana cost 3 or less"); + private static final FilterCard filter = new FilterPermanentCard("permanent card in your graveyard with mana value 3 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } TayamLuminousEnigmaEffect() { super(Outcome.Benefit); - staticText = ", then return a permanent card with converted mana cost 3 or less from your graveyard to the battlefield"; + staticText = ", then return a permanent card with mana value 3 or less from your graveyard to the battlefield"; } private TayamLuminousEnigmaEffect(TayamLuminousEnigmaEffect effect) { diff --git a/Mage.Sets/src/mage/cards/t/TeachByExample.java b/Mage.Sets/src/mage/cards/t/TeachByExample.java new file mode 100644 index 000000000000..5aceb1021b32 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TeachByExample.java @@ -0,0 +1,81 @@ +package mage.cards.t; + +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.effects.common.CopyTargetSpellEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TeachByExample extends CardImpl { + + public TeachByExample(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U/R}{U/R}"); + + // When you cast your next instant or sorcery spell this turn, copy that spell. You may choose new targets for the copy. + this.getSpellAbility().addEffect( + new CreateDelayedTriggeredAbilityEffect(new TeachByExampleAbility()) + .setText("When you cast your next instant or sorcery spell this turn, " + + "copy that spell. You may choose new targets for the copy") + ); + } + + private TeachByExample(final TeachByExample card) { + super(card); + } + + @Override + public TeachByExample copy() { + return new TeachByExample(this); + } +} + +class TeachByExampleAbility extends DelayedTriggeredAbility { + + TeachByExampleAbility() { + super(new CopyTargetSpellEffect(true), Duration.EndOfTurn); + } + + private TeachByExampleAbility(final TeachByExampleAbility ability) { + super(ability); + } + + @Override + public TeachByExampleAbility copy() { + return new TeachByExampleAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!isControlledBy(event.getPlayerId())) { + return false; + } + Spell spell = game.getStack().getSpell(event.getTargetId()); + if (spell == null || !spell.isInstantOrSorcery()) { + return false; + } + this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId())); + return true; + } + + @Override + public String getRule() { + return "When you cast your next instant or sorcery spell this turn, " + + "copy that spell. You may choose new targets for the copy."; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TeachingsOfTheArchaics.java b/Mage.Sets/src/mage/cards/t/TeachingsOfTheArchaics.java new file mode 100644 index 000000000000..e87db4b3e5cd --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TeachingsOfTheArchaics.java @@ -0,0 +1,79 @@ +package mage.cards.t; + +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.constants.SubType; +import mage.game.Game; +import mage.players.Player; + +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TeachingsOfTheArchaics extends CardImpl { + + public TeachingsOfTheArchaics(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}"); + + this.subtype.add(SubType.LESSON); + + // If an opponent has more cards in hand than you, draw two cards. Draw three cards instead if an opponent has at least four more cards in hand than you. + this.getSpellAbility().addEffect(new TeachingsOfTheArchaicsEffect()); + } + + private TeachingsOfTheArchaics(final TeachingsOfTheArchaics card) { + super(card); + } + + @Override + public TeachingsOfTheArchaics copy() { + return new TeachingsOfTheArchaics(this); + } +} + +class TeachingsOfTheArchaicsEffect extends OneShotEffect { + + TeachingsOfTheArchaicsEffect() { + super(Outcome.Benefit); + staticText = "if an opponent has more cards in hand than you, draw two cards. " + + "Draw three cards instead if an opponent has at least four more cards in hand than you"; + } + + private TeachingsOfTheArchaicsEffect(final TeachingsOfTheArchaicsEffect effect) { + super(effect); + } + + @Override + public TeachingsOfTheArchaicsEffect copy() { + return new TeachingsOfTheArchaicsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + int diff = game + .getOpponents(source.getControllerId()) + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .map(Player::getHand) + .mapToInt(Set::size) + .max() + .orElse(0); + diff -= player.getHand().size(); + if (diff <= 0) { + return false; + } + return player.drawCards(diff >= 4 ? 3 : 2, source, game) > 0; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TeamPennant.java b/Mage.Sets/src/mage/cards/t/TeamPennant.java new file mode 100644 index 000000000000..2cf9b07a77a9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TeamPennant.java @@ -0,0 +1,67 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TeamPennant extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("creature token"); + + static { + filter.add(TokenPredicate.instance); + } + + public TeamPennant(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Equipped creature gets +1/+1 and has vigilance and trample. + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(1, 1)); + ability.addEffect(new GainAbilityAttachedEffect( + VigilanceAbility.getInstance(), AttachmentType.EQUIPMENT + ).setText("and has vigilance")); + ability.addEffect(new GainAbilityAttachedEffect( + TrampleAbility.getInstance(), AttachmentType.EQUIPMENT + ).setText("and trample")); + this.addAbility(ability); + + // Equip creature token {1} + this.addAbility(new EquipAbility( + Outcome.BoostCreature, new GenericManaCost(1), new TargetPermanent(filter) + )); + + // Equip {3} + this.addAbility(new EquipAbility(3)); + } + + private TeamPennant(final TeamPennant card) { + super(card); + } + + @Override + public TeamPennant copy() { + return new TeamPennant(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TectonicEdge.java b/Mage.Sets/src/mage/cards/t/TectonicEdge.java index bc0be33e6b53..7169132472b7 100644 --- a/Mage.Sets/src/mage/cards/t/TectonicEdge.java +++ b/Mage.Sets/src/mage/cards/t/TectonicEdge.java @@ -64,7 +64,7 @@ class TectonicEdgeCost extends CostImpl { public TectonicEdgeCost() { - this.text = "Activate this ability only if an opponent controls four or more lands"; + this.text = "Activate only if an opponent controls four or more lands"; } public TectonicEdgeCost(final TectonicEdgeCost cost) { diff --git a/Mage.Sets/src/mage/cards/t/TeferisProtection.java b/Mage.Sets/src/mage/cards/t/TeferisProtection.java index d59f62a546a6..42dc0d65b05f 100644 --- a/Mage.Sets/src/mage/cards/t/TeferisProtection.java +++ b/Mage.Sets/src/mage/cards/t/TeferisProtection.java @@ -39,7 +39,7 @@ public TeferisProtection(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new TeferisProtectionPhaseOutEffect()); // Exile Teferi's Protection. - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private TeferisProtection(final TeferisProtection card) { @@ -78,7 +78,7 @@ class TeferisProtectionEffect extends OneShotEffect { */ public TeferisProtectionEffect() { super(Outcome.Protect); - staticText = ", and you gain protection from everything"; + staticText = " and you gain protection from everything"; } public TeferisProtectionEffect(final TeferisProtectionEffect effect) { @@ -126,7 +126,7 @@ class TeferisProtectionPhaseOutEffect extends OneShotEffect { public TeferisProtectionPhaseOutEffect() { super(Outcome.Benefit); - this.staticText = "All permanents you control phase out. (While they're phased out, they're treated as though they don't exist. They phase in before you untap during your untap step.)"; + this.staticText = "All permanents you control phase out. (While they're phased out, they're treated as though they don't exist. They phase in before you untap during your untap step.)
"; } public TeferisProtectionPhaseOutEffect(final TeferisProtectionPhaseOutEffect effect) { diff --git a/Mage.Sets/src/mage/cards/t/TeferisResponse.java b/Mage.Sets/src/mage/cards/t/TeferisResponse.java index 21868a6633e5..a4b2150844a1 100644 --- a/Mage.Sets/src/mage/cards/t/TeferisResponse.java +++ b/Mage.Sets/src/mage/cards/t/TeferisResponse.java @@ -11,7 +11,7 @@ import mage.constants.Outcome; import mage.filter.FilterStackObject; import mage.filter.common.FilterControlledLandPermanent; -import mage.filter.predicate.other.TargetsPermanentPredicate; +import mage.filter.predicate.mageobject.TargetsPermanentPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.StackObject; diff --git a/Mage.Sets/src/mage/cards/t/TempleAltisaur.java b/Mage.Sets/src/mage/cards/t/TempleAltisaur.java index 7ca73f5db868..5ee3f4ab5b51 100644 --- a/Mage.Sets/src/mage/cards/t/TempleAltisaur.java +++ b/Mage.Sets/src/mage/cards/t/TempleAltisaur.java @@ -72,7 +72,7 @@ public boolean apply(Game game, Ability source) { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.DAMAGE_CREATURE) { + if (event.getType() == GameEvent.EventType.DAMAGE_PERMANENT) { Permanent permanent = game.getPermanent(event.getTargetId()); if (permanent != null && !permanent.getId().equals(source.getSourceId()) diff --git a/Mage.Sets/src/mage/cards/t/TempleThief.java b/Mage.Sets/src/mage/cards/t/TempleThief.java index 9bac89047fd8..ecc1d0ead6f5 100644 --- a/Mage.Sets/src/mage/cards/t/TempleThief.java +++ b/Mage.Sets/src/mage/cards/t/TempleThief.java @@ -9,7 +9,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.EnchantmentOrEnchantedPredicate; +import mage.filter.predicate.permanent.EnchantmentOrEnchantedPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/t/TemporalMastery.java b/Mage.Sets/src/mage/cards/t/TemporalMastery.java index 987265f1388f..444cfff28ff6 100644 --- a/Mage.Sets/src/mage/cards/t/TemporalMastery.java +++ b/Mage.Sets/src/mage/cards/t/TemporalMastery.java @@ -21,7 +21,7 @@ public TemporalMastery(UUID ownerId, CardSetInfo setInfo) { // Take an extra turn after this one. Exile Temporal Mastery. this.getSpellAbility().addEffect(new AddExtraTurnControllerEffect()); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); // Miracle {1}{U} this.addAbility(new MiracleAbility(this, new ManaCostsImpl("{1}{U}"))); diff --git a/Mage.Sets/src/mage/cards/t/TemporalTrespass.java b/Mage.Sets/src/mage/cards/t/TemporalTrespass.java index ab2e83cf921a..d493616320d2 100644 --- a/Mage.Sets/src/mage/cards/t/TemporalTrespass.java +++ b/Mage.Sets/src/mage/cards/t/TemporalTrespass.java @@ -22,7 +22,7 @@ public TemporalTrespass(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new DelveAbility()); // Take an extra turn after this one. Exile Temporal Trespass. this.getSpellAbility().addEffect(new AddExtraTurnControllerEffect()); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private TemporalTrespass(final TemporalTrespass card) { diff --git a/Mage.Sets/src/mage/cards/t/TemptWithDiscovery.java b/Mage.Sets/src/mage/cards/t/TemptWithDiscovery.java index f7ef5ae1b58b..cb2538f5cb94 100644 --- a/Mage.Sets/src/mage/cards/t/TemptWithDiscovery.java +++ b/Mage.Sets/src/mage/cards/t/TemptWithDiscovery.java @@ -47,7 +47,7 @@ class TemptWithDiscoveryEffect extends OneShotEffect { public TemptWithDiscoveryEffect() { super(Outcome.PutLandInPlay); - this.staticText = "Tempting offer — Search your library for a land card and put it onto the battlefield. Each opponent may search their library for a land card and put it onto the battlefield. For each opponent who searches a library this way, search your library for a land card and put it onto the battlefield. Then each player who searched a library this way shuffles it"; + this.staticText = "Tempting offer — Search your library for a land card and put it onto the battlefield. Each opponent may search their library for a land card and put it onto the battlefield. For each opponent who searches a library this way, search your library for a land card and put it onto the battlefield. Then each player who searched a library this way shuffles"; } public TemptWithDiscoveryEffect(final TemptWithDiscoveryEffect effect) { diff --git a/Mage.Sets/src/mage/cards/t/TemptWithImmortality.java b/Mage.Sets/src/mage/cards/t/TemptWithImmortality.java index b70e273cfb8e..fb7dfcc85ec0 100644 --- a/Mage.Sets/src/mage/cards/t/TemptWithImmortality.java +++ b/Mage.Sets/src/mage/cards/t/TemptWithImmortality.java @@ -12,7 +12,7 @@ import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.players.Player; import mage.target.Target; diff --git a/Mage.Sets/src/mage/cards/t/TemptedByTheOriq.java b/Mage.Sets/src/mage/cards/t/TemptedByTheOriq.java new file mode 100644 index 000000000000..8eff4f25d7c9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TemptedByTheOriq.java @@ -0,0 +1,67 @@ +package mage.cards.t; + +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.ComparisonType; +import mage.constants.Duration; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; +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.targetadjustment.TargetAdjuster; +import mage.target.targetpointer.EachTargetPointer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TemptedByTheOriq extends CardImpl { + + public TemptedByTheOriq(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{U}{U}{U}"); + + // For each opponent, gain control of up to one target creature or planeswalker that player controls with mana value 3 or less. + this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.Custom, true) + .setTargetPointer(new EachTargetPointer()) + .setText("for each opponent, gain control of up to one target creature " + + "or planeswalker that player controls with mana value 3 or less")); + this.getSpellAbility().setTargetAdjuster(TemptedByTheOriqAdjuster.instance); + } + + private TemptedByTheOriq(final TemptedByTheOriq card) { + super(card); + } + + @Override + public TemptedByTheOriq copy() { + return new TemptedByTheOriq(this); + } +} + +enum TemptedByTheOriqAdjuster 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 FilterCreatureOrPlaneswalkerPermanent( + "creature or planeswalker " + opponent.getName() + " controls with mana value 3 or less" + ); + filter.add(new ControllerIdPredicate(opponentId)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); + ability.addTarget(new TargetPermanent(0, 1, filter, false)); + } + } +} diff --git a/Mage.Sets/src/mage/cards/t/TemptingContract.java b/Mage.Sets/src/mage/cards/t/TemptingContract.java new file mode 100644 index 000000000000..1cbce2362de3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TemptingContract.java @@ -0,0 +1,81 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.game.Game; +import mage.game.permanent.token.TreasureToken; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TemptingContract extends CardImpl { + + public TemptingContract(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); + + // At the beginning of your upkeep, each opponent may create a Treasure token. For each opponent who does, you create a Treasure token. + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new TemptingContractEffect(), TargetController.YOU, false + )); + } + + private TemptingContract(final TemptingContract card) { + super(card); + } + + @Override + public TemptingContract copy() { + return new TemptingContract(this); + } +} + +class TemptingContractEffect extends OneShotEffect { + + TemptingContractEffect() { + super(Outcome.Benefit); + staticText = "each opponent may create a Treasure token. " + + "For each opponent who does, you create a Treasure token"; + } + + private TemptingContractEffect(final TemptingContractEffect effect) { + super(effect); + } + + @Override + public TemptingContractEffect copy() { + return new TemptingContractEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + int counter = 0; + for (UUID playerId : game.getOpponents(source.getControllerId())) { + Player opponent = game.getPlayer(playerId); + if (opponent != null && opponent.chooseUse( + outcome, "Create a Treasure token?", + "If you do, " + controller.getName() + " will create one as well", + "Yes", "No", source, game + ) && new TreasureToken().putOntoBattlefield(1, game, source, opponent.getId())) { + counter++; + } + } + if (counter > 0) { + new TreasureToken().putOntoBattlefield(counter, game, source, source.getControllerId()); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TemurAscendancy.java b/Mage.Sets/src/mage/cards/t/TemurAscendancy.java index 808d0fda8c84..e2a327fa73e9 100644 --- a/Mage.Sets/src/mage/cards/t/TemurAscendancy.java +++ b/Mage.Sets/src/mage/cards/t/TemurAscendancy.java @@ -23,7 +23,7 @@ */ public final class TemurAscendancy extends CardImpl { - final private static FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with power 4 or greater"); + final private static FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature with power 4 or greater"); static { filter.add(new PowerPredicate(ComparisonType.MORE_THAN, 3)); } diff --git a/Mage.Sets/src/mage/cards/t/TemurSabertooth.java b/Mage.Sets/src/mage/cards/t/TemurSabertooth.java index de2ca63dd067..2f8058a3b60f 100644 --- a/Mage.Sets/src/mage/cards/t/TemurSabertooth.java +++ b/Mage.Sets/src/mage/cards/t/TemurSabertooth.java @@ -17,7 +17,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/t/TendThePests.java b/Mage.Sets/src/mage/cards/t/TendThePests.java new file mode 100644 index 000000000000..4aa547703101 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TendThePests.java @@ -0,0 +1,43 @@ +package mage.cards.t; + +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.dynamicvalue.common.SacrificeCostCreaturesPower; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.game.permanent.token.WitherbloomToken; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TendThePests extends CardImpl { + + public TendThePests(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}{G}"); + + // As an additional cost to cast this spell, sacrifice a creature. + this.getSpellAbility().addCost(new SacrificeTargetCost( + new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT) + )); + + // Create X 1/1 black and green Pest creature tokens with "When this creature dies, you gain 1 life," where X is the sacrificed creature's power. + this.getSpellAbility().addEffect(new CreateTokenEffect( + new WitherbloomToken(), SacrificeCostCreaturesPower.instance + ).setText("create X 1/1 black and green Pest creature tokens with " + + "\"When this creature dies, you gain 1 life,\" where X is the sacrificed creature's power")); + } + + private TendThePests(final TendThePests card) { + super(card); + } + + @Override + public TendThePests copy() { + return new TendThePests(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TendrilsOfAgony.java b/Mage.Sets/src/mage/cards/t/TendrilsOfAgony.java index be851a757972..c4fc4d3b7fee 100644 --- a/Mage.Sets/src/mage/cards/t/TendrilsOfAgony.java +++ b/Mage.Sets/src/mage/cards/t/TendrilsOfAgony.java @@ -22,7 +22,7 @@ public TendrilsOfAgony(UUID ownerId, CardSetInfo setInfo) { // Target player loses 2 life and you gain 2 life. this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addEffect(new LoseLifeTargetEffect(2)); - this.getSpellAbility().addEffect(new GainLifeEffect(2)); + this.getSpellAbility().addEffect(new GainLifeEffect(2).concatBy("and")); // Storm (When you cast this spell, copy it for each spell cast before it this turn. You may choose new targets for the copies.) this.addAbility(new StormAbility()); } diff --git a/Mage.Sets/src/mage/cards/t/TendrilsOfCorruption.java b/Mage.Sets/src/mage/cards/t/TendrilsOfCorruption.java index c38f979b1ff5..0a769e7c42f6 100644 --- a/Mage.Sets/src/mage/cards/t/TendrilsOfCorruption.java +++ b/Mage.Sets/src/mage/cards/t/TendrilsOfCorruption.java @@ -1,7 +1,6 @@ - package mage.cards.t; -import java.util.UUID; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.GainLifeEffect; @@ -9,27 +8,33 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author Loki */ public final class TendrilsOfCorruption extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("Swamp you control"); + private static final FilterPermanent filter = new FilterControlledPermanent(); static { filter.add(SubType.SWAMP.getPredicate()); } + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + public TendrilsOfCorruption(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{B}"); - this.getSpellAbility().addEffect(new DamageTargetEffect(new PermanentsOnBattlefieldCount(filter))); + this.getSpellAbility().addEffect(new DamageTargetEffect(xValue) + .setText("{this} deals X damage to target creature")); + this.getSpellAbility().addEffect(new GainLifeEffect(xValue) + .setText("and you gain X life, where X is the number of Swamps you control")); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - this.getSpellAbility().addEffect(new GainLifeEffect(new PermanentsOnBattlefieldCount(filter))); } private TendrilsOfCorruption(final TendrilsOfCorruption card) { diff --git a/Mage.Sets/src/mage/cards/t/TenthDistrictVeteran.java b/Mage.Sets/src/mage/cards/t/TenthDistrictVeteran.java index 820e9e57b3be..3ad5eafe4346 100644 --- a/Mage.Sets/src/mage/cards/t/TenthDistrictVeteran.java +++ b/Mage.Sets/src/mage/cards/t/TenthDistrictVeteran.java @@ -10,7 +10,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/t/TenuredInkcaster.java b/Mage.Sets/src/mage/cards/t/TenuredInkcaster.java new file mode 100644 index 000000000000..214c24671640 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TenuredInkcaster.java @@ -0,0 +1,67 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksAllTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SetTargetPointer; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.filter.common.FilterCreaturePermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TenuredInkcaster extends CardImpl { + + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("creature you control with a +1/+1 counter on it"); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + filter.add(CounterType.P1P1.getPredicate()); + } + + public TenuredInkcaster(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); + + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // When Tenured Inkcaster enters the battlefield, put a +1/+1 counter on target creature. + Ability ability = new EntersBattlefieldTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()) + ); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + + // Whenever a creature you control with a +1/+1 counter on it attacks, each opponent loses 1 life and you gain 1 life. + ability = new AttacksAllTriggeredAbility( + new LoseLifeOpponentsEffect(1), false, + filter, SetTargetPointer.NONE, false + ); + ability.addEffect(new GainLifeEffect(1).concatBy("and")); + this.addAbility(ability); + } + + private TenuredInkcaster(final TenuredInkcaster card) { + super(card); + } + + @Override + public TenuredInkcaster copy() { + return new TenuredInkcaster(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/Tephraderm.java b/Mage.Sets/src/mage/cards/t/Tephraderm.java index 446d7c091138..bfd2a109efdb 100644 --- a/Mage.Sets/src/mage/cards/t/Tephraderm.java +++ b/Mage.Sets/src/mage/cards/t/Tephraderm.java @@ -64,7 +64,7 @@ public TephradermCreatureDamageTriggeredAbility(final TephradermCreatureDamageTr @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override @@ -110,7 +110,7 @@ public TephradermSpellDamageTriggeredAbility(final TephradermSpellDamageTriggere @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/t/TerashisGrasp.java b/Mage.Sets/src/mage/cards/t/TerashisGrasp.java index 8e8a210f83ed..174101c1b50b 100644 --- a/Mage.Sets/src/mage/cards/t/TerashisGrasp.java +++ b/Mage.Sets/src/mage/cards/t/TerashisGrasp.java @@ -47,7 +47,7 @@ private static class TerashisGraspEffect extends OneShotEffect { public TerashisGraspEffect() { super(Outcome.DestroyPermanent); - staticText = "You gain life equal to its converted mana cost"; + staticText = "You gain life equal to its mana value"; } public TerashisGraspEffect(TerashisGraspEffect effect) { @@ -58,7 +58,7 @@ public TerashisGraspEffect(TerashisGraspEffect effect) { public boolean apply(Game game, Ability source) { Permanent targetPermanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (targetPermanent != null) { - int cost = targetPermanent.getConvertedManaCost(); + int cost = targetPermanent.getManaValue(); Player player = game.getPlayer(source.getControllerId()); if (player != null) { player.gainLife(cost, game, source); diff --git a/Mage.Sets/src/mage/cards/t/TerentatekCub.java b/Mage.Sets/src/mage/cards/t/TerentatekCub.java index 8615990666ed..56814d272790 100644 --- a/Mage.Sets/src/mage/cards/t/TerentatekCub.java +++ b/Mage.Sets/src/mage/cards/t/TerentatekCub.java @@ -43,7 +43,7 @@ public TerentatekCub(UUID ownerId, CardSetInfo setInfo) { new OpponentControlsPermanentCondition(filter), "As long as an opponent controls a Jedi or Sith, {this} gets +1/+1")); Effect effect = new ConditionalRequirementEffect(new AttacksIfAbleSourceEffect(Duration.Custom), new OpponentControlsPermanentCondition(filter)); - effect.setText("and attacks each turn if able"); + effect.setText("and attacks each combat if able"); ability.addEffect(effect); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/TergridGodOfFright.java b/Mage.Sets/src/mage/cards/t/TergridGodOfFright.java index 90c0cf08e559..e5f66b6bd53f 100644 --- a/Mage.Sets/src/mage/cards/t/TergridGodOfFright.java +++ b/Mage.Sets/src/mage/cards/t/TergridGodOfFright.java @@ -120,7 +120,7 @@ public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.DISCARDED_CARD) { Card discardedCard = game.getCard(event.getTargetId()); if (discardedCard != null - && !(discardedCard.isInstantOrSorcery())) { + && !discardedCard.isInstantOrSorcery()) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(discardedCard.getId(), game)); } diff --git a/Mage.Sets/src/mage/cards/t/Terminus.java b/Mage.Sets/src/mage/cards/t/Terminus.java index 56622c230424..1bd33f97f035 100644 --- a/Mage.Sets/src/mage/cards/t/Terminus.java +++ b/Mage.Sets/src/mage/cards/t/Terminus.java @@ -12,7 +12,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/t/TerritorialAllosaurus.java b/Mage.Sets/src/mage/cards/t/TerritorialAllosaurus.java index 0866bc1dd97c..ad3f2dbe87e4 100644 --- a/Mage.Sets/src/mage/cards/t/TerritorialAllosaurus.java +++ b/Mage.Sets/src/mage/cards/t/TerritorialAllosaurus.java @@ -14,7 +14,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/t/TerrorOfKruinPass.java b/Mage.Sets/src/mage/cards/t/TerrorOfKruinPass.java index 4baa103d0cc9..7e3cb3b8a886 100644 --- a/Mage.Sets/src/mage/cards/t/TerrorOfKruinPass.java +++ b/Mage.Sets/src/mage/cards/t/TerrorOfKruinPass.java @@ -1,41 +1,29 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.common.TransformedCondition; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.TransformSourceEffect; -import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.common.WerewolfBackTriggeredAbility; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.DoubleStrikeAbility; import mage.abilities.keyword.MenaceAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.common.FilterCreaturePermanent; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; + +import java.util.UUID; /** - * * @author North */ public final class TerrorOfKruinPass extends CardImpl { - private static final String ruleText = "Werewolves you control have menace. (They can't be blocked except by two or more creatures.)"; - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Werewolf you control"); - static { - filter.add(TargetController.YOU.getControllerPredicate()); - filter.add(SubType.WEREWOLF.getPredicate()); - } + private static final FilterPermanent filter = new FilterPermanent(SubType.WEREWOLF, "Werewolves"); public TerrorOfKruinPass(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.WEREWOLF); // this card is the second face of double-faced card @@ -47,13 +35,14 @@ public TerrorOfKruinPass(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(3); this.addAbility(DoubleStrikeAbility.getInstance()); + // Werewolves you control have menace. (They can't be blocked except by two or more creatures.) - Effect effect = new ConditionalContinuousEffect(new GainAbilityAllEffect(new MenaceAbility(), Duration.Custom, filter), new TransformedCondition(), ruleText); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); - + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + new MenaceAbility(false), Duration.WhileOnBattlefield, filter, false + ))); + // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Terror of Kruin Pass. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private TerrorOfKruinPass(final TerrorOfKruinPass card) { diff --git a/Mage.Sets/src/mage/cards/t/TesharAncestorsApostle.java b/Mage.Sets/src/mage/cards/t/TesharAncestorsApostle.java index cfe5898c04f1..3942479468ad 100644 --- a/Mage.Sets/src/mage/cards/t/TesharAncestorsApostle.java +++ b/Mage.Sets/src/mage/cards/t/TesharAncestorsApostle.java @@ -16,7 +16,7 @@ import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; import mage.filter.common.FilterHistoricSpell; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInYourGraveyard; /** @@ -25,10 +25,10 @@ */ public final class TesharAncestorsApostle extends CardImpl { - private static final FilterCard filter = new FilterCreatureCard("creature card with converted mana cost 3 or less from your graveyard"); + private static final FilterCard filter = new FilterCreatureCard("creature card with mana value 3 or less from your graveyard"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); filter.add(CardType.CREATURE.getPredicate()); } @@ -45,7 +45,7 @@ public TesharAncestorsApostle(UUID ownerId, CardSetInfo cardSetInfo) { // Whenever you cast a historic spell, return target creature card with converted mana cost 3 or less from your graveyard to the battlefield. Ability ability = new SpellCastControllerTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect() - .setText("return target creature card with converted mana cost 3 or less from your graveyard to the battlefield. " + .setText("return target creature card with mana value 3 or less from your graveyard to the battlefield. " + "(Artifacts, legendaries, and Sagas are historic.)"), new FilterHistoricSpell("a historic spell"), false); ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/t/TestOfTalents.java b/Mage.Sets/src/mage/cards/t/TestOfTalents.java new file mode 100644 index 000000000000..3051c0f0aed9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TestOfTalents.java @@ -0,0 +1,102 @@ +package mage.cards.t; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.common.search.SearchTargetGraveyardHandLibraryForCardNameAndExileEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.stack.StackObject; +import mage.players.Player; +import mage.target.TargetSpell; +import mage.util.CardUtil; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TestOfTalents extends CardImpl { + + public TestOfTalents(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); + + // Counter target instant or sorcery spell. Search its controller's graveyard, hand, and library for any number of cards with the same name as that spell and exile them. That player shuffles, then draws a card for each card exiled from their hand this way. + this.getSpellAbility().addEffect(new TestOfTalentsEffect()); + this.getSpellAbility().addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_INSTANT_OR_SORCERY)); + } + + private TestOfTalents(final TestOfTalents card) { + super(card); + } + + @Override + public TestOfTalents copy() { + return new TestOfTalents(this); + } +} + +class TestOfTalentsEffect extends SearchTargetGraveyardHandLibraryForCardNameAndExileEffect { + + TestOfTalentsEffect() { + super(false, "its controller's", "all cards with the same name as that spell"); + staticText = "counter target instant or sorcery spell. Search its controller's graveyard, hand, " + + "and library for any number of cards with the same name as that spell and exile them. " + + "That player shuffles, then draws a card for each card exiled from their hand this way"; + } + + private TestOfTalentsEffect(final TestOfTalentsEffect effect) { + super(effect); + } + + @Override + public TestOfTalentsEffect copy() { + return new TestOfTalentsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + StackObject stackObject = game.getStack().getStackObject(source.getFirstTarget()); + if (stackObject == null) { + return false; + } + MageObject targetObject = game.getObject(stackObject.getSourceId()); + String cardName; + if (targetObject instanceof Card) { + cardName = targetObject.getName(); + } else { + cardName = ""; + } + UUID searchPlayerId = stackObject.getControllerId(); + Player player = game.getPlayer(searchPlayerId); + if (player == null) { + return false; + } + int previousCount = player + .getHand() + .getCards(game) + .stream() + .map(MageObject::getName) + .filter(Objects::nonNull) + .mapToInt(s -> CardUtil.haveSameNames(s, cardName) ? 1 : 0) + .sum(); + game.getStack().counter(source.getFirstTarget(), source, game); + this.applySearchAndExile(game, source, cardName, searchPlayerId); + int newCount = player + .getHand() + .getCards(game) + .stream() + .map(MageObject::getName) + .filter(Objects::nonNull) + .mapToInt(s -> CardUtil.haveSameNames(s, cardName) ? 1 : 0) + .sum(); + if (previousCount > newCount) { + player.drawCards(previousCount - newCount, source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TethmosHighPriest.java b/Mage.Sets/src/mage/cards/t/TethmosHighPriest.java index 7d278fda99a8..0749e8f6ad5a 100644 --- a/Mage.Sets/src/mage/cards/t/TethmosHighPriest.java +++ b/Mage.Sets/src/mage/cards/t/TethmosHighPriest.java @@ -12,7 +12,7 @@ import mage.constants.SubType; import mage.constants.ComparisonType; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.Target; import mage.target.common.TargetCardInYourGraveyard; @@ -22,10 +22,10 @@ */ public final class TethmosHighPriest extends CardImpl { - private static final FilterCreatureCard filter = new FilterCreatureCard("creature card with converted mana cost 2 or less from your graveyard"); + private static final FilterCreatureCard filter = new FilterCreatureCard("creature card with mana value 2 or less from your graveyard"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); } public TethmosHighPriest(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/t/TeveshSzatDoomOfFools.java b/Mage.Sets/src/mage/cards/t/TeveshSzatDoomOfFools.java index 3869b403f7e7..67a9ae5eb041 100644 --- a/Mage.Sets/src/mage/cards/t/TeveshSzatDoomOfFools.java +++ b/Mage.Sets/src/mage/cards/t/TeveshSzatDoomOfFools.java @@ -15,8 +15,8 @@ import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; -import mage.filter.predicate.permanent.CommanderPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.predicate.mageobject.CommanderPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.BreedingPitThrullToken; diff --git a/Mage.Sets/src/mage/cards/t/TeysaOrzhovScion.java b/Mage.Sets/src/mage/cards/t/TeysaOrzhovScion.java index fa46a42dd60a..e66437a06a0a 100644 --- a/Mage.Sets/src/mage/cards/t/TeysaOrzhovScion.java +++ b/Mage.Sets/src/mage/cards/t/TeysaOrzhovScion.java @@ -14,7 +14,7 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.SpiritWhiteToken; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/t/TezzeretCruelMachinist.java b/Mage.Sets/src/mage/cards/t/TezzeretCruelMachinist.java index 8b20398e3ccc..bcd04fe0503f 100644 --- a/Mage.Sets/src/mage/cards/t/TezzeretCruelMachinist.java +++ b/Mage.Sets/src/mage/cards/t/TezzeretCruelMachinist.java @@ -1,14 +1,18 @@ package mage.cards.t; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; -import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.AddCardTypeTargetEffect; import mage.abilities.effects.common.continuous.SetPowerToughnessTargetEffect; -import mage.cards.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.*; import mage.filter.StaticFilters; import mage.game.Game; @@ -17,9 +21,10 @@ import mage.target.Target; import mage.target.TargetPermanent; import mage.target.common.TargetCardInHand; -import mage.target.targetpointer.FixedTarget; +import mage.target.targetpointer.FixedTargets; import java.util.UUID; +import java.util.stream.Collectors; /** * @author TheElk801 @@ -38,9 +43,7 @@ public TezzeretCruelMachinist(UUID ownerId, CardSetInfo setInfo) { // 0: Until your next turn, target artifact you control becomes a 5/5 creature in addition to its other types. Ability ability = new LoyaltyAbility(new AddCardTypeTargetEffect( - Duration.UntilYourNextTurn, - CardType.ARTIFACT, - CardType.CREATURE + Duration.UntilYourNextTurn, CardType.ARTIFACT, CardType.CREATURE ).setText("Until your next turn, target artifact you control becomes an artifact creature"), 0); ability.addEffect(new SetPowerToughnessTargetEffect( 5, 5, Duration.UntilYourNextTurn @@ -85,41 +88,30 @@ public boolean apply(Game game, Ability source) { return false; } Target target = new TargetCardInHand(0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD); - if (!player.choose(outcome, target, source.getSourceId(), game)) { + player.choose(outcome, target, source.getSourceId(), game); + Cards cardsToMove = new CardsImpl(target.getTargets()); + if (cardsToMove.isEmpty()) { return false; } - Cards cardsToMove = new CardsImpl(); - - for (UUID cardId : target.getTargets()) { - Card card = game.getCard(cardId); - if (card == null) { - continue; - } - cardsToMove.add(card); - - ContinuousEffect effectCardType = new TezzeretCruelMachinistCardTypeEffect(); - effectCardType.setTargetPointer(new FixedTarget( - card.getId(), - card.getZoneChangeCounter(game) + 1 - )); - game.addEffect(effectCardType, source); - - ContinuousEffect effectPowerToughness = new TezzeretCruelMachinistPowerToughnessEffect(); - effectPowerToughness.setTargetPointer(new FixedTarget( - card.getId(), - card.getZoneChangeCounter(game) + 1 - )); - game.addEffect(effectPowerToughness, source); - } - - return player.moveCards(cardsToMove.getCards(game), Zone.BATTLEFIELD, source, game, false, true, true, null); + game.addEffect(new TezzeretCruelMachinistCardTypeEffect().setTargetPointer(new FixedTargets( + cardsToMove + .getCards(game) + .stream() + .map(card -> new MageObjectReference(card, game, 1)) + .collect(Collectors.toSet()), game + )), source); + player.moveCards( + cardsToMove.getCards(game), Zone.BATTLEFIELD, source, game, + false, true, true, null + ); + return true; } } -class TezzeretCruelMachinistCardTypeEffect extends AddCardTypeTargetEffect { +class TezzeretCruelMachinistCardTypeEffect extends ContinuousEffectImpl { public TezzeretCruelMachinistCardTypeEffect() { - super(Duration.WhileOnBattlefield, CardType.ARTIFACT, CardType.CREATURE); + super(Duration.Custom, Layer.CopyEffects_1, SubLayer.FaceDownEffects_1b, Outcome.Neutral); } public TezzeretCruelMachinistCardTypeEffect(final TezzeretCruelMachinistCardTypeEffect effect) { @@ -132,53 +124,26 @@ public TezzeretCruelMachinistCardTypeEffect copy() { } @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + public boolean apply(Game game, Ability source) { + boolean flag = false; for (UUID targetId : targetPointer.getTargets(game, source)) { Permanent target = game.getPermanent(targetId); - if (target != null - && target.isFaceDown(game)) { - switch (layer) { - case TypeChangingEffects_4: - target.getSuperType().clear(); - target.getCardType().clear(); - target.removeAllSubTypes(game); - target.addCardType(CardType.ARTIFACT); - target.addCardType(CardType.CREATURE); - break; - } - return true; + if (target == null || !target.isFaceDown(game)) { + continue; } + flag = true; + target.getSuperType().clear(); + target.getCardType().clear(); + target.removeAllSubTypes(game); + target.addCardType(CardType.ARTIFACT); + target.addCardType(CardType.CREATURE); + target.getPower().setValue(5); + target.getToughness().setValue(5); } - return false; - } -} - -class TezzeretCruelMachinistPowerToughnessEffect extends SetPowerToughnessTargetEffect { - - public TezzeretCruelMachinistPowerToughnessEffect() { - super(5, 5, Duration.WhileOnBattlefield); - } - - public TezzeretCruelMachinistPowerToughnessEffect(final TezzeretCruelMachinistPowerToughnessEffect effect) { - super(effect); - } - - @Override - public TezzeretCruelMachinistPowerToughnessEffect copy() { - return new TezzeretCruelMachinistPowerToughnessEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - for (UUID targetId : this.getTargetPointer().getTargets(game, source)) { - Permanent target = game.getPermanent(targetId); - if (target != null - && target.isFaceDown(game)) { - target.getPower().setValue(5); - target.getToughness().setValue(5); - return true; - } + if (!flag) { + discard(); + return false; } - return false; + return true; } } diff --git a/Mage.Sets/src/mage/cards/t/TezzeretTheSeeker.java b/Mage.Sets/src/mage/cards/t/TezzeretTheSeeker.java index 05432166eeed..8790069986b2 100644 --- a/Mage.Sets/src/mage/cards/t/TezzeretTheSeeker.java +++ b/Mage.Sets/src/mage/cards/t/TezzeretTheSeeker.java @@ -17,7 +17,7 @@ import mage.constants.*; import mage.filter.common.FilterArtifactCard; import mage.filter.common.FilterArtifactPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -61,7 +61,7 @@ class TezzeretTheSeekerEffect2 extends OneShotEffect { public TezzeretTheSeekerEffect2() { super(Outcome.DrawCard); - this.staticText = "Search your library for an artifact card with converted mana cost X or less and put it onto the battlefield. Then shuffle your library"; + this.staticText = "Search your library for an artifact card with mana value X or less, put it onto the battlefield, then shuffle"; } public TezzeretTheSeekerEffect2(final TezzeretTheSeekerEffect2 effect) { @@ -87,8 +87,8 @@ public boolean apply(Game game, Ability source) { } } - FilterArtifactCard filter = new FilterArtifactCard("artifact card with converted mana cost " + cmc + " or less"); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, cmc + 1)); + FilterArtifactCard filter = new FilterArtifactCard("artifact card with mana value " + cmc + " or less"); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, cmc + 1)); TargetCardInLibrary target = new TargetCardInLibrary(filter); if (controller.searchLibrary(target, source, game)) { diff --git a/Mage.Sets/src/mage/cards/t/ThadaAdelAcquisitor.java b/Mage.Sets/src/mage/cards/t/ThadaAdelAcquisitor.java index d23962a5bb67..bf02e7fc12b1 100644 --- a/Mage.Sets/src/mage/cards/t/ThadaAdelAcquisitor.java +++ b/Mage.Sets/src/mage/cards/t/ThadaAdelAcquisitor.java @@ -56,7 +56,7 @@ class ThadaAdelAcquisitorEffect extends OneShotEffect { ThadaAdelAcquisitorEffect() { super(Outcome.Exile); - staticText = "search that player's library for an artifact card and exile it. Then that player shuffles their library. Until end of turn, you may play that card"; + staticText = "search that player's library for an artifact card and exile it. Then that player shuffles. Until end of turn, you may play that card"; } ThadaAdelAcquisitorEffect(final ThadaAdelAcquisitorEffect effect) { diff --git a/Mage.Sets/src/mage/cards/t/ThaliasLancers.java b/Mage.Sets/src/mage/cards/t/ThaliasLancers.java index 6829400f4583..9808929eab36 100644 --- a/Mage.Sets/src/mage/cards/t/ThaliasLancers.java +++ b/Mage.Sets/src/mage/cards/t/ThaliasLancers.java @@ -39,7 +39,7 @@ public ThaliasLancers(UUID ownerId, CardSetInfo setInfo) { // When Thalia's Lancers enters the battlefield, you may search your library for a legendary card, reveal it, put it into your hand, then shuffle your library. Effect effect = new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 1, filter), true, true); - effect.setText("you may search your library for a legendary card, reveal it, put it into your hand, then shuffle your library"); + effect.setText("you may search your library for a legendary card, reveal it, put it into your hand, then shuffle"); this.addAbility(new EntersBattlefieldTriggeredAbility(effect, true)); } diff --git a/Mage.Sets/src/mage/cards/t/ThaliasLieutenant.java b/Mage.Sets/src/mage/cards/t/ThaliasLieutenant.java index 6b3f6f188d37..a5329812e5a7 100644 --- a/Mage.Sets/src/mage/cards/t/ThaliasLieutenant.java +++ b/Mage.Sets/src/mage/cards/t/ThaliasLieutenant.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/t/ThassaDeepDwelling.java b/Mage.Sets/src/mage/cards/t/ThassaDeepDwelling.java index dd0345c002b5..4fd1af7f4bd2 100644 --- a/Mage.Sets/src/mage/cards/t/ThassaDeepDwelling.java +++ b/Mage.Sets/src/mage/cards/t/ThassaDeepDwelling.java @@ -21,7 +21,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/t/ThatWhichWasTaken.java b/Mage.Sets/src/mage/cards/t/ThatWhichWasTaken.java index 0ef3c0a62bf4..dca5111e31b9 100644 --- a/Mage.Sets/src/mage/cards/t/ThatWhichWasTaken.java +++ b/Mage.Sets/src/mage/cards/t/ThatWhichWasTaken.java @@ -19,7 +19,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; diff --git a/Mage.Sets/src/mage/cards/t/TheBiblioplex.java b/Mage.Sets/src/mage/cards/t/TheBiblioplex.java new file mode 100644 index 000000000000..8b7c16494344 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheBiblioplex.java @@ -0,0 +1,108 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.mana.ColorlessManaAbility; +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.Zone; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheBiblioplex extends CardImpl { + + public TheBiblioplex(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // {T}: Add {C}. + this.addAbility(new ColorlessManaAbility()); + + // {2}, {T}: Look at the top card of your library. If it's an instant or sorcery card, you may reveal it and put it into your hand. If you don't put the card into your hand, you may put it into your graveyard. Activate only if you have exactly zero or seven cards in hand. + Ability ability = new ActivateIfConditionActivatedAbility( + Zone.BATTLEFIELD, new TheBiblioplexEffect(), + new GenericManaCost(2), TheBiblioplexCondition.instance + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private TheBiblioplex(final TheBiblioplex card) { + super(card); + } + + @Override + public TheBiblioplex copy() { + return new TheBiblioplex(this); + } +} + +enum TheBiblioplexCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + return player != null && (player.getHand().size() == 0 || player.getHand().size() == 7); + } + + @Override + public String toString() { + return "if you have exactly zero or seven cards in hand"; + } +} + +class TheBiblioplexEffect extends OneShotEffect { + + TheBiblioplexEffect() { + super(Outcome.Benefit); + staticText = "look at the top card of your library. If it's an instant or sorcery card, " + + "you may reveal it and put it into your hand. If you don't put the card into your hand, " + + "you may put it into your graveyard"; + } + + private TheBiblioplexEffect(final TheBiblioplexEffect effect) { + super(effect); + } + + @Override + public TheBiblioplexEffect copy() { + return new TheBiblioplexEffect(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 of library", card, game); + if (card.isInstantOrSorcery() && player.chooseUse( + Outcome.DrawCard, "Reveal that card and put it into your hand?", source, game + )) { + player.revealCards(source, new CardsImpl(card), game); + player.moveCards(card, Zone.HAND, source, game); + } else if (player.chooseUse( + Outcome.Discard, "Put that card into your graveyard?", source, game + )) { + player.moveCards(card, Zone.GRAVEYARD, source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheFallen.java b/Mage.Sets/src/mage/cards/t/TheFallen.java index 40b393dea95a..65e16a5df62e 100644 --- a/Mage.Sets/src/mage/cards/t/TheFallen.java +++ b/Mage.Sets/src/mage/cards/t/TheFallen.java @@ -1,25 +1,21 @@ package mage.cards.t; -import java.util.*; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.constants.WatcherScope; +import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.watchers.Watcher; +import java.util.*; + /** - * * @author L_J */ public final class TheFallen extends CardImpl { @@ -85,20 +81,26 @@ public TheFallenWatcher() { @Override public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER) { - Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); - if (permanent != null) { - Set toAdd; - if (playersAndWalkersDealtDamageThisGame.get(event.getSourceId()) == null) { - toAdd = new HashSet<>(); - } else { - toAdd = playersAndWalkersDealtDamageThisGame.get(event.getSourceId()); - } - toAdd.add(event.getPlayerId()); - playersAndWalkersDealtDamageThisGame.put(event.getSourceId(), toAdd); - } + if (event.getType() != GameEvent.EventType.DAMAGED_PLAYER + && event.getType() != GameEvent.EventType.DAMAGED_PERMANENT) { + return; + } + Permanent damaged = game.getPermanent(event.getTargetId()); + if (damaged != null && !damaged.isPlaneswalker()) { + return; + } + Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); + if (permanent == null) { + return; + } + Set toAdd; + if (playersAndWalkersDealtDamageThisGame.get(event.getSourceId()) == null) { + toAdd = new HashSet<>(); + } else { + toAdd = playersAndWalkersDealtDamageThisGame.get(event.getSourceId()); } + toAdd.add(event.getPlayerId()); + playersAndWalkersDealtDamageThisGame.put(event.getSourceId(), toAdd); } public Set getPlayersAndWalkersDealtDamageThisGame(UUID creatureId) { diff --git a/Mage.Sets/src/mage/cards/t/TheFirstSliver.java b/Mage.Sets/src/mage/cards/t/TheFirstSliver.java index d3381480e41d..bc3cc6c1f013 100644 --- a/Mage.Sets/src/mage/cards/t/TheFirstSliver.java +++ b/Mage.Sets/src/mage/cards/t/TheFirstSliver.java @@ -33,10 +33,10 @@ public TheFirstSliver(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(7); // Cascade - this.addAbility(new CascadeAbility()); + this.addAbility(new CascadeAbility(false)); // Sliver spells you cast have cascade. - this.addAbility(new SimpleStaticAbility(new GainAbilityControlledSpellsEffect(new CascadeAbility(), filter))); + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledSpellsEffect(new CascadeAbility(false), filter))); } private TheFirstSliver(final TheFirstSliver card) { diff --git a/Mage.Sets/src/mage/cards/t/TheFlameOfKeld.java b/Mage.Sets/src/mage/cards/t/TheFlameOfKeld.java index b76864c3af3e..951bc53e6b22 100644 --- a/Mage.Sets/src/mage/cards/t/TheFlameOfKeld.java +++ b/Mage.Sets/src/mage/cards/t/TheFlameOfKeld.java @@ -72,8 +72,7 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { @Override public boolean checksEventType(GameEvent event, Game game) { switch (event.getType()) { - case DAMAGE_CREATURE: - case DAMAGE_PLANESWALKER: + case DAMAGE_PERMANENT: case DAMAGE_PLAYER: return true; default: diff --git a/Mage.Sets/src/mage/cards/t/TheGreatAurora.java b/Mage.Sets/src/mage/cards/t/TheGreatAurora.java index e24c21d41de2..227fc96cb03d 100644 --- a/Mage.Sets/src/mage/cards/t/TheGreatAurora.java +++ b/Mage.Sets/src/mage/cards/t/TheGreatAurora.java @@ -30,7 +30,7 @@ public TheGreatAurora(UUID ownerId, CardSetInfo setInfo) { // Each player shuffles all cards from their hand and all permanents they own into their library, then draws that many cards. Each player may put any number of land cards from their hand onto the battlefield. Exile The Great Aurora. this.getSpellAbility().addEffect(new TheGreatAuroraEffect()); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private TheGreatAurora(final TheGreatAurora card) { diff --git a/Mage.Sets/src/mage/cards/t/TheMirariConjecture.java b/Mage.Sets/src/mage/cards/t/TheMirariConjecture.java index f3f57d666471..2f8fe7ad92b0 100644 --- a/Mage.Sets/src/mage/cards/t/TheMirariConjecture.java +++ b/Mage.Sets/src/mage/cards/t/TheMirariConjecture.java @@ -97,7 +97,7 @@ public boolean checkTrigger(GameEvent event, Game game) { if (event.getPlayerId().equals(this.getControllerId())) { Spell spell = game.getStack().getSpell(event.getTargetId()); if (spell != null - && (spell.isInstant() || spell.isSorcery())) { + && spell.isInstantOrSorcery()) { this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId())); return true; } diff --git a/Mage.Sets/src/mage/cards/t/TheOzolith.java b/Mage.Sets/src/mage/cards/t/TheOzolith.java index 24140818e4b4..5fc3fbf55a50 100644 --- a/Mage.Sets/src/mage/cards/t/TheOzolith.java +++ b/Mage.Sets/src/mage/cards/t/TheOzolith.java @@ -14,9 +14,7 @@ import mage.constants.TargetController; import mage.counters.Counter; import mage.counters.Counters; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.CounterAnyPredicate; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; @@ -61,14 +59,8 @@ public TheOzolith copy() { class TheOzolithTriggeredAbility extends LeavesBattlefieldAllTriggeredAbility { - private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); - - static { - filter.add(CounterAnyPredicate.instance); - } - TheOzolithTriggeredAbility() { - super(null, filter); + super(null, StaticFilters.FILTER_CONTROLLED_CREATURE); } private TheOzolithTriggeredAbility(final TheOzolithTriggeredAbility ability) { @@ -85,8 +77,12 @@ public boolean checkTrigger(GameEvent event, Game game) { return false; } Permanent permanent = ((ZoneChangeEvent) event).getTarget(); + Counters counters = permanent.getCounters(game); + if (counters.values().stream().mapToInt(Counter::getCount).noneMatch(x -> x > 0)) { + return false; + } this.getEffects().clear(); - this.addEffect(new TheOzolithLeaveEffect(permanent.getCounters(game))); + this.addEffect(new TheOzolithLeaveEffect(counters)); return true; } diff --git a/Mage.Sets/src/mage/cards/t/TheScorpionGod.java b/Mage.Sets/src/mage/cards/t/TheScorpionGod.java index aaed9259bc99..f7e40ecea7d4 100644 --- a/Mage.Sets/src/mage/cards/t/TheScorpionGod.java +++ b/Mage.Sets/src/mage/cards/t/TheScorpionGod.java @@ -24,10 +24,9 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/t/TheWanderer.java b/Mage.Sets/src/mage/cards/t/TheWanderer.java index 12c10959e963..1b4a1bc002ae 100644 --- a/Mage.Sets/src/mage/cards/t/TheWanderer.java +++ b/Mage.Sets/src/mage/cards/t/TheWanderer.java @@ -16,7 +16,7 @@ import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/t/ThelonOfHavenwood.java b/Mage.Sets/src/mage/cards/t/ThelonOfHavenwood.java index e60794c90af8..b87598f205f2 100644 --- a/Mage.Sets/src/mage/cards/t/ThelonOfHavenwood.java +++ b/Mage.Sets/src/mage/cards/t/ThelonOfHavenwood.java @@ -47,7 +47,7 @@ public ThelonOfHavenwood(UUID ownerId, CardSetInfo setInfo) { // {B}{G}, Exile a Fungus card from a graveyard: Put a spore counter on each Fungus on the battlefield. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersAllEffect(CounterType.SPORE.createInstance(), filterPermanent), new ManaCostsImpl<>("{B}{G}")); - ability.addCost(new ExileFromGraveCost(new TargetCardInASingleGraveyard(1, 1, filterCard))); + ability.addCost(new ExileFromGraveCost(new TargetCardInASingleGraveyard(1, 1, filterCard), "exile a Fungus card from a graveyard")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/TheloniteHermit.java b/Mage.Sets/src/mage/cards/t/TheloniteHermit.java index 87a25f7ae25f..7f78ecf9f1bc 100644 --- a/Mage.Sets/src/mage/cards/t/TheloniteHermit.java +++ b/Mage.Sets/src/mage/cards/t/TheloniteHermit.java @@ -24,7 +24,7 @@ */ public final class TheloniteHermit extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Saproling creatures"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("All Saprolings"); static { filter.add(SubType.SAPROLING.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/t/TheoreticalDuplication.java b/Mage.Sets/src/mage/cards/t/TheoreticalDuplication.java new file mode 100644 index 000000000000..03b6b2b61f5c --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheoreticalDuplication.java @@ -0,0 +1,83 @@ +package mage.cards.t; + +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.PermanentToken; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheoreticalDuplication extends CardImpl { + + public TheoreticalDuplication(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}"); + + // Whenever a nontoken creature enters the battlefield under an opponent's control this turn, create a token that's a copy of that creature. + this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new TheoreticalDuplicationTriggeredAbility())); + } + + private TheoreticalDuplication(final TheoreticalDuplication card) { + super(card); + } + + @Override + public TheoreticalDuplication copy() { + return new TheoreticalDuplication(this); + } +} + +class TheoreticalDuplicationTriggeredAbility extends DelayedTriggeredAbility { + + private static final CreateTokenCopyTargetEffect makeEffect() { + CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(); + effect.setUseLKI(true); + return effect; + } + + TheoreticalDuplicationTriggeredAbility() { + super(makeEffect(), Duration.EndOfTurn, false, false); + } + + private TheoreticalDuplicationTriggeredAbility(final TheoreticalDuplicationTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + EntersTheBattlefieldEvent eEvent = (EntersTheBattlefieldEvent) event; + if (!eEvent.getTarget().isCreature() + || eEvent.getTarget() instanceof PermanentToken + || !game.getOpponents(getControllerId()).contains(eEvent.getTarget().getControllerId())) { + return false; + } + getEffects().setTargetPointer(new FixedTarget(eEvent.getTarget(), game)); + return true; + } + + @Override + public TheoreticalDuplicationTriggeredAbility copy() { + return new TheoreticalDuplicationTriggeredAbility(this); + } + + @Override + public String getRule() { + return "Whenever a nontoken creature enters the battlefield under an opponent's control this turn, " + + "create a token that's a copy of that creature."; + } +} diff --git a/Mage.Sets/src/mage/cards/t/ThiefOfHope.java b/Mage.Sets/src/mage/cards/t/ThiefOfHope.java index e6f5aa3844d6..c86a9916e492 100644 --- a/Mage.Sets/src/mage/cards/t/ThiefOfHope.java +++ b/Mage.Sets/src/mage/cards/t/ThiefOfHope.java @@ -28,7 +28,7 @@ public ThiefOfHope(UUID ownerId, CardSetInfo setInfo) { this.power = new MageInt(2); this.toughness = new MageInt(2); Ability ability = new SpellCastControllerTriggeredAbility(new LoseLifeTargetEffect(1), StaticFilters.SPIRIT_OR_ARCANE_CARD, false); - ability.addEffect(new GainLifeEffect(1)); + ability.addEffect(new GainLifeEffect(1).concatBy("and")); ability.addTarget(new TargetOpponent()); this.addAbility(ability); this.addAbility(new SoulshiftAbility(2)); diff --git a/Mage.Sets/src/mage/cards/t/ThievingSkydiver.java b/Mage.Sets/src/mage/cards/t/ThievingSkydiver.java index cc9370f74162..e58c14ff18b5 100644 --- a/Mage.Sets/src/mage/cards/t/ThievingSkydiver.java +++ b/Mage.Sets/src/mage/cards/t/ThievingSkydiver.java @@ -15,7 +15,7 @@ import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterArtifactPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; @@ -51,7 +51,7 @@ public ThievingSkydiver(UUID ownerId, CardSetInfo setInfo) { Ability ability = new ConditionalInterveningIfTriggeredAbility( new EntersBattlefieldTriggeredAbility(new GainControlTargetEffect(Duration.Custom), false), KickedCondition.instance, "When {this} enters the battlefield, if it was kicked, " + - "gain control of target artifact with converted mana cost X or less. " + + "gain control of target artifact with mana value X or less. " + "If that artifact is an Equipment, attach it to {this}." ); ability.addEffect(new ThievingSkydiverEffect()); @@ -76,9 +76,9 @@ enum ThievingSkydiverAdjuster implements TargetAdjuster { public void adjustTargets(Ability ability, Game game) { int xValue = GetKickerXValue.instance.calculate(game, ability, null); FilterPermanent filter = new FilterArtifactPermanent( - "artifact with converted mana cost " + xValue + " or less" + "artifact with mana value " + xValue + " or less" ); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, xValue + 1)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); ability.getTargets().clear(); ability.addTarget(new TargetPermanent(filter)); } diff --git a/Mage.Sets/src/mage/cards/t/ThinkTank.java b/Mage.Sets/src/mage/cards/t/ThinkTank.java index 655a726f9c9e..996f01809972 100644 --- a/Mage.Sets/src/mage/cards/t/ThinkTank.java +++ b/Mage.Sets/src/mage/cards/t/ThinkTank.java @@ -67,7 +67,7 @@ public boolean apply(Game game, Ability source) { CardsImpl cards = new CardsImpl(); cards.add(card); controller.lookAtCards("Think Tank", cards, game); - if (controller.chooseUse(Outcome.Neutral, "Do you wish to put the card into your graveyard?", source, game)) { + if (controller.chooseUse(Outcome.Neutral, "Put that card into your graveyard?", source, game)) { return controller.moveCards(card, Zone.GRAVEYARD, source, game); } diff --git a/Mage.Sets/src/mage/cards/t/ThirstForKnowledge.java b/Mage.Sets/src/mage/cards/t/ThirstForKnowledge.java index 4a3d27c6348e..f92124defe83 100644 --- a/Mage.Sets/src/mage/cards/t/ThirstForKnowledge.java +++ b/Mage.Sets/src/mage/cards/t/ThirstForKnowledge.java @@ -24,12 +24,10 @@ public ThirstForKnowledge(UUID ownerId, CardSetInfo setInfo) { // Draw three cards. Then discard two cards unless you discard an artifact card. DiscardCardCost cost = new DiscardCardCost(filter); - cost.setText("discard one artifact card instead two cards"); + cost.setText("discard an artifact card instead of discarding two cards"); this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(3)); this.getSpellAbility().addEffect(new DoIfCostPaid( - null, - new DiscardControllerEffect(2), - cost + null, new DiscardControllerEffect(2), cost ).setText("Then discard two cards unless you discard an artifact card")); } diff --git a/Mage.Sets/src/mage/cards/t/ThirstForMeaning.java b/Mage.Sets/src/mage/cards/t/ThirstForMeaning.java index 65c452f0d1af..dd4754a1b185 100644 --- a/Mage.Sets/src/mage/cards/t/ThirstForMeaning.java +++ b/Mage.Sets/src/mage/cards/t/ThirstForMeaning.java @@ -24,12 +24,10 @@ public ThirstForMeaning(UUID ownerId, CardSetInfo setInfo) { // Draw three cards. Then discard two cards unless you discard an enchantment card. DiscardCardCost cost = new DiscardCardCost(filter); - cost.setText("discard one enchantment card instead two cards"); + cost.setText("discard an enchantment card instead of discarding two cards"); this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(3)); this.getSpellAbility().addEffect(new DoIfCostPaid( - null, - new DiscardControllerEffect(2), - cost + null, new DiscardControllerEffect(2), cost ).setText("Then discard two cards unless you discard an enchantment card")); } diff --git a/Mage.Sets/src/mage/cards/t/ThirstingAxe.java b/Mage.Sets/src/mage/cards/t/ThirstingAxe.java index 87ccb691b8da..44faef4166ab 100644 --- a/Mage.Sets/src/mage/cards/t/ThirstingAxe.java +++ b/Mage.Sets/src/mage/cards/t/ThirstingAxe.java @@ -1,9 +1,6 @@ package mage.cards.t; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.TriggeredAbility; @@ -22,20 +19,23 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; -import mage.game.events.DamagedCreatureEvent; +import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.watchers.Watcher; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author Quercitron */ public final class ThirstingAxe extends CardImpl { public ThirstingAxe(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); this.subtype.add(SubType.EQUIPMENT); // Equipped creature gets +4/+0. @@ -47,7 +47,7 @@ public ThirstingAxe(UUID ownerId, CardSetInfo setInfo) { AttachedCondition.instance, new InvertCondition(new EquippedDealtCombatDamageToCreatureCondition())); String triggeredAbilityText = "At the beginning of your end step, if equipped creature " + - "didn't deal combat damage to a creature this turn, sacrifice it."; + "didn't deal combat damage to a creature this turn, sacrifice it."; ConditionalInterveningIfTriggeredAbility sacrificeTriggeredAbility = new ConditionalInterveningIfTriggeredAbility(ability, condition, triggeredAbilityText); this.addAbility(sacrificeTriggeredAbility, new CombatDamageToCreatureWatcher()); @@ -85,7 +85,6 @@ class CombatDamageToCreatureWatcher extends Watcher { // which objects dealt combat damage to creature during the turn private final Set dealtCombatDamageToCreature; - public CombatDamageToCreatureWatcher() { super(WatcherScope.GAME); dealtCombatDamageToCreature = new HashSet<>(); @@ -93,12 +92,16 @@ public CombatDamageToCreatureWatcher() { @Override public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.DAMAGED_CREATURE) { - if (((DamagedCreatureEvent) event).isCombatDamage()) { - MageObjectReference damageSource = new MageObjectReference(event.getSourceId(), game); - dealtCombatDamageToCreature.add(damageSource); - } + if (event.getType() != GameEvent.EventType.DAMAGED_PERMANENT + || !((DamagedEvent) event).isCombatDamage()) { + return; + } + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent == null || !permanent.isCreature()) { + return; } + MageObjectReference damageSource = new MageObjectReference(event.getSourceId(), game); + dealtCombatDamageToCreature.add(damageSource); } @Override diff --git a/Mage.Sets/src/mage/cards/t/ThopterAssembly.java b/Mage.Sets/src/mage/cards/t/ThopterAssembly.java index bdeb81218344..3effabdba55b 100644 --- a/Mage.Sets/src/mage/cards/t/ThopterAssembly.java +++ b/Mage.Sets/src/mage/cards/t/ThopterAssembly.java @@ -16,7 +16,7 @@ import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.ThopterColorlessToken; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/t/ThopterSquadron.java b/Mage.Sets/src/mage/cards/t/ThopterSquadron.java index 3fd6416614be..5020ada93e27 100644 --- a/Mage.Sets/src/mage/cards/t/ThopterSquadron.java +++ b/Mage.Sets/src/mage/cards/t/ThopterSquadron.java @@ -20,7 +20,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.ThopterColorlessToken; import mage.target.common.TargetControlledPermanent; diff --git a/Mage.Sets/src/mage/cards/t/ThoughtHemorrhage.java b/Mage.Sets/src/mage/cards/t/ThoughtHemorrhage.java index 1d2ca09c96b1..521204f7f739 100644 --- a/Mage.Sets/src/mage/cards/t/ThoughtHemorrhage.java +++ b/Mage.Sets/src/mage/cards/t/ThoughtHemorrhage.java @@ -59,7 +59,7 @@ class ThoughtHemorrhageEffect extends OneShotEffect { + "the chosen name revealed this way. " + "Search that player's graveyard, hand, and library for " + "all cards with that name and exile them. " - + "Then that player shuffles their library"; + + "Then that player shuffles"; public ThoughtHemorrhageEffect() { super(Outcome.Exile); diff --git a/Mage.Sets/src/mage/cards/t/ThoughtPrison.java b/Mage.Sets/src/mage/cards/t/ThoughtPrison.java index caf26fd9c5d2..8e830a6915d9 100644 --- a/Mage.Sets/src/mage/cards/t/ThoughtPrison.java +++ b/Mage.Sets/src/mage/cards/t/ThoughtPrison.java @@ -17,7 +17,6 @@ import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.players.Player; @@ -151,8 +150,8 @@ public boolean checkTrigger(GameEvent event, Game game) { matches = true; } // Check if spell's CMC matches the imprinted card - int cmc = spell.getConvertedManaCost(); - int imprintedCmc = imprintedCard.getConvertedManaCost(); + int cmc = spell.getManaValue(); + int imprintedCmc = imprintedCard.getManaValue(); if (cmc == imprintedCmc) { matches = true; } @@ -172,7 +171,7 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { - return "Whenever a player casts a spell that shares a color or converted mana cost with the exiled card, " + super.getRule(); + return "Whenever a player casts a spell that shares a color or mana value with the exiled card, " + super.getRule(); } } diff --git a/Mage.Sets/src/mage/cards/t/Thoughtbind.java b/Mage.Sets/src/mage/cards/t/Thoughtbind.java index 47cb274647b4..fa61c2bc1be3 100644 --- a/Mage.Sets/src/mage/cards/t/Thoughtbind.java +++ b/Mage.Sets/src/mage/cards/t/Thoughtbind.java @@ -9,7 +9,7 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetSpell; /** @@ -17,10 +17,10 @@ */ public final class Thoughtbind extends CardImpl { - private static final FilterSpell filter = new FilterSpell("spell with converted mana cost 4 or less"); + private static final FilterSpell filter = new FilterSpell("spell with mana value 4 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 5)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 5)); } public Thoughtbind(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/t/ThousandWinds.java b/Mage.Sets/src/mage/cards/t/ThousandWinds.java index 8a284b368f5e..6d1fc4f2d4da 100644 --- a/Mage.Sets/src/mage/cards/t/ThousandWinds.java +++ b/Mage.Sets/src/mage/cards/t/ThousandWinds.java @@ -13,7 +13,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TappedPredicate; /** diff --git a/Mage.Sets/src/mage/cards/t/ThousandYearStorm.java b/Mage.Sets/src/mage/cards/t/ThousandYearStorm.java index ac79bf03d54f..a953b99f00aa 100644 --- a/Mage.Sets/src/mage/cards/t/ThousandYearStorm.java +++ b/Mage.Sets/src/mage/cards/t/ThousandYearStorm.java @@ -158,7 +158,7 @@ public ThousandYearStormWatcher() { public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { MageObject object = game.getObject(event.getTargetId()); - if (object != null && (object.isInstant() || object.isSorcery())) { + if (object != null && object.isInstantOrSorcery()) { UUID playerId = event.getPlayerId(); List spellsCast = spellsThisTurn.getOrDefault(playerId, new ArrayList()); spellsCast.add(new MageObjectReference(object, game)); diff --git a/Mage.Sets/src/mage/cards/t/ThrashOfRaptors.java b/Mage.Sets/src/mage/cards/t/ThrashOfRaptors.java index a1392c47b57b..ae96a235cf34 100644 --- a/Mage.Sets/src/mage/cards/t/ThrashOfRaptors.java +++ b/Mage.Sets/src/mage/cards/t/ThrashOfRaptors.java @@ -17,7 +17,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/t/ThrashingMudspawn.java b/Mage.Sets/src/mage/cards/t/ThrashingMudspawn.java index 17a8133b0ba5..7e1becc18ee3 100644 --- a/Mage.Sets/src/mage/cards/t/ThrashingMudspawn.java +++ b/Mage.Sets/src/mage/cards/t/ThrashingMudspawn.java @@ -1,11 +1,8 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DealtDamageToSourceTriggeredAbility; -import mage.constants.SubType; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.MorphAbility; @@ -13,11 +10,13 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class ThrashingMudspawn extends CardImpl { @@ -30,11 +29,13 @@ public ThrashingMudspawn(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(4); // Whenever Thrashing Mudspawn is dealt damage, you lose that much life. - Ability ability = new DealtDamageToSourceTriggeredAbility(new ThrashingMudspawnEffect(), false); + Ability ability = new DealtDamageToSourceTriggeredAbility( + new ThrashingMudspawnEffect(), false, false, true + ); this.addAbility(ability); // Morph {1}{B}{B} - this.addAbility(new MorphAbility(this, new ManaCostsImpl("{1}{B}{B}"))); + this.addAbility(new MorphAbility(this, new ManaCostsImpl<>("{1}{B}{B}"))); } @@ -66,14 +67,12 @@ public ThrashingMudspawnEffect copy() { @Override public boolean apply(Game game, Ability source) { - int amount = (Integer) getValue("damage"); - if (amount > 0) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - player.loseLife(amount, game, source, false); - return true; - } + Integer amount = (Integer) getValue("damage"); + Player player = game.getPlayer(source.getControllerId()); + if (amount == null || amount < 1 || player == null) { + return false; } - return false; + player.loseLife(amount, game, source, false); + return true; } } diff --git a/Mage.Sets/src/mage/cards/t/ThreadsOfDisloyalty.java b/Mage.Sets/src/mage/cards/t/ThreadsOfDisloyalty.java index 7568aede4d83..4e634833649e 100644 --- a/Mage.Sets/src/mage/cards/t/ThreadsOfDisloyalty.java +++ b/Mage.Sets/src/mage/cards/t/ThreadsOfDisloyalty.java @@ -16,7 +16,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -26,10 +26,10 @@ */ public final class ThreadsOfDisloyalty extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with converted mana cost 2 or less"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with mana value 2 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); } public ThreadsOfDisloyalty(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/t/ThrillingDiscovery.java b/Mage.Sets/src/mage/cards/t/ThrillingDiscovery.java new file mode 100644 index 000000000000..740c30eacfb7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThrillingDiscovery.java @@ -0,0 +1,39 @@ +package mage.cards.t; + +import mage.abilities.costs.common.DiscardTargetCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInHand; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ThrillingDiscovery extends CardImpl { + + public ThrillingDiscovery(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}{W}"); + + // You gain 2 life. Then you may discard two cards. If you do, draw three cards. + this.getSpellAbility().addEffect(new GainLifeEffect(2)); + this.getSpellAbility().addEffect(new DoIfCostPaid( + new DrawCardSourceControllerEffect(3), + new DiscardTargetCost(new TargetCardInHand(2, StaticFilters.FILTER_CARD)) + ).setText("Then you may discard two cards. If you do, draw three cards")); + } + + private ThrillingDiscovery(final ThrillingDiscovery card) { + super(card); + } + + @Override + public ThrillingDiscovery copy() { + return new ThrillingDiscovery(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/ThrillingEncore.java b/Mage.Sets/src/mage/cards/t/ThrillingEncore.java index 2dcfe48a9d7e..865b21b9ea5a 100644 --- a/Mage.Sets/src/mage/cards/t/ThrillingEncore.java +++ b/Mage.Sets/src/mage/cards/t/ThrillingEncore.java @@ -1,30 +1,31 @@ - package mage.cards.t; -import java.util.UUID; -import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.card.PutIntoGraveFromBattlefieldThisTurnPredicate; import mage.game.Game; -import mage.target.targetpointer.FixedTarget; +import mage.players.Player; import mage.watchers.common.CardsPutIntoGraveyardWatcher; +import java.util.UUID; + /** - * * @author L_J */ public final class ThrillingEncore extends CardImpl { public ThrillingEncore(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{B}"); - + // Put onto the battlefield under your control all creature cards in all graveyards that were put there from the battlefield this turn. this.getSpellAbility().addEffect(new ThrillingEncoreEffect()); this.getSpellAbility().addWatcher(new CardsPutIntoGraveyardWatcher()); @@ -41,36 +42,42 @@ public ThrillingEncore copy() { } class ThrillingEncoreEffect extends OneShotEffect { - - public ThrillingEncoreEffect() { + + private static final FilterCard filter = new FilterCreatureCard(); + + static { + filter.add(PutIntoGraveFromBattlefieldThisTurnPredicate.instance); + } + + ThrillingEncoreEffect() { super(Outcome.PutCardInPlay); - this.staticText = "Put onto the battlefield under your control all creature cards in all graveyards that were put there from the battlefield this turn"; + this.staticText = "Put onto the battlefield under your control all creature cards " + + "in all graveyards that were put there from the battlefield this turn"; } - - public ThrillingEncoreEffect(final ThrillingEncoreEffect effect) { + + private ThrillingEncoreEffect(final ThrillingEncoreEffect effect) { super(effect); } - + @Override public ThrillingEncoreEffect copy() { return new ThrillingEncoreEffect(this); } - + @Override public boolean apply(Game game, Ability source) { - CardsPutIntoGraveyardWatcher watcher = game.getState().getWatcher(CardsPutIntoGraveyardWatcher.class); - if (watcher != null) { - for (MageObjectReference mor : watcher.getCardsPutToGraveyardFromBattlefield()) { - if (game.getState().getZoneChangeCounter(mor.getSourceId()) == mor.getZoneChangeCounter()) { - Card card = mor.getCard(game); - if (card != null && card.isCreature()) { - Effect effect = new ReturnFromGraveyardToBattlefieldTargetEffect(); - effect.setTargetPointer(new FixedTarget(card, game)); - effect.apply(game, source); - } - } + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Cards cards = new CardsImpl(); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; } + cards.addAll(player.getGraveyard().getCards(filter, source.getSourceId(), playerId, game)); } - return true; + return controller.moveCards(cards, Zone.BATTLEFIELD, source, game); } } diff --git a/Mage.Sets/src/mage/cards/t/ThroneOfTheGodPharaoh.java b/Mage.Sets/src/mage/cards/t/ThroneOfTheGodPharaoh.java index f87a185021e1..55037f645d8d 100644 --- a/Mage.Sets/src/mage/cards/t/ThroneOfTheGodPharaoh.java +++ b/Mage.Sets/src/mage/cards/t/ThroneOfTheGodPharaoh.java @@ -1,8 +1,7 @@ - package mage.cards.t; -import java.util.UUID; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.LoseLifeOpponentsEffect; import mage.cards.CardImpl; @@ -14,25 +13,32 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.permanent.TappedPredicate; +import java.util.UUID; + /** - * * @author Styxo */ public final class ThroneOfTheGodPharaoh extends CardImpl { - private static final FilterPermanent filter = new FilterControlledCreaturePermanent("tapped creature you control"); + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); static { filter.add(TappedPredicate.instance); } + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + public ThroneOfTheGodPharaoh(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); addSuperType(SuperType.LEGENDARY); // At the beginning of your end step, each opponent loses life equal to the number of tapped creatures you control. - this.addAbility(new BeginningOfEndStepTriggeredAbility(new LoseLifeOpponentsEffect(new PermanentsOnBattlefieldCount(filter)), TargetController.YOU, false)); + this.addAbility(new BeginningOfEndStepTriggeredAbility( + new LoseLifeOpponentsEffect(xValue).setText( + "each opponent loses life equal to the number of tapped creatures you control" + ), TargetController.YOU, false + )); } private ThroneOfTheGodPharaoh(final ThroneOfTheGodPharaoh card) { diff --git a/Mage.Sets/src/mage/cards/t/ThryxTheSuddenStorm.java b/Mage.Sets/src/mage/cards/t/ThryxTheSuddenStorm.java index 422f71966b2d..a1d068a57245 100644 --- a/Mage.Sets/src/mage/cards/t/ThryxTheSuddenStorm.java +++ b/Mage.Sets/src/mage/cards/t/ThryxTheSuddenStorm.java @@ -14,7 +14,7 @@ import mage.filter.FilterCard; import mage.filter.FilterSpell; import mage.filter.predicate.Predicate; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; /** * @author TheElk801 @@ -25,7 +25,7 @@ public final class ThryxTheSuddenStorm extends CardImpl { private static final FilterSpell filter2 = new FilterSpell(); static { - Predicate predicate = new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 4); + Predicate predicate = new ManaValuePredicate(ComparisonType.MORE_THAN, 4); filter.add(predicate); filter2.add(predicate); } @@ -47,7 +47,7 @@ public ThryxTheSuddenStorm(UUID ownerId, CardSetInfo setInfo) { // Spells you cast with converted mana cost 5 or greater cost {1} less to cast and can't be countered. Ability ability = new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 1) - .setText("spells you cast with converted mana cost 5 or greater cost {1} less to cast")); + .setText("spells you cast with mana value 5 or greater cost {1} less to cast")); ability.addEffect(new CantBeCounteredControlledEffect( filter2, null, Duration.WhileOnBattlefield ).setText("and can't be countered")); diff --git a/Mage.Sets/src/mage/cards/t/ThundercloudElemental.java b/Mage.Sets/src/mage/cards/t/ThundercloudElemental.java index 1a924963b787..e0a0e42685ae 100644 --- a/Mage.Sets/src/mage/cards/t/ThundercloudElemental.java +++ b/Mage.Sets/src/mage/cards/t/ThundercloudElemental.java @@ -18,7 +18,7 @@ import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ToughnessPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/t/Thundermare.java b/Mage.Sets/src/mage/cards/t/Thundermare.java index a4986b24c55f..d25e51e23c77 100644 --- a/Mage.Sets/src/mage/cards/t/Thundermare.java +++ b/Mage.Sets/src/mage/cards/t/Thundermare.java @@ -11,7 +11,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/t/ThunderousOrator.java b/Mage.Sets/src/mage/cards/t/ThunderousOrator.java new file mode 100644 index 000000000000..ca16ff18ed78 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThunderousOrator.java @@ -0,0 +1,112 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.MageSingleton; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.*; +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.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.game.Game; + +import java.util.*; + +/** + * @author TheElk801 + */ +public final class ThunderousOrator extends CardImpl { + + public ThunderousOrator(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.KOR); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Whenever Thunderous Orator attacks, it gains flying until end of turn if you control a creature with flying. The same is true for first strike, double strike, deathtouch, indestructible, lifelink, menace, and trample. + this.addAbility(new AttacksTriggeredAbility(new ThunderousOratorEffect(), false)); + } + + private ThunderousOrator(final ThunderousOrator card) { + super(card); + } + + @Override + public ThunderousOrator copy() { + return new ThunderousOrator(this); + } +} + +class ThunderousOratorEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); + + static { + filter.add(new AbilityPredicate(MenaceAbility.class)); + } + + private static final List keywords = Arrays.asList( + FlyingAbility.getInstance(), + FirstStrikeAbility.getInstance(), + DoubleStrikeAbility.getInstance(), + DeathtouchAbility.getInstance(), + IndestructibleAbility.getInstance(), + LifelinkAbility.getInstance(), + TrampleAbility.getInstance() + ); + + ThunderousOratorEffect() { + super(Outcome.Benefit); + staticText = "it gains flying until end of turn if you control a creature with flying. The same is true " + + "for first strike, double strike, deathtouch, indestructible, lifelink, menace, and trample"; + } + + private ThunderousOratorEffect(final ThunderousOratorEffect effect) { + super(effect); + } + + @Override + public ThunderousOratorEffect copy() { + return new ThunderousOratorEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + List abilities = new ArrayList<>(); + if (game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game) > 0) { + abilities.add(new MenaceAbility()); + } + game.getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_CONTROLLED_CREATURE, + source.getControllerId(), source.getSourceId(), game + ).stream() + .filter(Objects::nonNull) + .map(p -> p.getAbilities(game)) + .flatMap(Collection::stream) + .filter(keywords::contains) + .distinct() + .forEach(abilities::add); + if (abilities.isEmpty()) { + return false; + } + for (Ability ability : abilities) { + game.addEffect(new GainAbilitySourceEffect(ability, Duration.EndOfTurn), source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/t/ThunderousSnapper.java b/Mage.Sets/src/mage/cards/t/ThunderousSnapper.java index 2a54b4a29508..d057be3bc008 100644 --- a/Mage.Sets/src/mage/cards/t/ThunderousSnapper.java +++ b/Mage.Sets/src/mage/cards/t/ThunderousSnapper.java @@ -9,7 +9,7 @@ import mage.constants.ComparisonType; import mage.constants.SubType; import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import java.util.UUID; @@ -18,10 +18,10 @@ */ public final class ThunderousSnapper extends CardImpl { - private static final FilterSpell filter = new FilterSpell("a spell with converted mana cost 5 or greater"); + private static final FilterSpell filter = new FilterSpell("a spell with mana value 5 or greater"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 4)); } public ThunderousSnapper(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/t/TianaShipsCaretaker.java b/Mage.Sets/src/mage/cards/t/TianaShipsCaretaker.java index 3357a9c93807..4ac19317258f 100644 --- a/Mage.Sets/src/mage/cards/t/TianaShipsCaretaker.java +++ b/Mage.Sets/src/mage/cards/t/TianaShipsCaretaker.java @@ -89,9 +89,7 @@ public boolean checkTrigger(GameEvent event, Game game) { } Permanent permanent = game.getPermanentOrLKIBattlefield(zEvent.getTarget().getId()); - if (permanent != null - && zEvent.getToZone() == Zone.GRAVEYARD - && zEvent.getFromZone() == Zone.BATTLEFIELD + if (permanent != null && zEvent.isDiesEvent() && (permanent.isArtifact() && permanent.hasSubtype(SubType.EQUIPMENT, game) || permanent.isEnchantment() && permanent.hasSubtype(SubType.AURA, game)) && permanent.isControlledBy(this.controllerId)) { diff --git a/Mage.Sets/src/mage/cards/t/TideforceElemental.java b/Mage.Sets/src/mage/cards/t/TideforceElemental.java index 303c1c3f4e67..bf19b55ea2b6 100644 --- a/Mage.Sets/src/mage/cards/t/TideforceElemental.java +++ b/Mage.Sets/src/mage/cards/t/TideforceElemental.java @@ -17,7 +17,7 @@ import mage.constants.ColoredManaSymbol; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/t/TilonallisSkinshifter.java b/Mage.Sets/src/mage/cards/t/TilonallisSkinshifter.java index ef161eeda5d6..61e0995a0318 100644 --- a/Mage.Sets/src/mage/cards/t/TilonallisSkinshifter.java +++ b/Mage.Sets/src/mage/cards/t/TilonallisSkinshifter.java @@ -16,7 +16,7 @@ import mage.constants.SuperType; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.AttackingPredicate; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/t/TimberProtector.java b/Mage.Sets/src/mage/cards/t/TimberProtector.java index 4d99c51bcab2..35ab5f860e87 100644 --- a/Mage.Sets/src/mage/cards/t/TimberProtector.java +++ b/Mage.Sets/src/mage/cards/t/TimberProtector.java @@ -17,7 +17,7 @@ import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/t/TimberShredder.java b/Mage.Sets/src/mage/cards/t/TimberShredder.java index b1616afe509c..186ba31618f5 100644 --- a/Mage.Sets/src/mage/cards/t/TimberShredder.java +++ b/Mage.Sets/src/mage/cards/t/TimberShredder.java @@ -1,29 +1,22 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.keyword.TrampleAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** - * * @author fireshoes */ public final class TimberShredder extends CardImpl { public TimberShredder(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.WEREWOLF); this.power = new MageInt(4); this.toughness = new MageInt(2); @@ -37,8 +30,7 @@ public TimberShredder(UUID ownerId, CardSetInfo setInfo) { this.addAbility(TrampleAbility.getInstance()); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Timber Shredder. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private TimberShredder(final TimberShredder card) { diff --git a/Mage.Sets/src/mage/cards/t/Timbermare.java b/Mage.Sets/src/mage/cards/t/Timbermare.java index cab292c920bd..07b038c034d1 100644 --- a/Mage.Sets/src/mage/cards/t/Timbermare.java +++ b/Mage.Sets/src/mage/cards/t/Timbermare.java @@ -12,7 +12,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/t/TimeReversal.java b/Mage.Sets/src/mage/cards/t/TimeReversal.java index 60917a985b81..b2b234d5b4a2 100644 --- a/Mage.Sets/src/mage/cards/t/TimeReversal.java +++ b/Mage.Sets/src/mage/cards/t/TimeReversal.java @@ -24,7 +24,7 @@ public TimeReversal(UUID ownerId, CardSetInfo setInfo) { Effect effect = new DrawCardAllEffect(7); effect.setText(", then draws seven cards"); this.getSpellAbility().addEffect(effect); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private TimeReversal(final TimeReversal card) { diff --git a/Mage.Sets/src/mage/cards/t/TimeSpiral.java b/Mage.Sets/src/mage/cards/t/TimeSpiral.java index c52e6caf469f..7ea1050374dd 100644 --- a/Mage.Sets/src/mage/cards/t/TimeSpiral.java +++ b/Mage.Sets/src/mage/cards/t/TimeSpiral.java @@ -26,7 +26,7 @@ public TimeSpiral(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{U}{U}"); // Exile Time Spiral. Each player shuffles their graveyard and hand into their library, then draws seven cards. You untap up to six lands. - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); this.getSpellAbility().addEffect(new TimeSpiralEffect()); Effect effect = new DrawCardAllEffect(7); effect.setText(", then draws seven cards"); diff --git a/Mage.Sets/src/mage/cards/t/Timebender.java b/Mage.Sets/src/mage/cards/t/Timebender.java index 75ab3efbd603..6fba549bd179 100644 --- a/Mage.Sets/src/mage/cards/t/Timebender.java +++ b/Mage.Sets/src/mage/cards/t/Timebender.java @@ -1,7 +1,5 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.Mode; @@ -13,21 +11,30 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.counters.CounterType; +import mage.filter.common.FilterPermanentOrSuspendedCard; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetPermanentOrSuspendedCard; +import java.util.UUID; + /** - * * @author L_J */ public final class Timebender extends CardImpl { + private static final FilterPermanentOrSuspendedCard filter + = new FilterPermanentOrSuspendedCard("permanent with a time counter on it or suspended card"); + + static { + filter.getPermanentFilter().add(CounterType.TIME.getPredicate()); + } + public Timebender(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WIZARD); this.power = new MageInt(1); @@ -42,9 +49,8 @@ public Timebender(UUID ownerId, CardSetInfo setInfo) { ability.addTarget(new TargetPermanentOrSuspendedCard()); // Put two time counters on target permanent with a time counter on it or suspended card. - Mode mode = new Mode(); - mode.addEffect(new TimebenderEffect(true)); - mode.addTarget(new TargetPermanentOrSuspendedCard()); + Mode mode = new Mode(new TimebenderEffect(true)); + mode.addTarget(new TargetPermanentOrSuspendedCard(filter, false)); ability.addMode(mode); ability.getModes().addMode(mode); this.addAbility(ability); @@ -62,20 +68,20 @@ public Timebender copy() { } class TimebenderEffect extends OneShotEffect { - + private final boolean addCounters; - public TimebenderEffect(boolean addCounters) { + TimebenderEffect(boolean addCounters) { super(Outcome.Benefit); this.addCounters = addCounters; if (addCounters) { - this.staticText = "put two time counters on target permanent or suspended card"; + this.staticText = "put two time counters on target permanent with a time counter on it or suspended card"; } else { this.staticText = "remove two time counters from target permanent or suspended card"; } } - public TimebenderEffect(final TimebenderEffect effect) { + private TimebenderEffect(final TimebenderEffect effect) { super(effect); this.addCounters = effect.addCounters; } diff --git a/Mage.Sets/src/mage/cards/t/TimelyHordemate.java b/Mage.Sets/src/mage/cards/t/TimelyHordemate.java index b51afe606f44..b34056c5a544 100644 --- a/Mage.Sets/src/mage/cards/t/TimelyHordemate.java +++ b/Mage.Sets/src/mage/cards/t/TimelyHordemate.java @@ -14,7 +14,7 @@ import mage.constants.ComparisonType; import mage.constants.SubType; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInYourGraveyard; import mage.watchers.common.PlayerAttackedWatcher; @@ -25,10 +25,10 @@ */ public final class TimelyHordemate extends CardImpl { - private static final FilterCreatureCard filter = new FilterCreatureCard("creature card with converted mana cost 2 or less from your graveyard"); + private static final FilterCreatureCard filter = new FilterCreatureCard("creature card with mana value 2 or less from your graveyard"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); } public TimelyHordemate(UUID ownerId, CardSetInfo setInfo) { @@ -41,7 +41,7 @@ public TimelyHordemate(UUID ownerId, CardSetInfo setInfo) { // Raid — When Timely Hordemate enters the battlefield, if you attacked this turn, return target creature card with converted mana cost 2 or less from your graveyard to the battlefield. Ability ability = new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect()), RaidCondition.instance, - "Raid — When {this} enters the battlefield, if you attacked this turn, return target creature card with converted mana cost 2 or less from your graveyard to the battlefield."); + "Raid — When {this} enters the battlefield, if you attacked this turn, return target creature card with mana value 2 or less from your graveyard to the battlefield."); ability.addTarget(new TargetCardInYourGraveyard(filter)); ability.setAbilityWord(AbilityWord.RAID); ability.addHint(RaidHint.instance); diff --git a/Mage.Sets/src/mage/cards/t/TimelyWard.java b/Mage.Sets/src/mage/cards/t/TimelyWard.java index 7aac35dac340..70e434187313 100644 --- a/Mage.Sets/src/mage/cards/t/TimelyWard.java +++ b/Mage.Sets/src/mage/cards/t/TimelyWard.java @@ -14,7 +14,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.CommanderPredicate; +import mage.filter.predicate.mageobject.CommanderPredicate; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/t/Timesifter.java b/Mage.Sets/src/mage/cards/t/Timesifter.java index 9ff62044f803..d084aa2eb4ba 100644 --- a/Mage.Sets/src/mage/cards/t/Timesifter.java +++ b/Mage.Sets/src/mage/cards/t/Timesifter.java @@ -29,7 +29,7 @@ public Timesifter(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{5}"); // At the beginning of each upkeep, each player exiles the top card of their library. The player who exiled the card with the highest converted mana cost takes an extra turn after this one. If two or more players' cards are tied for highest cost, the tied players repeat this process until the tie is broken. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new TimesifterEffect(), TargetController.ANY, false)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new TimesifterEffect(), TargetController.EACH_PLAYER, false)); } private Timesifter(final Timesifter card) { @@ -46,7 +46,7 @@ class TimesifterEffect extends OneShotEffect { TimesifterEffect() { super(Outcome.ExtraTurn); - this.staticText = "each player exiles the top card of their library. The player who exiled the card with the highest converted mana cost takes an extra turn after this one. If two or more players' cards are tied for highest cost, the tied players repeat this process until the tie is broken"; + this.staticText = "each player exiles the top card of their library. The player who exiled the card with the highest mana value takes an extra turn after this one. If two or more players' cards are tied for highest, the tied players repeat this process until the tie is broken"; } TimesifterEffect(final TimesifterEffect effect) { @@ -69,7 +69,7 @@ public boolean apply(Game game, Ability source) { if (player != null) { Card card = player.getLibrary().getFromTop(game); if (card != null) { - int cardCMC = card.getConvertedManaCost(); + int cardCMC = card.getManaValue(); player.moveCardsToExile(card, source, game, true, null, ""); if (cardCMC > highestCMC) { highestCMC = cardCMC; diff --git a/Mage.Sets/src/mage/cards/t/TimidDrake.java b/Mage.Sets/src/mage/cards/t/TimidDrake.java index db36a29e1812..109c69a5f4a2 100644 --- a/Mage.Sets/src/mage/cards/t/TimidDrake.java +++ b/Mage.Sets/src/mage/cards/t/TimidDrake.java @@ -12,7 +12,7 @@ import mage.constants.CardType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/t/TitaniasSong.java b/Mage.Sets/src/mage/cards/t/TitaniasSong.java index fada1936ba27..00ff36cf5028 100644 --- a/Mage.Sets/src/mage/cards/t/TitaniasSong.java +++ b/Mage.Sets/src/mage/cards/t/TitaniasSong.java @@ -55,7 +55,7 @@ class TitaniasSongEffect extends ContinuousEffectImpl { public TitaniasSongEffect(Duration duration) { super(duration, Outcome.BecomeCreature); - staticText = "Each noncreature artifact loses its abilities and is an artifact creature with power and toughness each equal to its converted mana cost"; + staticText = "Each noncreature artifact loses its abilities and is an artifact creature with power and toughness each equal to its mana value"; } public TitaniasSongEffect(final TitaniasSongEffect effect) { @@ -94,7 +94,7 @@ public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) for (Iterator it = affectedObjectList.iterator(); it.hasNext();) { Permanent permanent = it.next().getPermanent(game); if (permanent != null) { - int manaCost = permanent.getConvertedManaCost(); + int manaCost = permanent.getManaValue(); permanent.getPower().setValue(manaCost); permanent.getToughness().setValue(manaCost); } diff --git a/Mage.Sets/src/mage/cards/t/Tithe.java b/Mage.Sets/src/mage/cards/t/Tithe.java index 107b59f76d61..22784469ce4f 100644 --- a/Mage.Sets/src/mage/cards/t/Tithe.java +++ b/Mage.Sets/src/mage/cards/t/Tithe.java @@ -49,7 +49,7 @@ class TitheEffect extends OneShotEffect { TitheEffect() { super(Outcome.Benefit); - this.staticText = "Search your library for a Plains card. If target opponent controls more lands than you, you may search your library for an additional Plains card. Reveal those cards and put them into your hand. Then shuffle your library"; + this.staticText = "Search your library for a Plains card. If target opponent controls more lands than you, you may search your library for an additional Plains card. Reveal those cards, put them into your hand, then shuffle"; } TitheEffect(final TitheEffect effect) { diff --git a/Mage.Sets/src/mage/cards/t/TivashGloomSummoner.java b/Mage.Sets/src/mage/cards/t/TivashGloomSummoner.java new file mode 100644 index 000000000000..c47e866568ea --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TivashGloomSummoner.java @@ -0,0 +1,97 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.YouGainedLifeCondition; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.dynamicvalue.common.ControllerGotLifeCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.permanent.token.PromiseOfPowerDemonToken; +import mage.players.Player; +import mage.watchers.common.PlayerGainedLifeWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TivashGloomSummoner extends CardImpl { + + private static final Condition condition = new YouGainedLifeCondition(ComparisonType.MORE_THAN, 0); + + public TivashGloomSummoner(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // At the beginning of your end step, if you gained life this turn, you may pay X life, where X is the amount of life you gained this turn. If you do, create an X/X black Demon creature token with flying. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + Zone.BATTLEFIELD, new TivashGloomSummonerEffect(), + TargetController.YOU, condition, false + ).addHint(ControllerGotLifeCount.getHint()), new PlayerGainedLifeWatcher()); + } + + private TivashGloomSummoner(final TivashGloomSummoner card) { + super(card); + } + + @Override + public TivashGloomSummoner copy() { + return new TivashGloomSummoner(this); + } +} + +class TivashGloomSummonerEffect extends OneShotEffect { + + TivashGloomSummonerEffect() { + super(Outcome.Benefit); + staticText = "you may pay X life, where X is the amount of life you gained this turn. " + + "If you do, create an X/X black Demon creature token with flying"; + } + + private TivashGloomSummonerEffect(final TivashGloomSummonerEffect effect) { + super(effect); + } + + @Override + public TivashGloomSummonerEffect copy() { + return new TivashGloomSummonerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + PlayerGainedLifeWatcher watcher = game.getState().getWatcher(PlayerGainedLifeWatcher.class); + if (player == null || watcher == null) { + return false; + } + int lifeGained = watcher.getLifeGained(source.getControllerId()); + Cost cost = new PayLifeCost(lifeGained); + if (!cost.canPay( + source, source, source.getControllerId(), game + ) || !player.chooseUse( + Outcome.PutCreatureInPlay, "Pay " + lifeGained + " life?", source, game + ) || !cost.pay( + source, game, source, source.getControllerId(), true + )) { + return false; + } + new PromiseOfPowerDemonToken(lifeGained).putOntoBattlefield(1, game, source, source.getControllerId()); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TobiasBeckett.java b/Mage.Sets/src/mage/cards/t/TobiasBeckett.java index 80b987d7192a..148711654509 100644 --- a/Mage.Sets/src/mage/cards/t/TobiasBeckett.java +++ b/Mage.Sets/src/mage/cards/t/TobiasBeckett.java @@ -83,7 +83,7 @@ public boolean apply(Game game, Ability source) { if (card.getSpellAbility() != null) { // allow to cast the card // and you may spend mana as though it were mana of any color to cast it - CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.Custom); + CardUtil.makeCardPlayable(game, source, card, Duration.Custom, true); } } return true; diff --git a/Mage.Sets/src/mage/cards/t/Tolaria.java b/Mage.Sets/src/mage/cards/t/Tolaria.java index d497cb55e91c..7f5d345110ce 100644 --- a/Mage.Sets/src/mage/cards/t/Tolaria.java +++ b/Mage.Sets/src/mage/cards/t/Tolaria.java @@ -35,7 +35,7 @@ public Tolaria(UUID ownerId, CardSetInfo setInfo) { // {T}: Target creature loses banding and all "bands with other" abilities until end of turn. Activate this ability only during any upkeep step. ActivatedAbilityImpl ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new LoseAbilityTargetEffect(BandingAbility.getInstance(), Duration.EndOfTurn), new TapSourceCost(), new IsStepCondition(PhaseStep.UPKEEP, false), - "{T}: Target creature loses banding and all \"bands with other\" abilities until end of turn. Activate this ability only during any upkeep step."); + "{T}: Target creature loses banding and all \"bands with other\" abilities until end of turn. Activate only during any upkeep step."); ability.addEffect(new LoseAbilityTargetEffect(new BandsWithOtherAbility(), Duration.EndOfTurn)); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/t/TomeOfLegends.java b/Mage.Sets/src/mage/cards/t/TomeOfLegends.java index a3f2e15a96fa..35e6bf1f55b4 100644 --- a/Mage.Sets/src/mage/cards/t/TomeOfLegends.java +++ b/Mage.Sets/src/mage/cards/t/TomeOfLegends.java @@ -15,7 +15,7 @@ import mage.constants.TargetController; import mage.counters.CounterType; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.CommanderPredicate; +import mage.filter.predicate.mageobject.CommanderPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/t/TomeShredder.java b/Mage.Sets/src/mage/cards/t/TomeShredder.java new file mode 100644 index 000000000000..58603883d196 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TomeShredder.java @@ -0,0 +1,55 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.ExileFromGraveCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterCard; +import mage.filter.common.FilterInstantOrSorceryCard; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TomeShredder extends CardImpl { + + private static final FilterCard filter + = new FilterInstantOrSorceryCard("an instant or sorcery card from your graveyard"); + + public TomeShredder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.WOLF); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // {T}, Exile an instant or sorcery card from your graveyard: Put a +1/+1 counter on Tome Shredder. + Ability ability = new SimpleActivatedAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new TapSourceCost() + ); + ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(filter))); + this.addAbility(ability); + } + + private TomeShredder(final TomeShredder card) { + super(card); + } + + @Override + public TomeShredder copy() { + return new TomeShredder(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/ToothAndNail.java b/Mage.Sets/src/mage/cards/t/ToothAndNail.java index 663ad56c1838..cd1ef626fe6d 100644 --- a/Mage.Sets/src/mage/cards/t/ToothAndNail.java +++ b/Mage.Sets/src/mage/cards/t/ToothAndNail.java @@ -30,7 +30,7 @@ public ToothAndNail(UUID ownerId, CardSetInfo setInfo) { // Choose one - // Search your library for up to two creature cards, reveal them, put them into your hand, then shuffle your library; - this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 2, StaticFilters.FILTER_CARD_CREATURE), true)); + this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 2, StaticFilters.FILTER_CARD_CREATURES), true)); // or put up to two creature cards from your hand onto the battlefield. Mode mode = new Mode(); mode.addEffect(new ToothAndNailPutCreatureOnBattlefieldEffect()); diff --git a/Mage.Sets/src/mage/cards/t/ToralfGodOfFury.java b/Mage.Sets/src/mage/cards/t/ToralfGodOfFury.java index fdc9c4d7cb11..a9ea1493fead 100644 --- a/Mage.Sets/src/mage/cards/t/ToralfGodOfFury.java +++ b/Mage.Sets/src/mage/cards/t/ToralfGodOfFury.java @@ -103,12 +103,7 @@ private ToralfGodOfFuryTriggeredAbility(final ToralfGodOfFuryTriggeredAbility ab @Override public boolean checkEventType(GameEvent event, Game game) { - switch (event.getType()) { - case DAMAGED_CREATURE: - case DAMAGED_PLANESWALKER: - return true; - } - return false; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/t/TorbranThaneOfRedFell.java b/Mage.Sets/src/mage/cards/t/TorbranThaneOfRedFell.java index 418850d4f352..2a07471bfb2c 100644 --- a/Mage.Sets/src/mage/cards/t/TorbranThaneOfRedFell.java +++ b/Mage.Sets/src/mage/cards/t/TorbranThaneOfRedFell.java @@ -65,8 +65,7 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { @Override public boolean checksEventType(GameEvent event, Game game) { switch (event.getType()) { - case DAMAGE_CREATURE: - case DAMAGE_PLANESWALKER: + case DAMAGE_PERMANENT: case DAMAGE_PLAYER: return true; default: diff --git a/Mage.Sets/src/mage/cards/t/TorchCourier.java b/Mage.Sets/src/mage/cards/t/TorchCourier.java index 6730e16d8c44..d97f2b033fb7 100644 --- a/Mage.Sets/src/mage/cards/t/TorchCourier.java +++ b/Mage.Sets/src/mage/cards/t/TorchCourier.java @@ -14,7 +14,7 @@ import mage.constants.Duration; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; /** diff --git a/Mage.Sets/src/mage/cards/t/TormentOfScarabs.java b/Mage.Sets/src/mage/cards/t/TormentOfScarabs.java index 8bcbeaf4b77d..ac2d734da377 100644 --- a/Mage.Sets/src/mage/cards/t/TormentOfScarabs.java +++ b/Mage.Sets/src/mage/cards/t/TormentOfScarabs.java @@ -1,8 +1,7 @@ - package mage.cards.t; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BeginningOfUpkeepAttachedTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.keyword.EnchantAbility; @@ -11,11 +10,8 @@ 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.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; @@ -25,7 +21,6 @@ import java.util.UUID; /** - * * @author LevelX2 */ public final class TormentOfScarabs extends CardImpl { @@ -43,7 +38,7 @@ public TormentOfScarabs(UUID ownerId, CardSetInfo setInfo) { this.addAbility(ability); // At the beginning of enchanted player's upkeep, that player loses 3 life unless they sacrifice a nonland permanent or discards a card. - this.addAbility(new TormentOfScarabsAbility()); + this.addAbility(new BeginningOfUpkeepAttachedTriggeredAbility(new TormentOfScarabsEffect())); } private TormentOfScarabs(final TormentOfScarabs card) { @@ -56,52 +51,14 @@ public TormentOfScarabs copy() { } } -class TormentOfScarabsAbility extends TriggeredAbilityImpl { - - public TormentOfScarabsAbility() { - super(Zone.BATTLEFIELD, new TormentOfScarabsEffect()); - } - - public TormentOfScarabsAbility(final TormentOfScarabsAbility ability) { - super(ability); - } - - @Override - public TormentOfScarabsAbility copy() { - return new TormentOfScarabsAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.UPKEEP_STEP_PRE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent enchantment = game.getPermanent(this.getSourceId()); - if (enchantment != null && enchantment.getAttachedTo() != null) { - if (game.isActivePlayer(enchantment.getAttachedTo())) { - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "At the beginning of enchanted player's upkeep, that player loses 3 life unless they sacrifice a nonland permanent or discards a card."; - } - -} - class TormentOfScarabsEffect extends OneShotEffect { - public TormentOfScarabsEffect() { + TormentOfScarabsEffect() { super(Outcome.LoseLife); - this.staticText = "that player loses 3 life unless they sacrifice a nonland permanent or discards a card"; + this.staticText = "that player loses 3 life unless they sacrifice a nonland permanent or discard a card"; } - public TormentOfScarabsEffect(final TormentOfScarabsEffect effect) { + private TormentOfScarabsEffect(final TormentOfScarabsEffect effect) { super(effect); } @@ -112,33 +69,28 @@ public TormentOfScarabsEffect copy() { @Override public boolean apply(Game game, Ability source) { - Permanent enchantment = game.getPermanent(source.getSourceId()); - if (enchantment == null || enchantment.getAttachedTo() == null) { + Player enchantedPlayer = game.getPlayer(targetPointer.getFirst(game, source)); + if (enchantedPlayer == null) { return false; } - Player enchantedPlayer = game.getPlayer(enchantment.getAttachedTo()); - if (enchantedPlayer != null) { - int permanents = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_NON_LAND, enchantedPlayer.getId(), game); - if (permanents > 0 && enchantedPlayer.chooseUse(outcome, "Sacrifice a nonland permanent?", - "Otherwise you have to discard a card or lose 3 life.", "Sacrifice", "Discard or life loss", source, game)) { - Target target = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_NON_LAND); - if (enchantedPlayer.choose(outcome, target, source.getSourceId(), game)) { - Permanent permanent = game.getPermanent(target.getFirstTarget()); - if (permanent != null) { - permanent.sacrifice(source, game); - return true; - } + int permanents = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_NON_LAND, enchantedPlayer.getId(), game); + if (permanents > 0 && enchantedPlayer.chooseUse(outcome, "Sacrifice a nonland permanent?", + "Otherwise you have to discard a card or lose 3 life.", "Sacrifice", "Discard or life loss", source, game)) { + Target target = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_NON_LAND); + if (enchantedPlayer.choose(outcome, target, source.getSourceId(), game)) { + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent != null) { + permanent.sacrifice(source, game); + return true; } } - if (!enchantedPlayer.getHand().isEmpty() && enchantedPlayer.chooseUse(outcome, "Discard a card?", - "Otherwise you lose 3 life.", "Discard", "Lose 3 life", source, game)) { - enchantedPlayer.discardOne(false, false, source, game); - return true; - } - enchantedPlayer.loseLife(3, game, source, false); + } + if (!enchantedPlayer.getHand().isEmpty() && enchantedPlayer.chooseUse(outcome, "Discard a card?", + "Otherwise you lose 3 life.", "Discard", "Lose 3 life", source, game)) { + enchantedPlayer.discardOne(false, false, source, game); return true; } - - return false; + enchantedPlayer.loseLife(3, game, source, false); + return true; } } diff --git a/Mage.Sets/src/mage/cards/t/TormentedPariah.java b/Mage.Sets/src/mage/cards/t/TormentedPariah.java index 4fc65b3eb941..36a88c23c02d 100644 --- a/Mage.Sets/src/mage/cards/t/TormentedPariah.java +++ b/Mage.Sets/src/mage/cards/t/TormentedPariah.java @@ -1,20 +1,14 @@ - package mage.cards.t; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** * @author nantuko @@ -35,8 +29,7 @@ public TormentedPariah(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each upkeep, if no spells were cast last turn, transform Tormented Pariah. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private TormentedPariah(final TormentedPariah card) { diff --git a/Mage.Sets/src/mage/cards/t/TormentorsTrident.java b/Mage.Sets/src/mage/cards/t/TormentorsTrident.java index 5f4be14b162d..9b1eb3a285d0 100644 --- a/Mage.Sets/src/mage/cards/t/TormentorsTrident.java +++ b/Mage.Sets/src/mage/cards/t/TormentorsTrident.java @@ -32,7 +32,7 @@ public TormentorsTrident(UUID ownerId, CardSetInfo setInfo) { // Equipped creature gets +3/+0 and attacks each turn if able. Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(3, 0)); Effect effect = new AttacksIfAbleAttachedEffect(Duration.WhileOnBattlefield, AttachmentType.EQUIPMENT); - effect.setText("and attacks each turn if able"); + effect.setText("and attacks each combat if able"); ability.addEffect(effect); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/t/TormodTheDesecrator.java b/Mage.Sets/src/mage/cards/t/TormodTheDesecrator.java index da7cc61e5390..aece5ad5702a 100644 --- a/Mage.Sets/src/mage/cards/t/TormodTheDesecrator.java +++ b/Mage.Sets/src/mage/cards/t/TormodTheDesecrator.java @@ -1,22 +1,16 @@ package mage.cards.t; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.CardsLeaveGraveyardTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.PartnerAbility; -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.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeGroupEvent; import mage.game.permanent.token.ZombieToken; -import java.util.Objects; import java.util.UUID; /** @@ -34,7 +28,9 @@ public TormodTheDesecrator(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(2); // Whenever one or more cards leave your graveyard, create a tapped 2/2 black Zombie creature token. - this.addAbility(new TormodTheDesecratorTriggeredAbility()); + this.addAbility(new CardsLeaveGraveyardTriggeredAbility( + new CreateTokenEffect(new ZombieToken(), 1, true, false) + )); // Partner this.addAbility(PartnerAbility.getInstance()); @@ -49,44 +45,3 @@ public TormodTheDesecrator copy() { return new TormodTheDesecrator(this); } } - -class TormodTheDesecratorTriggeredAbility extends TriggeredAbilityImpl { - - TormodTheDesecratorTriggeredAbility() { - super(Zone.BATTLEFIELD, new CreateTokenEffect(new ZombieToken(), 1, true, false), false); - } - - private TormodTheDesecratorTriggeredAbility(final TormodTheDesecratorTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ZONE_CHANGE_GROUP; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - ZoneChangeGroupEvent zEvent = (ZoneChangeGroupEvent) event; - return zEvent != null - && Zone.GRAVEYARD == zEvent.getFromZone() - && Zone.GRAVEYARD != zEvent.getToZone() - && zEvent.getCards() != null - && zEvent.getCards() - .stream() - .filter(Objects::nonNull) - .map(Card::getOwnerId) - .anyMatch(getControllerId()::equals); - } - - @Override - public TormodTheDesecratorTriggeredAbility copy() { - return new TormodTheDesecratorTriggeredAbility(this); - } - - @Override - public String getRule() { - return "Whenever one or more cards leave your graveyard, " + - "create a tapped 2/2 black Zombie creature token."; - } -} diff --git a/Mage.Sets/src/mage/cards/t/TorrentOfFire.java b/Mage.Sets/src/mage/cards/t/TorrentOfFire.java index d0a161c5523b..ad3183cc1992 100644 --- a/Mage.Sets/src/mage/cards/t/TorrentOfFire.java +++ b/Mage.Sets/src/mage/cards/t/TorrentOfFire.java @@ -2,7 +2,7 @@ package mage.cards.t; import java.util.UUID; -import mage.abilities.dynamicvalue.common.HighestConvertedManaCostValue; +import mage.abilities.dynamicvalue.common.HighestManaValueCount; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -19,8 +19,8 @@ public TorrentOfFire(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}{R}"); // Torrent of Fire deals damage equal to the highest converted mana cost among permanents you control to any target. - this.getSpellAbility().addEffect(new DamageTargetEffect(new HighestConvertedManaCostValue()) - .setText("{this} deals damage to any target equal to the highest converted mana cost among permanents you control.") + this.getSpellAbility().addEffect(new DamageTargetEffect(new HighestManaValueCount()) + .setText("{this} deals damage to any target equal to the highest mana value among permanents you control.") ); this.getSpellAbility().addTarget(new TargetAnyTarget()); } diff --git a/Mage.Sets/src/mage/cards/t/TorrentSculptor.java b/Mage.Sets/src/mage/cards/t/TorrentSculptor.java new file mode 100644 index 000000000000..9ac297f97be8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TorrentSculptor.java @@ -0,0 +1,152 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.keyword.WardAbility; +import mage.cards.Card; +import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.TargetPermanent; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +import static mage.constants.Outcome.Benefit; + +/** + * @author TheElk801 + */ +public final class TorrentSculptor extends ModalDoubleFacesCard { + + public TorrentSculptor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.MERFOLK, SubType.WIZARD}, "{2}{U}{U}", + "Flamethrower Sonata", new CardType[]{CardType.SORCERY}, new SubType[]{}, "{1}{R}"); + + // 1. + // Torrent Sculptor + // Creature - Merfolk Wizard + this.getLeftHalfCard().setPT(2, 2); + + // Ward {2} + this.getLeftHalfCard().addAbility(new WardAbility(new ManaCostsImpl<>("{2}"))); + + // When Torrent Sculptor enters the battlefield, exile an instant or sorcery card from your graveyard. Put a number of +1/+1 counters on Torrent Sculptor equal to half that card's mana value, rounded up. + this.getLeftHalfCard().addAbility(new EntersBattlefieldTriggeredAbility(new TorrentSculptorEffect())); + + // 2. + // Flamethrower Sonata + // Sorcery + // Discard a card, then draw a card. When you discard an instant or sorcery card this way, Flamethrower Sonata deals damage equal to that card's mana value to target creature or planeswalker you don't control. + this.getRightHalfCard().getSpellAbility().addEffect(new FlamethrowerSonataEffect()); + } + + private TorrentSculptor(final TorrentSculptor card) { + super(card); + } + + @Override + public TorrentSculptor copy() { + return new TorrentSculptor(this); + } +} + +class TorrentSculptorEffect extends OneShotEffect { + + TorrentSculptorEffect() { + super(Benefit); + staticText = "exile an instant or sorcery card from your graveyard. " + + "Put a number of +1/+1 counters on {this} equal to half that card's mana value, rounded up."; + } + + private TorrentSculptorEffect(final TorrentSculptorEffect effect) { + super(effect); + } + + @Override + public TorrentSculptorEffect copy() { + return new TorrentSculptorEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null || player.getGraveyard().count(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, game) < 1) { + return false; + } + TargetCard targetCard = new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY); + targetCard.setNotTarget(true); + player.choose(Outcome.Exile, targetCard, source.getSourceId(), game); + Card card = game.getCard(targetCard.getFirstTarget()); + if (card == null) { + return false; + } + int counters = card.getManaValue(); + counters = Math.floorDiv(counters, 2) + counters % 2; + player.moveCards(card, Zone.EXILED, source, game); + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent != null) { + permanent.addCounters(CounterType.P1P1.createInstance(counters), player.getId(), source, game); + } + return true; + } +} + +class FlamethrowerSonataEffect extends OneShotEffect { + + private static final FilterPermanent filter + = new FilterCreatureOrPlaneswalkerPermanent("creature or planeswalker you don't control"); + + static { + filter.add(TargetController.NOT_YOU.getControllerPredicate()); + } + + FlamethrowerSonataEffect() { + super(Outcome.Benefit); + staticText = "Discard a card, then draw a card. When you discard an instant or sorcery card this way, " + + "{this} deals damage equal to that card's mana value to target creature or planeswalker you don't control."; + } + + private FlamethrowerSonataEffect(final FlamethrowerSonataEffect effect) { + super(effect); + } + + @Override + public FlamethrowerSonataEffect copy() { + return new FlamethrowerSonataEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Card card = player.discardOne(false, false, source, game); + player.drawCards(1, source, game); + if (card == null || !card.isInstantOrSorcery()) { + return true; + } + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new DamageTargetEffect(card.getManaValue()), + false, "{this} deals damage equal to that card's mana value " + + "to target creature or planeswalker you don't control" + ); + ability.addTarget(new TargetPermanent(filter)); + game.fireReflexiveTriggeredAbility(ability, source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TortoiseFormation.java b/Mage.Sets/src/mage/cards/t/TortoiseFormation.java index cf5e90074d68..06be216742a0 100644 --- a/Mage.Sets/src/mage/cards/t/TortoiseFormation.java +++ b/Mage.Sets/src/mage/cards/t/TortoiseFormation.java @@ -1,27 +1,28 @@ - package mage.cards.t; -import java.util.UUID; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.ShroudAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author North */ public final class TortoiseFormation extends CardImpl { public TortoiseFormation(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}"); // Creatures you control gain shroud until end of turn. - this.getSpellAbility().addEffect(new GainAbilityControlledEffect(ShroudAbility.getInstance(), Duration.EndOfTurn, new FilterCreaturePermanent())); + this.getSpellAbility().addEffect(new GainAbilityControlledEffect( + ShroudAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_CONTROLLED_CREATURES + )); } private TortoiseFormation(final TortoiseFormation card) { diff --git a/Mage.Sets/src/mage/cards/t/TovolarsMagehunter.java b/Mage.Sets/src/mage/cards/t/TovolarsMagehunter.java index ae8c5e38f2e8..046a6da18ad7 100644 --- a/Mage.Sets/src/mage/cards/t/TovolarsMagehunter.java +++ b/Mage.Sets/src/mage/cards/t/TovolarsMagehunter.java @@ -1,35 +1,27 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; -import mage.abilities.effects.common.TransformSourceEffect; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author North */ public final class TovolarsMagehunter extends CardImpl { public TovolarsMagehunter(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.WEREWOLF); this.color.setRed(true); @@ -42,11 +34,9 @@ public TovolarsMagehunter(UUID ownerId, CardSetInfo setInfo) { // Whenever an opponent casts a spell, Tovolar's Magehunter deals 2 damage to that player. this.addAbility(new TovolarsMagehunterTriggeredAbility()); + // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Tovolar's Magehunter. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, - TwoOrMoreSpellsWereCastLastTurnCondition.instance, - TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private TovolarsMagehunter(final TovolarsMagehunter card) { @@ -61,11 +51,11 @@ public TovolarsMagehunter copy() { class TovolarsMagehunterTriggeredAbility extends TriggeredAbilityImpl { - public TovolarsMagehunterTriggeredAbility() { + TovolarsMagehunterTriggeredAbility() { super(Zone.BATTLEFIELD, new DamageTargetEffect(2), false); } - public TovolarsMagehunterTriggeredAbility(final TovolarsMagehunterTriggeredAbility ability) { + private TovolarsMagehunterTriggeredAbility(final TovolarsMagehunterTriggeredAbility ability) { super(ability); } @@ -82,7 +72,7 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { if (game.getOpponents(controllerId).contains(event.getPlayerId())) { - this.getEffects().get(0).setTargetPointer(new FixedTarget(event.getPlayerId())); + this.getEffects().setTargetPointer(new FixedTarget(event.getPlayerId())); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/t/ToweringWaveMystic.java b/Mage.Sets/src/mage/cards/t/ToweringWaveMystic.java index 0af73635a302..50dc1ee42b4f 100644 --- a/Mage.Sets/src/mage/cards/t/ToweringWaveMystic.java +++ b/Mage.Sets/src/mage/cards/t/ToweringWaveMystic.java @@ -59,9 +59,8 @@ public ToweringWaveMysticTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE - || event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT + || event.getType() == GameEvent.EventType.DAMAGED_PLAYER; } @Override diff --git a/Mage.Sets/src/mage/cards/t/Toymaker.java b/Mage.Sets/src/mage/cards/t/Toymaker.java index 9dfac87f04cd..075479dd3c6f 100644 --- a/Mage.Sets/src/mage/cards/t/Toymaker.java +++ b/Mage.Sets/src/mage/cards/t/Toymaker.java @@ -65,7 +65,7 @@ class ToymakerEffect extends ContinuousEffectImpl { public ToymakerEffect() { super(Duration.EndOfTurn, Outcome.BecomeCreature); - staticText = "Target noncreature artifact becomes an artifact creature with power and toughness each equal to its converted mana cost until end of turn"; + staticText = "Target noncreature artifact becomes an artifact creature with power and toughness each equal to its mana value until end of turn"; } public ToymakerEffect(final ToymakerEffect effect) { @@ -97,7 +97,7 @@ public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) case PTChangingEffects_7: if (sublayer == SubLayer.SetPT_7b) { - int cmc = artifact.getConvertedManaCost(); + int cmc = artifact.getManaValue(); artifact.getPower().setValue(cmc); artifact.getToughness().setValue(cmc); } diff --git a/Mage.Sets/src/mage/cards/t/TradeCaravan.java b/Mage.Sets/src/mage/cards/t/TradeCaravan.java index a050d6700704..ccf70657fde4 100644 --- a/Mage.Sets/src/mage/cards/t/TradeCaravan.java +++ b/Mage.Sets/src/mage/cards/t/TradeCaravan.java @@ -48,7 +48,7 @@ public TradeCaravan(UUID ownerId, CardSetInfo setInfo) { // Remove two currency counters from Trade Caravan: Untap target basic land. Activate this ability only during an opponent's upkeep. Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new UntapTargetEffect(), new RemoveCountersSourceCost(CounterType.CURRENCY.createInstance(2)), new CompoundCondition(OnOpponentsTurnCondition.instance, new IsStepCondition(PhaseStep.UPKEEP, false)), - "Remove two currency counters from {this}: Untap target basic land. Activate this ability only during an opponent's upkeep."); + "Remove two currency counters from {this}: Untap target basic land. Activate only during an opponent's upkeep."); ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/TrailOfMystery.java b/Mage.Sets/src/mage/cards/t/TrailOfMystery.java index 8fcf6cd12401..396d2d8720a9 100644 --- a/Mage.Sets/src/mage/cards/t/TrailOfMystery.java +++ b/Mage.Sets/src/mage/cards/t/TrailOfMystery.java @@ -13,7 +13,7 @@ import mage.constants.Zone; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.other.FaceDownPredicate; +import mage.filter.predicate.card.FaceDownPredicate; import mage.target.common.TargetCardInLibrary; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/t/TrainedCondor.java b/Mage.Sets/src/mage/cards/t/TrainedCondor.java index 8b365aae6c52..c07841354b1b 100644 --- a/Mage.Sets/src/mage/cards/t/TrainedCondor.java +++ b/Mage.Sets/src/mage/cards/t/TrainedCondor.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.constants.Duration; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.Target; import mage.target.common.TargetControlledCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/t/TranquilGrove.java b/Mage.Sets/src/mage/cards/t/TranquilGrove.java index bb1fa541c523..8e59930e7daa 100644 --- a/Mage.Sets/src/mage/cards/t/TranquilGrove.java +++ b/Mage.Sets/src/mage/cards/t/TranquilGrove.java @@ -11,7 +11,7 @@ import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.common.FilterEnchantmentPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/t/TransgressTheMind.java b/Mage.Sets/src/mage/cards/t/TransgressTheMind.java index 27ef719a3c71..7a4088d9385d 100644 --- a/Mage.Sets/src/mage/cards/t/TransgressTheMind.java +++ b/Mage.Sets/src/mage/cards/t/TransgressTheMind.java @@ -10,7 +10,7 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetPlayer; /** @@ -19,10 +19,10 @@ */ public final class TransgressTheMind extends CardImpl { - private static final FilterCard filter = new FilterCard("a card from it with converted mana cost 3 or greater"); + private static final FilterCard filter = new FilterCard("a card from it with mana value 3 or greater"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 2)); + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 2)); } public TransgressTheMind(UUID ownerId, CardSetInfo setInfo) { @@ -33,7 +33,7 @@ public TransgressTheMind(UUID ownerId, CardSetInfo setInfo) { // Target player reveals their hand. You may choose a card from it with converted mana cost 3 or greater and exile that card. Effect effect = new ExileCardYouChooseTargetOpponentEffect(filter); - effect.setText("Target player reveals their hand. You may choose a card from it with converted mana cost 3 or greater and exile that card"); + effect.setText("Target player reveals their hand. You may choose a card from it with mana value 3 or greater and exile that card"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetPlayer()); } diff --git a/Mage.Sets/src/mage/cards/t/TransmogrifyingWand.java b/Mage.Sets/src/mage/cards/t/TransmogrifyingWand.java index 67b11d1267c9..e69a9ee32b98 100644 --- a/Mage.Sets/src/mage/cards/t/TransmogrifyingWand.java +++ b/Mage.Sets/src/mage/cards/t/TransmogrifyingWand.java @@ -36,7 +36,7 @@ public TransmogrifyingWand(UUID ownerId, CardSetInfo setInfo) { // Transmogrifying Wand enters the battlefield with three charge counters on it. this.addAbility(new EntersBattlefieldAbility( new AddCountersSourceEffect(CounterType.CHARGE.createInstance(3)), - "{this} enters the battlefield with three charge counters on it" + "with three charge counters on it" )); // {1}, {T}, Remove a charge counter from Transmogrifying Wand: Destroy target creature. Its controller creates a 2/4 white Ox creature token. Activate this ability only any time you could cast a sorcery. diff --git a/Mage.Sets/src/mage/cards/t/TransmuteArtifact.java b/Mage.Sets/src/mage/cards/t/TransmuteArtifact.java index bd08b4321533..211245ba6227 100644 --- a/Mage.Sets/src/mage/cards/t/TransmuteArtifact.java +++ b/Mage.Sets/src/mage/cards/t/TransmuteArtifact.java @@ -46,7 +46,7 @@ class TransmuteArtifactEffect extends SearchEffect { public TransmuteArtifactEffect() { super(new TargetCardInLibrary(new FilterArtifactCard()), Outcome.PutCardInPlay); - staticText = "Sacrifice an artifact. If you do, search your library for an artifact card. If that card's converted mana cost is less than or equal to the sacrificed artifact's converted mana cost, put it onto the battlefield. If it's greater, you may pay {X}, where X is the difference. If you do, put it onto the battlefield. If you don't, put it into its owner's graveyard. Then shuffle your library"; + staticText = "Sacrifice an artifact. If you do, search your library for an artifact card. If that card's mana value is less than or equal to the sacrificed artifact's mana value, put it onto the battlefield. If it's greater, you may pay {X}, where X is the difference. If you do, put it onto the battlefield. If you don't, put it into its owner's graveyard. Then shuffle"; } public TransmuteArtifactEffect(final TransmuteArtifactEffect effect) { @@ -65,13 +65,13 @@ public boolean apply(Game game, Ability source) { return false; } //Sacrifice an artifact. - int convertedManaCost = 0; + int manaValue = 0; boolean sacrifice = false; TargetControlledPermanent targetArtifact = new TargetControlledPermanent(new FilterControlledArtifactPermanent()); if (controller.chooseTarget(Outcome.Sacrifice, targetArtifact, source, game)) { Permanent permanent = game.getPermanent(targetArtifact.getFirstTarget()); if (permanent != null) { - convertedManaCost = permanent.getConvertedManaCost(); + manaValue = permanent.getManaValue(); sacrifice = permanent.sacrifice(source, game); } } else { @@ -84,11 +84,11 @@ public boolean apply(Game game, Ability source) { Card card = controller.getLibrary().getCard(cardId, game); if (card != null) { //If that card's converted mana cost is less than or equal to the sacrificed artifact's converted mana cost, put it onto the battlefield. - if (card.getConvertedManaCost() <= convertedManaCost) { + if (card.getManaValue() <= manaValue) { controller.moveCards(card, Zone.BATTLEFIELD, source, game); } else { //If it's greater, you may pay {X}, where X is the difference. If you do, put it onto the battlefield. - Cost cost = ManaUtil.createManaCost(card.getConvertedManaCost() - convertedManaCost, true); + Cost cost = ManaUtil.createManaCost(card.getManaValue() - manaValue, true); boolean payed = false; if (controller.chooseUse(Outcome.Benefit, "Do you want to pay " + cost.getText() + " to put it onto the battlefield?", source, game) && cost.pay(source, game, source, source.getControllerId(), false)) { diff --git a/Mage.Sets/src/mage/cards/t/TraverseTheOutlands.java b/Mage.Sets/src/mage/cards/t/TraverseTheOutlands.java index e02ca3cda480..95a387ca76ca 100644 --- a/Mage.Sets/src/mage/cards/t/TraverseTheOutlands.java +++ b/Mage.Sets/src/mage/cards/t/TraverseTheOutlands.java @@ -46,7 +46,7 @@ class TraverseTheOutlandsEffect extends OneShotEffect { public TraverseTheOutlandsEffect() { super(Outcome.Benefit); - this.staticText = "Search your library for up to X basic land cards, where X is the greatest power among creatures you control. Put those cards onto the battlefield tapped, then shuffle your library."; + this.staticText = "Search your library for up to X basic land cards, where X is the greatest power among creatures you control. Put those cards onto the battlefield tapped, then shuffle."; } public TraverseTheOutlandsEffect(final TraverseTheOutlandsEffect effect) { diff --git a/Mage.Sets/src/mage/cards/t/TraverseTheUlvenwald.java b/Mage.Sets/src/mage/cards/t/TraverseTheUlvenwald.java index 40bf1fa75a9a..299056dce801 100644 --- a/Mage.Sets/src/mage/cards/t/TraverseTheUlvenwald.java +++ b/Mage.Sets/src/mage/cards/t/TraverseTheUlvenwald.java @@ -33,7 +33,7 @@ public TraverseTheUlvenwald(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new SearchLibraryPutInHandEffect(new TargetCardInLibrary(1, 1, StaticFilters.FILTER_CARD_BASIC_LAND), true), new InvertCondition(DeliriumCondition.instance), - "Search your library for a basic land card, reveal it, put it into your hand, then shuffle your library.")); + "Search your library for a basic land card, reveal it, put it into your hand, then shuffle.")); // Delirium — If there are four or more card types among cards in your graveyard, instead search your library // for a creature or land card, reveal it, put it into your hand, then shuffle your library. @@ -41,7 +41,7 @@ public TraverseTheUlvenwald(UUID ownerId, CardSetInfo setInfo) { new SearchLibraryPutInHandEffect(new TargetCardInLibrary(1, 1, filter), true), DeliriumCondition.instance, "
Delirium — If there are four or more card types among cards in your graveyard, instead search your library for a creature or land card, " - + "reveal it, put it into your hand, then shuffle your library.")); + + "reveal it, put it into your hand, then shuffle.")); this.getSpellAbility().addHint(DeliriumHint.instance); } diff --git a/Mage.Sets/src/mage/cards/t/TreacherousLink.java b/Mage.Sets/src/mage/cards/t/TreacherousLink.java index 9f16e3e44807..408d5d942c2d 100644 --- a/Mage.Sets/src/mage/cards/t/TreacherousLink.java +++ b/Mage.Sets/src/mage/cards/t/TreacherousLink.java @@ -70,7 +70,7 @@ public TreacherousLinkEffect copy() { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/t/TreasureKeeper.java b/Mage.Sets/src/mage/cards/t/TreasureKeeper.java index 620bf8e9fd3a..2302070739bf 100644 --- a/Mage.Sets/src/mage/cards/t/TreasureKeeper.java +++ b/Mage.Sets/src/mage/cards/t/TreasureKeeper.java @@ -49,7 +49,7 @@ class TreasureKeeperEffect extends OneShotEffect { public TreasureKeeperEffect() { super(Outcome.PlayForFree); this.staticText = "reveal cards from the top of your library until you reveal a " - + "nonland card with converted mana cost 3 or less. " + + "nonland card with mana value 3 or less. " + "You may cast that card without paying its mana cost. Put all revealed " + "cards not cast this way on the bottom of your library in a random order"; } @@ -67,7 +67,7 @@ public boolean apply(Game game, Ability source) { Card nonLandCard = null; for (Card card : controller.getLibrary().getCards(game)) { toReveal.add(card); - if (!card.isLand() && card.getConvertedManaCost() < 4) { + if (!card.isLand() && card.getManaValue() < 4) { nonLandCard = card; break; } diff --git a/Mage.Sets/src/mage/cards/t/TreasureMage.java b/Mage.Sets/src/mage/cards/t/TreasureMage.java index f7c492ccd9c2..ac3db080b1ae 100644 --- a/Mage.Sets/src/mage/cards/t/TreasureMage.java +++ b/Mage.Sets/src/mage/cards/t/TreasureMage.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.constants.ComparisonType; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; /** @@ -22,11 +22,11 @@ */ public final class TreasureMage extends CardImpl { - private static final FilterCard filter = new FilterCard("an artifact card with converted mana cost 6 or more"); + private static final FilterCard filter = new FilterCard("an artifact card with mana value 6 or more"); static { filter.add(CardType.ARTIFACT.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 5)); + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 5)); } public TreasureMage (UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/t/TreasuredFind.java b/Mage.Sets/src/mage/cards/t/TreasuredFind.java index ad2bf7d6d21b..7ddf7d47fec3 100644 --- a/Mage.Sets/src/mage/cards/t/TreasuredFind.java +++ b/Mage.Sets/src/mage/cards/t/TreasuredFind.java @@ -23,7 +23,7 @@ public TreasuredFind(UUID ownerId, CardSetInfo setInfo) { // Return target card from your graveyard to your hand. Exile Treasured Find. this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard()); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private TreasuredFind(final TreasuredFind card) { diff --git a/Mage.Sets/src/mage/cards/t/TrenchGorger.java b/Mage.Sets/src/mage/cards/t/TrenchGorger.java index 5c8fecf008d8..31c5cb1c50ca 100644 --- a/Mage.Sets/src/mage/cards/t/TrenchGorger.java +++ b/Mage.Sets/src/mage/cards/t/TrenchGorger.java @@ -50,7 +50,7 @@ class TrenchGorgerEffect extends OneShotEffect { public TrenchGorgerEffect() { super(Outcome.BoostCreature); - this.staticText = "you may search your library for any number of land cards, exile them, then shuffle your library. If you do, {this}'s power and toughness each become equal to the number of cards exiled this way"; + this.staticText = "you may search your library for any number of land cards, exile them, then shuffle. If you do, {this}'s power and toughness each become equal to the number of cards exiled this way"; } public TrenchGorgerEffect(final TrenchGorgerEffect effect) { diff --git a/Mage.Sets/src/mage/cards/t/TresserhornSkyknight.java b/Mage.Sets/src/mage/cards/t/TresserhornSkyknight.java index 91d8815018da..8256f5f72830 100644 --- a/Mage.Sets/src/mage/cards/t/TresserhornSkyknight.java +++ b/Mage.Sets/src/mage/cards/t/TresserhornSkyknight.java @@ -17,8 +17,8 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.AbilityPredicate; import mage.game.Game; +import mage.game.events.DamageEvent; import mage.game.events.GameEvent; -import mage.game.events.DamageCreatureEvent; import mage.game.permanent.Permanent; /** @@ -75,8 +75,8 @@ public TresserhornSkyknightEffect copy() { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (super.applies(event, source, game) && event instanceof DamageCreatureEvent && event.getAmount() > 0) { - DamageCreatureEvent damageEvent = (DamageCreatureEvent) event; + if (super.applies(event, source, game) && event instanceof DamageEvent && event.getAmount() > 0) { + DamageEvent damageEvent = (DamageEvent) event; if (event.getTargetId().equals(source.getSourceId())) { Permanent permanent = game.getPermanentOrLKIBattlefield(damageEvent.getSourceId()); if (permanent != null && filter.match(permanent, game)) { diff --git a/Mage.Sets/src/mage/cards/t/TriadOfFates.java b/Mage.Sets/src/mage/cards/t/TriadOfFates.java index be701cb65e59..bcf55fefcfdf 100644 --- a/Mage.Sets/src/mage/cards/t/TriadOfFates.java +++ b/Mage.Sets/src/mage/cards/t/TriadOfFates.java @@ -15,7 +15,7 @@ import mage.constants.*; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/t/TriassicEgg.java b/Mage.Sets/src/mage/cards/t/TriassicEgg.java index 85b38fdf7d30..c6e283da8fff 100644 --- a/Mage.Sets/src/mage/cards/t/TriassicEgg.java +++ b/Mage.Sets/src/mage/cards/t/TriassicEgg.java @@ -42,8 +42,7 @@ public TriassicEgg(UUID ownerId, CardSetInfo setInfo) { ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new PutCardFromHandOntoBattlefieldEffect(StaticFilters.FILTER_CARD_CREATURE_A), new SacrificeSourceCost(), - new SourceHasCounterCondition(CounterType.HATCHLING, 2, Integer.MAX_VALUE), - "Sacrifice Triassic Egg: Choose one - You may put a creature card from your hand onto the battlefield; or return target creature card from your graveyard to the battlefield. Activate this ability only if two or more hatchling counters are on {this}."); + new SourceHasCounterCondition(CounterType.HATCHLING, 2, Integer.MAX_VALUE)); // or return target creature card from your graveyard to the battlefield. Activate this ability only if two or more hatchling counters are on Triassic Egg. Mode mode = new Mode(); diff --git a/Mage.Sets/src/mage/cards/t/TributeMage.java b/Mage.Sets/src/mage/cards/t/TributeMage.java index 23aef5d8e714..2b62f0b0943f 100644 --- a/Mage.Sets/src/mage/cards/t/TributeMage.java +++ b/Mage.Sets/src/mage/cards/t/TributeMage.java @@ -9,7 +9,7 @@ import mage.constants.ComparisonType; import mage.constants.SubType; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; import java.util.UUID; @@ -19,11 +19,11 @@ */ public final class TributeMage extends CardImpl { - private static final FilterCard filter = new FilterCard("an artifact card with converted mana cost 2"); + private static final FilterCard filter = new FilterCard("an artifact card with mana value 2"); static { filter.add(CardType.ARTIFACT.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, 2)); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, 2)); } public TributeMage(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/t/Trinisphere.java b/Mage.Sets/src/mage/cards/t/Trinisphere.java index 1024282a1471..8685396ece41 100644 --- a/Mage.Sets/src/mage/cards/t/Trinisphere.java +++ b/Mage.Sets/src/mage/cards/t/Trinisphere.java @@ -53,7 +53,7 @@ protected TrinisphereEffect(TrinisphereEffect effect) { @Override public boolean apply(Game game, Ability source, Ability abilityToModify) { - int manaCost = abilityToModify.getManaCostsToPay().convertedManaCost(); + int manaCost = abilityToModify.getManaCostsToPay().manaValue(); if (manaCost < 3) { CardUtil.increaseCost(abilityToModify, 3 - manaCost); } diff --git a/Mage.Sets/src/mage/cards/t/TrinketMage.java b/Mage.Sets/src/mage/cards/t/TrinketMage.java index 3fac4683b354..16ae9dc2579e 100644 --- a/Mage.Sets/src/mage/cards/t/TrinketMage.java +++ b/Mage.Sets/src/mage/cards/t/TrinketMage.java @@ -2,7 +2,6 @@ package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.SearchEffect; @@ -10,26 +9,28 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.ComparisonType; +import mage.constants.SubType; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; +import java.util.UUID; + /** * @author ayratn */ public final class TrinketMage extends CardImpl { - private static final FilterCard filter = new FilterCard("artifact card with converted mana cost 1 or less"); + private static final FilterCard filter = new FilterCard("an artifact card with mana value 1 or less"); static { filter.add(CardType.ARTIFACT.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 2)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 2)); } public TrinketMage(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WIZARD); @@ -37,7 +38,7 @@ public TrinketMage(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(2); // When Trinket Mage enters the battlefield, you may search your library for an artifact card with converted mana cost 1 or less, reveal that card, and put it into your hand. If you do, shuffle your library. - TargetCardInLibrary target = new TargetCardInLibrary(0, 1, filter); + TargetCardInLibrary target = new TargetCardInLibrary(filter); SearchEffect effect = new SearchLibraryPutInHandEffect(target, true, true); this.addAbility(new EntersBattlefieldTriggeredAbility(effect, true)); } diff --git a/Mage.Sets/src/mage/cards/t/TriplicateTitan.java b/Mage.Sets/src/mage/cards/t/TriplicateTitan.java new file mode 100644 index 000000000000..67127ce5976e --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TriplicateTitan.java @@ -0,0 +1,58 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.TrampleAbility; +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.GolemFlyingToken; +import mage.game.permanent.token.GolemTrampleToken; +import mage.game.permanent.token.GolemVigilanceToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TriplicateTitan extends CardImpl { + + public TriplicateTitan(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{9}"); + + this.subtype.add(SubType.GOLEM); + this.power = new MageInt(9); + this.toughness = new MageInt(9); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // When Triplicate Titan dies, create a 3/3 colorless Golem artifact creature token with flying, a 3/3 colorless Golem artifact creature token with vigilance, and a 3/3 colorless Golem artifact creature token with trample. + Ability ability = new DiesSourceTriggeredAbility(new CreateTokenEffect(new GolemFlyingToken())); + ability.addEffect(new CreateTokenEffect(new GolemVigilanceToken()) + .setText(", a 3/3 colorless Golem artifact creature token with vigilance")); + ability.addEffect(new CreateTokenEffect(new GolemTrampleToken()) + .setText(", and a 3/3 colorless Golem artifact creature token with trample")); + this.addAbility(ability); + } + + private TriplicateTitan(final TriplicateTitan card) { + super(card); + } + + @Override + public TriplicateTitan copy() { + return new TriplicateTitan(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TrompTheDomains.java b/Mage.Sets/src/mage/cards/t/TrompTheDomains.java index 99a06632f4d0..7c2218919947 100644 --- a/Mage.Sets/src/mage/cards/t/TrompTheDomains.java +++ b/Mage.Sets/src/mage/cards/t/TrompTheDomains.java @@ -25,7 +25,7 @@ public TrompTheDomains(UUID ownerId, CardSetInfo setInfo) { // Domain - Until end of turn, creatures you control gain trample and get +1/+1 for each basic land type among lands you control. Effect effect = new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfTurn); - effect.setText("Domain — Until end of turn, creatures you control gain Trample"); + effect.setText("Domain — Until end of turn, creatures you control gain trample"); this.getSpellAbility().addEffect(effect); DynamicValue domain = new DomainValue(); effect = new BoostControlledEffect(domain, domain, Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURE, false); diff --git a/Mage.Sets/src/mage/cards/t/TrophyMage.java b/Mage.Sets/src/mage/cards/t/TrophyMage.java index d5b0a26a7453..ef63fefacd90 100644 --- a/Mage.Sets/src/mage/cards/t/TrophyMage.java +++ b/Mage.Sets/src/mage/cards/t/TrophyMage.java @@ -11,7 +11,7 @@ import mage.constants.SubType; import mage.constants.ComparisonType; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; /** @@ -20,11 +20,11 @@ */ public final class TrophyMage extends CardImpl { - private static final FilterCard filter = new FilterCard("an artifact card with converted mana cost 3"); + private static final FilterCard filter = new FilterCard("an artifact card with mana value 3"); static { filter.add(CardType.ARTIFACT.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, 3)); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, 3)); } public TrophyMage(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/t/TrostaniDiscordant.java b/Mage.Sets/src/mage/cards/t/TrostaniDiscordant.java index ced6663a9083..55989f46f1b3 100644 --- a/Mage.Sets/src/mage/cards/t/TrostaniDiscordant.java +++ b/Mage.Sets/src/mage/cards/t/TrostaniDiscordant.java @@ -24,7 +24,7 @@ import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.SoldierLifelinkToken; diff --git a/Mage.Sets/src/mage/cards/t/TroveWarden.java b/Mage.Sets/src/mage/cards/t/TroveWarden.java index fa962c88e77b..d51cc28b24e1 100644 --- a/Mage.Sets/src/mage/cards/t/TroveWarden.java +++ b/Mage.Sets/src/mage/cards/t/TroveWarden.java @@ -12,7 +12,7 @@ import mage.constants.*; import mage.filter.FilterCard; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.ExileZone; import mage.game.Game; import mage.game.permanent.Permanent; @@ -28,10 +28,10 @@ public final class TroveWarden extends CardImpl { private static final FilterCard filter - = new FilterPermanentCard("permanent card with converted mana cost 3 or less from your graveyard"); + = new FilterPermanentCard("permanent card with mana value 3 or less from your graveyard"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public TroveWarden(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/t/TrudgeGarden.java b/Mage.Sets/src/mage/cards/t/TrudgeGarden.java new file mode 100644 index 000000000000..236ab3ddb3c4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TrudgeGarden.java @@ -0,0 +1,36 @@ +package mage.cards.t; + +import mage.abilities.common.GainLifeControllerTriggeredAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.permanent.token.FungusBeastToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TrudgeGarden extends CardImpl { + + public TrudgeGarden(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); + + // Whenever you gain life, you may pay {2}. If you do, create a 4/4 green Fungus Beast creature token with trample. + this.addAbility(new GainLifeControllerTriggeredAbility(new DoIfCostPaid( + new CreateTokenEffect(new FungusBeastToken()), new GenericManaCost(2) + ), false)); + } + + private TrudgeGarden(final TrudgeGarden card) { + super(card); + } + + @Override + public TrudgeGarden copy() { + return new TrudgeGarden(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TunnelVision.java b/Mage.Sets/src/mage/cards/t/TunnelVision.java index 0aa3ec49c0f3..9a37ef1b2987 100644 --- a/Mage.Sets/src/mage/cards/t/TunnelVision.java +++ b/Mage.Sets/src/mage/cards/t/TunnelVision.java @@ -48,7 +48,7 @@ public TunnelVisionEffect() { + "until a card with that name is revealed. If it is, that player puts " + "the rest of the revealed cards into their graveyard and " + "puts the card with the chosen name on top of their library. " - + "Otherwise, the player shuffles their library"; + + "Otherwise, the player shuffles"; } public TunnelVisionEffect(final TunnelVisionEffect effect) { diff --git a/Mage.Sets/src/mage/cards/t/TurnAside.java b/Mage.Sets/src/mage/cards/t/TurnAside.java index 5276d737bf63..434b90d55d3a 100644 --- a/Mage.Sets/src/mage/cards/t/TurnAside.java +++ b/Mage.Sets/src/mage/cards/t/TurnAside.java @@ -9,7 +9,7 @@ import mage.constants.CardType; import mage.filter.FilterSpell; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.other.TargetsPermanentPredicate; +import mage.filter.predicate.mageobject.TargetsPermanentPredicate; import mage.target.TargetSpell; diff --git a/Mage.Sets/src/mage/cards/t/TurntimberSymbiosis.java b/Mage.Sets/src/mage/cards/t/TurntimberSymbiosis.java index 611a4d3053c5..373c8fe99535 100644 --- a/Mage.Sets/src/mage/cards/t/TurntimberSymbiosis.java +++ b/Mage.Sets/src/mage/cards/t/TurntimberSymbiosis.java @@ -70,7 +70,7 @@ class TurntimberSymbiosisEffect extends OneShotEffect { TurntimberSymbiosisEffect() { super(Outcome.Benefit); staticText = "Look at the top seven cards of your library. You may put a creature card " + - "from among them onto the battlefield. If that card has converted mana cost 3 or less, " + + "from among them onto the battlefield. If that card has mana value 3 or less, " + "it enters with three additional +1/+1 counters on it. " + "Put the rest on the bottom of your library in a random order"; } @@ -102,7 +102,7 @@ public boolean apply(Game game, Ability source) { return true; } - boolean small = card.getConvertedManaCost() <= 3; + boolean small = card.getManaValue() <= 3; player.moveCards(card, Zone.BATTLEFIELD, source, game); Permanent permanent = game.getPermanent(card.getId()); if (permanent == null || !small) { diff --git a/Mage.Sets/src/mage/cards/t/TurretOgre.java b/Mage.Sets/src/mage/cards/t/TurretOgre.java index 8a60e55e73de..4e46052f666a 100644 --- a/Mage.Sets/src/mage/cards/t/TurretOgre.java +++ b/Mage.Sets/src/mage/cards/t/TurretOgre.java @@ -16,7 +16,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/t/TwilightProphet.java b/Mage.Sets/src/mage/cards/t/TwilightProphet.java index c8944eae6b94..b449319217dd 100644 --- a/Mage.Sets/src/mage/cards/t/TwilightProphet.java +++ b/Mage.Sets/src/mage/cards/t/TwilightProphet.java @@ -45,7 +45,7 @@ public TwilightProphet(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new ConditionalInterveningIfTriggeredAbility(new BeginningOfUpkeepTriggeredAbility( new TwilightProphetEffect(), TargetController.YOU, false), CitysBlessingCondition.instance, "At the beginning of your upkeep, if you have the city's blessing, reveal the top card of your library and put it into your hand." - + "Each opponent loses X life and you gain X life, where X is that card's converted mana cost.") + + "Each opponent loses X life and you gain X life, where X is that card's mana value.") .addHint(CitysBlessingHint.instance)); } @@ -65,7 +65,7 @@ class TwilightProphetEffect extends OneShotEffect { public TwilightProphetEffect() { super(Outcome.Benefit); this.staticText = "if you have the city's blessing, reveal the top card of your library and put it into your hand. " - + "Each opponent loses X life and you gain X life, where X is that card's converted mana cost."; + + "Each opponent loses X life and you gain X life, where X is that card's mana value."; } public TwilightProphetEffect(final TwilightProphetEffect effect) { @@ -87,7 +87,7 @@ public boolean apply(Game game, Ability source) { controller.revealCards(sourceObject.getIdName(), new CardsImpl(card), game); controller.moveCards(card, Zone.HAND, source, game); game.getState().processAction(game); - int amount = card.getConvertedManaCost(); + int amount = card.getManaValue(); if (amount > 0) { new LoseLifeOpponentsEffect(amount).apply(game, source); controller.gainLife(amount, game, source); diff --git a/Mage.Sets/src/mage/cards/t/TwilightShepherd.java b/Mage.Sets/src/mage/cards/t/TwilightShepherd.java index 5738d4d4ab22..5ab7293ba4a5 100644 --- a/Mage.Sets/src/mage/cards/t/TwilightShepherd.java +++ b/Mage.Sets/src/mage/cards/t/TwilightShepherd.java @@ -1,38 +1,35 @@ - package mage.cards.t; -import java.util.Set; -import java.util.UUID; import mage.MageInt; -import mage.MageObjectReference; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ReturnToHandFromGraveyardAllEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.PersistAbility; import mage.abilities.keyword.VigilanceAbility; -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.SubType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.players.Player; +import mage.constants.TargetController; +import mage.filter.FilterCard; +import mage.filter.predicate.card.PutIntoGraveFromBattlefieldThisTurnPredicate; import mage.watchers.common.CardsPutIntoGraveyardWatcher; +import java.util.UUID; + /** - * * @author jeffwadsworth - * */ public final class TwilightShepherd extends CardImpl { + private static final FilterCard filter = new FilterCard(); + + static { + filter.add(PutIntoGraveFromBattlefieldThisTurnPredicate.instance); + } + public TwilightShepherd(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}{W}"); this.subtype.add(SubType.ANGEL); this.power = new MageInt(5); @@ -45,7 +42,12 @@ public TwilightShepherd(UUID ownerId, CardSetInfo setInfo) { this.addAbility(VigilanceAbility.getInstance()); // When Twilight Shepherd enters the battlefield, return to your hand all cards in your graveyard that were put there from the battlefield this turn. - this.addAbility(new EntersBattlefieldTriggeredAbility(new TwilightShepherdEffect(), false), new CardsPutIntoGraveyardWatcher()); + this.addAbility(new EntersBattlefieldTriggeredAbility( + new ReturnToHandFromGraveyardAllEffect(filter, TargetController.YOU) + .setText("return to your hand all cards in your graveyard " + + "that were put there from the battlefield this turn"), + false + ), new CardsPutIntoGraveyardWatcher()); // Persist this.addAbility(new PersistAbility()); @@ -60,44 +62,3 @@ public TwilightShepherd copy() { return new TwilightShepherd(this); } } - -class TwilightShepherdEffect extends OneShotEffect { - - boolean applied = false; - - public TwilightShepherdEffect() { - super(Outcome.ReturnToHand); - this.staticText = "return to your hand all cards in your graveyard that were put there from the battlefield this turn"; - } - - public TwilightShepherdEffect(final TwilightShepherdEffect effect) { - super(effect); - } - - @Override - public TwilightShepherdEffect copy() { - return new TwilightShepherdEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - CardsPutIntoGraveyardWatcher watcher = game.getState().getWatcher(CardsPutIntoGraveyardWatcher.class); - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null && watcher != null) { - Set cardsInGraveyard = watcher.getCardsPutToGraveyardFromBattlefield(); - Cards cardsToHand = new CardsImpl(); - for (MageObjectReference mor : cardsInGraveyard) { - if (game.getState().getZoneChangeCounter(mor.getSourceId()) == mor.getZoneChangeCounter()) { - Card card = game.getCard(mor.getSourceId()); - if (card != null - && card.isOwnedBy(source.getControllerId())) { - cardsToHand.add(card); - } - } - } - controller.moveCards(cardsToHand, Zone.HAND, source, game); - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/t/Twincast.java b/Mage.Sets/src/mage/cards/t/Twincast.java index c9257e74f508..09ac32540e56 100644 --- a/Mage.Sets/src/mage/cards/t/Twincast.java +++ b/Mage.Sets/src/mage/cards/t/Twincast.java @@ -1,7 +1,5 @@ - package mage.cards.t; -import java.util.UUID; import mage.abilities.effects.common.CopyTargetSpellEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -9,18 +7,19 @@ import mage.filter.StaticFilters; import mage.target.TargetSpell; +import java.util.UUID; + /** - * * @author Loki */ public final class Twincast extends CardImpl { public Twincast(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}{U}"); // Copy target instant or sorcery spell. You may choose new targets for the copy. this.getSpellAbility().addEffect(new CopyTargetSpellEffect()); - this.getSpellAbility().addTarget(new TargetSpell()); + this.getSpellAbility().addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_INSTANT_OR_SORCERY)); } private Twincast(final Twincast card) { diff --git a/Mage.Sets/src/mage/cards/t/TwinscrollShaman.java b/Mage.Sets/src/mage/cards/t/TwinscrollShaman.java new file mode 100644 index 000000000000..83a81e0e173f --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TwinscrollShaman.java @@ -0,0 +1,37 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.keyword.DoubleStrikeAbility; +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 TwinscrollShaman extends CardImpl { + + public TwinscrollShaman(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.DWARF); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // Double strike + this.addAbility(DoubleStrikeAbility.getInstance()); + } + + private TwinscrollShaman(final TwinscrollShaman card) { + super(card); + } + + @Override + public TwinscrollShaman copy() { + return new TwinscrollShaman(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TyrantsChoice.java b/Mage.Sets/src/mage/cards/t/TyrantsChoice.java index dd44e4cfbd7a..edd438d28638 100644 --- a/Mage.Sets/src/mage/cards/t/TyrantsChoice.java +++ b/Mage.Sets/src/mage/cards/t/TyrantsChoice.java @@ -1,26 +1,26 @@ - package mage.cards.t; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; import mage.abilities.effects.common.SacrificeOpponentsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.choices.TwoChoiceVote; import mage.constants.CardType; import mage.constants.Outcome; import mage.filter.StaticFilters; import mage.game.Game; -import mage.players.Player; + +import java.util.UUID; /** - * - * @author fireshoes + * @author fireshoes, TheElk801 */ public final class TyrantsChoice extends CardImpl { public TyrantsChoice(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}"); // Will of the council - Starting with you, each player votes for death or torture. If death gets more votes, each opponent sacrifices a creature. If torture gets more votes or the vote is tied, each opponent loses 4 life. this.getSpellAbility().addEffect(new TyrantsChoiceEffect()); @@ -40,10 +40,13 @@ class TyrantsChoiceEffect extends OneShotEffect { TyrantsChoiceEffect() { super(Outcome.Benefit); - this.staticText = "Will of the council — Starting with you, each player votes for death or torture. If death gets more votes, each opponent sacrifices a creature. If torture gets more votes or the vote is tied, each opponent loses 4 life"; + this.staticText = "Will of the council — Starting with you, " + + "each player votes for death or torture. If death gets more votes, " + + "each opponent sacrifices a creature. If torture gets more votes " + + "or the vote is tied, each opponent loses 4 life"; } - TyrantsChoiceEffect(final TyrantsChoiceEffect effect) { + private TyrantsChoiceEffect(final TyrantsChoiceEffect effect) { super(effect); } @@ -54,55 +57,24 @@ public TyrantsChoiceEffect copy() { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - int deathCount = 0; - int tortureCount = 0; - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - if (player.chooseUse(Outcome.Sacrifice, "Choose death?", source, game)) { - deathCount++; - game.informPlayers(player.getLogName() + " has voted for death"); - } else { - tortureCount++; - game.informPlayers(player.getLogName() + " has voted for torture"); - } - } - } - if (deathCount > tortureCount) { - new SacrificeOpponentsEffect(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT).apply(game, source); + TwoChoiceVote vote = new TwoChoiceVote("Death (sacrifice a creature)", "Torture (lose 4 life)", Outcome.Benefit); + vote.doVotes(source, game, (voteHandler, aiPlayer, aiDecidingPlayer, aiSource, aiGame) -> { + // ai hint + if (aiSource.isControlledBy(aiDecidingPlayer.getId())) { + // best for controller - lose life + return Boolean.FALSE; } else { - new TyrantsChoiceLoseLifeEffect().apply(game, source); + // best for opponent - sacrifice + return Boolean.TRUE; } - return true; + }); + + int deathCount = vote.getVoteCount(true); + int tortureCount = vote.getVoteCount(false); + if (deathCount > tortureCount) { + return new SacrificeOpponentsEffect(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT).apply(game, source); + } else { + return new LoseLifeOpponentsEffect(4).apply(game, source); } - return false; } } - -class TyrantsChoiceLoseLifeEffect extends OneShotEffect { - - public TyrantsChoiceLoseLifeEffect() { - super(Outcome.Damage); - staticText = "Each opponent loses 2 life"; - } - - public TyrantsChoiceLoseLifeEffect(final TyrantsChoiceLoseLifeEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - for (UUID opponentId : game.getOpponents(source.getControllerId())) { - game.getPlayer(opponentId).loseLife(4, game, source, false); - } - return true; - } - - @Override - public TyrantsChoiceLoseLifeEffect copy() { - return new TyrantsChoiceLoseLifeEffect(this); - } - -} diff --git a/Mage.Sets/src/mage/cards/t/TyrantsScorn.java b/Mage.Sets/src/mage/cards/t/TyrantsScorn.java index 0b082c947887..b1b4b663ed28 100644 --- a/Mage.Sets/src/mage/cards/t/TyrantsScorn.java +++ b/Mage.Sets/src/mage/cards/t/TyrantsScorn.java @@ -9,7 +9,7 @@ import mage.constants.ComparisonType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -21,10 +21,10 @@ public final class TyrantsScorn extends CardImpl { private static final FilterPermanent filter - = new FilterCreaturePermanent("creature with converted mana cost 3 or less"); + = new FilterCreaturePermanent("creature with mana value 3 or less"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public TyrantsScorn(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/u/UginTheSpiritDragon.java b/Mage.Sets/src/mage/cards/u/UginTheSpiritDragon.java index c6194a714298..b8170dd9fc9f 100644 --- a/Mage.Sets/src/mage/cards/u/UginTheSpiritDragon.java +++ b/Mage.Sets/src/mage/cards/u/UginTheSpiritDragon.java @@ -25,7 +25,7 @@ import mage.filter.common.FilterPermanentCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorlessPredicate; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInHand; @@ -72,7 +72,7 @@ class UginTheSpiritDragonEffect2 extends OneShotEffect { public UginTheSpiritDragonEffect2() { super(Outcome.Exile); - this.staticText = "exile each permanent with converted mana cost X or less that's one or more colors"; + this.staticText = "exile each permanent with mana value X or less that's one or more colors"; } public UginTheSpiritDragonEffect2(final UginTheSpiritDragonEffect2 effect) { @@ -98,8 +98,8 @@ public boolean apply(Game game, Ability source) { } } - FilterPermanent filter = new FilterPermanent("permanent with converted mana cost X or less that's one or more colors"); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, cmc + 1)); + FilterPermanent filter = new FilterPermanent("permanent with mana value X or less that's one or more colors"); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, cmc + 1)); filter.add(Predicates.not(ColorlessPredicate.instance)); Set permanentsToExile = new HashSet<>(); permanentsToExile.addAll(game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)); diff --git a/Mage.Sets/src/mage/cards/u/UginsInsight.java b/Mage.Sets/src/mage/cards/u/UginsInsight.java index 6f035570957f..e506edba62ac 100644 --- a/Mage.Sets/src/mage/cards/u/UginsInsight.java +++ b/Mage.Sets/src/mage/cards/u/UginsInsight.java @@ -3,7 +3,7 @@ import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.dynamicvalue.common.HighestConvertedManaCostValue; +import mage.abilities.dynamicvalue.common.HighestManaValueCount; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -39,7 +39,7 @@ class UginsInsightEffect extends OneShotEffect { public UginsInsightEffect() { super(Outcome.DrawCard); - this.staticText = "Scry X, where X is the highest converted mana cost among permanents you control, then draw three cards"; + this.staticText = "Scry X, where X is the highest mana value among permanents you control, then draw three cards"; } public UginsInsightEffect(final UginsInsightEffect effect) { @@ -55,7 +55,7 @@ public UginsInsightEffect copy() { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - int highCMC = new HighestConvertedManaCostValue().calculate(game, source, this); + int highCMC = new HighestManaValueCount().calculate(game, source, this); if (highCMC > 0) { controller.scry(highCMC, source, game); } diff --git a/Mage.Sets/src/mage/cards/u/UlamogTheCeaselessHunger.java b/Mage.Sets/src/mage/cards/u/UlamogTheCeaselessHunger.java index 26f7cee8a7e0..4cbca4367ecf 100644 --- a/Mage.Sets/src/mage/cards/u/UlamogTheCeaselessHunger.java +++ b/Mage.Sets/src/mage/cards/u/UlamogTheCeaselessHunger.java @@ -90,7 +90,7 @@ public UlamogExilePermanentsOnCastAbility copy() { @Override public String getRule() { - return "When you cast {this}, " + super.getRule(); + return "When you cast this spell, " + super.getRule(); } } diff --git a/Mage.Sets/src/mage/cards/u/UlashtTheHateSeed.java b/Mage.Sets/src/mage/cards/u/UlashtTheHateSeed.java index 6a207c92dde9..31cf77fb0354 100644 --- a/Mage.Sets/src/mage/cards/u/UlashtTheHateSeed.java +++ b/Mage.Sets/src/mage/cards/u/UlashtTheHateSeed.java @@ -24,7 +24,7 @@ import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.SaprolingToken; diff --git a/Mage.Sets/src/mage/cards/u/UlrichOfTheKrallenhorde.java b/Mage.Sets/src/mage/cards/u/UlrichOfTheKrallenhorde.java index 8dc950c935b4..276c819511e4 100644 --- a/Mage.Sets/src/mage/cards/u/UlrichOfTheKrallenhorde.java +++ b/Mage.Sets/src/mage/cards/u/UlrichOfTheKrallenhorde.java @@ -1,14 +1,8 @@ - package mage.cards.u; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; @@ -19,14 +13,15 @@ import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class UlrichOfTheKrallenhorde extends CardImpl { public UlrichOfTheKrallenhorde(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{G}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WEREWOLF); @@ -42,8 +37,7 @@ public UlrichOfTheKrallenhorde(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each upkeep, if no spells were cast last turn, transform Ulrich of the Krallenhorde. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private UlrichOfTheKrallenhorde(final UlrichOfTheKrallenhorde card) { diff --git a/Mage.Sets/src/mage/cards/u/UlrichUncontestedAlpha.java b/Mage.Sets/src/mage/cards/u/UlrichUncontestedAlpha.java index 8da414b6005d..5da9d4f8c41e 100644 --- a/Mage.Sets/src/mage/cards/u/UlrichUncontestedAlpha.java +++ b/Mage.Sets/src/mage/cards/u/UlrichUncontestedAlpha.java @@ -1,16 +1,9 @@ - package mage.cards.u; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.effects.common.FightTargetSourceEffect; -import mage.abilities.effects.common.TransformSourceEffect; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -22,14 +15,15 @@ import mage.target.Target; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class UlrichUncontestedAlpha extends CardImpl { public UlrichUncontestedAlpha(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.WEREWOLF); this.power = new MageInt(6); @@ -45,8 +39,7 @@ public UlrichUncontestedAlpha(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new UlrichUncontestedAlphaAbility()); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Ulrich, Uncontested Alpha. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private UlrichUncontestedAlpha(final UlrichUncontestedAlpha card) { diff --git a/Mage.Sets/src/mage/cards/u/UlvenwaldMystics.java b/Mage.Sets/src/mage/cards/u/UlvenwaldMystics.java index d7f3e127fbb8..3703f8792ae7 100644 --- a/Mage.Sets/src/mage/cards/u/UlvenwaldMystics.java +++ b/Mage.Sets/src/mage/cards/u/UlvenwaldMystics.java @@ -1,19 +1,14 @@ - package mage.cards.u; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** * @author nantuko @@ -21,7 +16,7 @@ public final class UlvenwaldMystics extends CardImpl { public UlvenwaldMystics(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SHAMAN); this.subtype.add(SubType.WEREWOLF); @@ -34,8 +29,7 @@ public UlvenwaldMystics(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each upkeep, if no spells were cast last turn, transform Ulvenwald Mystics. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private UlvenwaldMystics(final UlvenwaldMystics card) { diff --git a/Mage.Sets/src/mage/cards/u/UlvenwaldPrimordials.java b/Mage.Sets/src/mage/cards/u/UlvenwaldPrimordials.java index a1fc3a03b371..b76a5fe24acd 100644 --- a/Mage.Sets/src/mage/cards/u/UlvenwaldPrimordials.java +++ b/Mage.Sets/src/mage/cards/u/UlvenwaldPrimordials.java @@ -1,23 +1,16 @@ - package mage.cards.u; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.RegenerateSourceEffect; -import mage.abilities.effects.common.TransformSourceEffect; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.constants.Zone; + +import java.util.UUID; /** * @author nantuko @@ -25,7 +18,7 @@ public final class UlvenwaldPrimordials extends CardImpl { public UlvenwaldPrimordials(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.WEREWOLF); this.color.setGreen(true); @@ -37,11 +30,10 @@ public UlvenwaldPrimordials(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(5); // {G}: Regenerate Ulvenwald Primordials. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new ManaCostsImpl("{G}"))); + this.addAbility(new SimpleActivatedAbility(new RegenerateSourceEffect(), new ManaCostsImpl("{G}"))); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Ulvenwald Primordials. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private UlvenwaldPrimordials(final UlvenwaldPrimordials card) { diff --git a/Mage.Sets/src/mage/cards/u/UlvenwaldTracker.java b/Mage.Sets/src/mage/cards/u/UlvenwaldTracker.java index ba822b5f05a4..5eace8f7904b 100644 --- a/Mage.Sets/src/mage/cards/u/UlvenwaldTracker.java +++ b/Mage.Sets/src/mage/cards/u/UlvenwaldTracker.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.target.Target; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/u/Umbilicus.java b/Mage.Sets/src/mage/cards/u/Umbilicus.java index ee4024bedf0a..e1a6e26fc710 100644 --- a/Mage.Sets/src/mage/cards/u/Umbilicus.java +++ b/Mage.Sets/src/mage/cards/u/Umbilicus.java @@ -1,4 +1,3 @@ - package mage.cards.u; import java.util.UUID; @@ -24,7 +23,7 @@ public final class Umbilicus extends CardImpl { public Umbilicus(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); // At the beginning of each player's upkeep, that player returns a permanent they control to its owner's hand unless they pay 2 life. Ability ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new BloodClockEffect(), TargetController.ANY, false, true); @@ -63,7 +62,8 @@ public boolean apply(Game game, Ability source) { if (player == null) { return false; } - if (player.getLife() > 2 && player.chooseUse(Outcome.Neutral, "Pay 2 life? If you don't, return a permanent you control to its owner's hand.", source, game)) { + if (player.getLife() > 2 + && player.chooseUse(Outcome.Neutral, "Pay 2 life? If you don't, return a permanent you control to its owner's hand.", source, game)) { player.loseLife(2, game, source, false); game.informPlayers(player.getLogName() + " pays 2 life. They will not return a permanent they control."); return true; @@ -73,10 +73,10 @@ public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(target.getFirstTarget()); if (permanent != null) { game.informPlayers(player.getLogName() + " returns " + permanent.getName() + " to hand."); - return permanent.moveToZone(Zone.HAND, source, game, false); + return player.moveCards(permanent, Zone.HAND, source, game); } } } return false; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/u/UmbralJuke.java b/Mage.Sets/src/mage/cards/u/UmbralJuke.java new file mode 100644 index 000000000000..11d53c19d02c --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UmbralJuke.java @@ -0,0 +1,42 @@ +package mage.cards.u; + +import mage.abilities.Mode; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.SacrificeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.game.permanent.token.SilverquillToken; +import mage.target.TargetPlayer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UmbralJuke extends CardImpl { + + public UmbralJuke(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}"); + + // Choose one: — + // • Target player sacrifices a creature or planeswalker. + this.getSpellAbility().addEffect(new SacrificeEffect( + StaticFilters.FILTER_PERMANENT_CREATURE_OR_PLANESWALKER, 1, "Target player" + )); + this.getSpellAbility().addTarget(new TargetPlayer()); + + // • Create a 2/1 white and black Inkling creature token with flying. + this.getSpellAbility().addMode(new Mode(new CreateTokenEffect(new SilverquillToken()))); + } + + private UmbralJuke(final UmbralJuke card) { + super(card); + } + + @Override + public UmbralJuke copy() { + return new UmbralJuke(this); + } +} diff --git a/Mage.Sets/src/mage/cards/u/UmezawasJitte.java b/Mage.Sets/src/mage/cards/u/UmezawasJitte.java index e96c5e62a988..1aa461fba2eb 100644 --- a/Mage.Sets/src/mage/cards/u/UmezawasJitte.java +++ b/Mage.Sets/src/mage/cards/u/UmezawasJitte.java @@ -91,8 +91,7 @@ public UmezawasJitteAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { switch(event.getType()) { - case DAMAGED_CREATURE: - case DAMAGED_PLANESWALKER: + case DAMAGED_PERMANENT: case DAMAGED_PLAYER: case COMBAT_DAMAGE_STEP_PRE: return true; diff --git a/Mage.Sets/src/mage/cards/u/UnbenderTine.java b/Mage.Sets/src/mage/cards/u/UnbenderTine.java index 7f708320fdce..1e8b4484aa84 100644 --- a/Mage.Sets/src/mage/cards/u/UnbenderTine.java +++ b/Mage.Sets/src/mage/cards/u/UnbenderTine.java @@ -12,7 +12,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; diff --git a/Mage.Sets/src/mage/cards/u/UnboundFlourishing.java b/Mage.Sets/src/mage/cards/u/UnboundFlourishing.java index f8b55668243a..fbca56b2702a 100644 --- a/Mage.Sets/src/mage/cards/u/UnboundFlourishing.java +++ b/Mage.Sets/src/mage/cards/u/UnboundFlourishing.java @@ -174,7 +174,6 @@ public boolean apply(Game game, Ability source) { if (needObject instanceof StackAbility) { StackAbility stackAbility = (StackAbility) needObject; stackAbility.createCopyOnStack(game, source, source.getControllerId(), true); - game.informPlayers(sourcePermanent.getName() + ": " + controller.getLogName() + " copied activated ability"); return true; } @@ -182,7 +181,6 @@ public boolean apply(Game game, Ability source) { if (needObject instanceof Spell) { Spell spell = (Spell) needObject; spell.createCopyOnStack(game, source, source.getControllerId(), true); - game.informPlayers(sourcePermanent.getName() + ": " + controller.getLogName() + " copied casted spell"); return true; } } diff --git a/Mage.Sets/src/mage/cards/u/UnburialRites.java b/Mage.Sets/src/mage/cards/u/UnburialRites.java index 6cf5a696e183..222d344b74b6 100644 --- a/Mage.Sets/src/mage/cards/u/UnburialRites.java +++ b/Mage.Sets/src/mage/cards/u/UnburialRites.java @@ -21,7 +21,7 @@ public UnburialRites(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}"); // Return target creature card from your graveyard to the battlefield. - this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect(false, false)); + this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); // Flashback {3}{W} diff --git a/Mage.Sets/src/mage/cards/u/UncageTheMenagerie.java b/Mage.Sets/src/mage/cards/u/UncageTheMenagerie.java index 6681c4ae2047..549b58af8b9c 100644 --- a/Mage.Sets/src/mage/cards/u/UncageTheMenagerie.java +++ b/Mage.Sets/src/mage/cards/u/UncageTheMenagerie.java @@ -46,7 +46,7 @@ class UncageTheMenagerieEffect extends OneShotEffect { public UncageTheMenagerieEffect() { super(Outcome.DrawCard); - this.staticText = "Search your library for up to X creature cards with different names that each have converted mana cost X, reveal them, put them into your hand, then shuffle your library."; + this.staticText = "Search your library for up to X creature cards with different names that each have mana value X, reveal them, put them into your hand, then shuffle."; } public UncageTheMenagerieEffect(final UncageTheMenagerieEffect effect) { @@ -94,7 +94,7 @@ class UncageTheMenagerieTarget extends TargetCardInLibrary { private int xValue; public UncageTheMenagerieTarget(int xValue) { - super(0, xValue, new FilterCreatureCard(xValue + " creature cards with different names with converted mana cost " + xValue)); + super(0, xValue, new FilterCreatureCard(xValue + " creature cards with different names with mana value " + xValue)); this.xValue = xValue; } @@ -119,7 +119,7 @@ public boolean canTarget(UUID playerId, UUID id, Ability source, Cards cards, Ga } } - if (!(card.isCreature() && card.getConvertedManaCost() == xValue)) { + if (!(card.isCreature() && card.getManaValue() == xValue)) { return false; } diff --git a/Mage.Sets/src/mage/cards/u/UncontrollableAnger.java b/Mage.Sets/src/mage/cards/u/UncontrollableAnger.java index c967d562ab75..02b7de1005fe 100644 --- a/Mage.Sets/src/mage/cards/u/UncontrollableAnger.java +++ b/Mage.Sets/src/mage/cards/u/UncontrollableAnger.java @@ -44,7 +44,7 @@ public UncontrollableAnger(UUID ownerId, CardSetInfo setInfo) { // Enchanted creature gets +2/+2 and attacks each turn if able. ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(2, 2, Duration.WhileOnBattlefield)); Effect effect = new AttacksIfAbleAttachedEffect(Duration.WhileOnBattlefield, AttachmentType.AURA); - effect.setText("and attacks each turn if able"); + effect.setText("and attacks each combat if able"); ability.addEffect(effect); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/u/UndercityNecrolisk.java b/Mage.Sets/src/mage/cards/u/UndercityNecrolisk.java index 3c919b4473f3..739a1181e009 100644 --- a/Mage.Sets/src/mage/cards/u/UndercityNecrolisk.java +++ b/Mage.Sets/src/mage/cards/u/UndercityNecrolisk.java @@ -18,7 +18,7 @@ import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledPermanent; /** diff --git a/Mage.Sets/src/mage/cards/u/UndercityScavenger.java b/Mage.Sets/src/mage/cards/u/UndercityScavenger.java index a985d82e089e..22bf1616c121 100644 --- a/Mage.Sets/src/mage/cards/u/UndercityScavenger.java +++ b/Mage.Sets/src/mage/cards/u/UndercityScavenger.java @@ -13,7 +13,7 @@ import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/u/UndyingFlames.java b/Mage.Sets/src/mage/cards/u/UndyingFlames.java index f05ebadf4753..79e434cd03df 100644 --- a/Mage.Sets/src/mage/cards/u/UndyingFlames.java +++ b/Mage.Sets/src/mage/cards/u/UndyingFlames.java @@ -47,7 +47,7 @@ class UndyingFlamesEffect extends OneShotEffect { public UndyingFlamesEffect() { super(Outcome.Benefit); - staticText = "Exile cards from the top of your library until you exile a nonland card. {this} deals damage to any target equal to that card's converted mana cost"; + staticText = "Exile cards from the top of your library until you exile a nonland card. {this} deals damage to any target equal to that card's mana value"; } public UndyingFlamesEffect(final UndyingFlamesEffect effect) { @@ -64,7 +64,7 @@ public boolean apply(Game game, Ability source) { if (card != null) { controller.moveCards(card, Zone.EXILED, source, game); if (!card.isLand()) { - new DamageTargetEffect(card.getConvertedManaCost()).apply(game, source); + new DamageTargetEffect(card.getManaValue()).apply(game, source); break; } } diff --git a/Mage.Sets/src/mage/cards/u/Unearth.java b/Mage.Sets/src/mage/cards/u/Unearth.java index e31ada62bf5e..a764d042f4bc 100644 --- a/Mage.Sets/src/mage/cards/u/Unearth.java +++ b/Mage.Sets/src/mage/cards/u/Unearth.java @@ -11,7 +11,7 @@ import mage.constants.ComparisonType; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInYourGraveyard; /** @@ -20,10 +20,10 @@ */ public final class Unearth extends CardImpl { - private static final FilterCard filter = new FilterCreatureCard("creature card with converted mana cost 3 or less from your graveyard"); + private static final FilterCard filter = new FilterCreatureCard("creature card with mana value 3 or less from your graveyard"); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public Unearth(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/u/UneshCriosphinxSovereign.java b/Mage.Sets/src/mage/cards/u/UneshCriosphinxSovereign.java index 2b60a7057a41..50f04bb915a3 100644 --- a/Mage.Sets/src/mage/cards/u/UneshCriosphinxSovereign.java +++ b/Mage.Sets/src/mage/cards/u/UneshCriosphinxSovereign.java @@ -19,6 +19,7 @@ import mage.target.common.TargetOpponent; import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.UUID; @@ -84,6 +85,8 @@ public UneshCriosphinxSovereignEffect copy() { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); + Set cardsToGraveyard = new LinkedHashSet<>(); + Set cardsToHand = new LinkedHashSet<>(); MageObject sourceObject = source.getSourceObject(game); if (controller == null || sourceObject == null) { return false; @@ -134,8 +137,9 @@ public boolean apply(Game game, Ability source) { if (i < pile1.size()) { sb.append(", "); } - card.moveToZone(pile1Zone, source, game, false); + cardsToGraveyard.add(card); } + controller.moveCards(cardsToGraveyard, pile1Zone, source, game); game.informPlayers(sb.toString()); sb = new StringBuilder("Pile 2, going to ").append(pile2Zone == Zone.HAND ? "Hand" : "Graveyard").append(':'); @@ -146,8 +150,9 @@ public boolean apply(Game game, Ability source) { if (i < pile2.size()) { sb.append(", "); } - card.moveToZone(pile2Zone, source, game, false); + cardsToHand.add(card); } + controller.moveCards(cardsToHand, pile2Zone, source, game); game.informPlayers(sb.toString()); } diff --git a/Mage.Sets/src/mage/cards/u/UnifyingTheory.java b/Mage.Sets/src/mage/cards/u/UnifyingTheory.java index f498d5ca2615..6b417c9430ef 100644 --- a/Mage.Sets/src/mage/cards/u/UnifyingTheory.java +++ b/Mage.Sets/src/mage/cards/u/UnifyingTheory.java @@ -13,6 +13,7 @@ import mage.constants.Outcome; import mage.constants.SetTargetPointer; import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; @@ -28,7 +29,7 @@ public UnifyingTheory(UUID ownerId, CardSetInfo setInfo) { // Whenever a player casts a spell, that player may pay {2}. If the player does, they draw a card. - this.addAbility(new SpellCastAllTriggeredAbility(new UnifyingTheoryEffect() , new FilterSpell("a spell"), false, SetTargetPointer.PLAYER)); + this.addAbility(new SpellCastAllTriggeredAbility(new UnifyingTheoryEffect() , StaticFilters.FILTER_SPELL_A, false, SetTargetPointer.PLAYER)); } private UnifyingTheory(final UnifyingTheory card) { diff --git a/Mage.Sets/src/mage/cards/u/UnmooredEgo.java b/Mage.Sets/src/mage/cards/u/UnmooredEgo.java index 7a8cb46c290a..57ad6a6bd89a 100644 --- a/Mage.Sets/src/mage/cards/u/UnmooredEgo.java +++ b/Mage.Sets/src/mage/cards/u/UnmooredEgo.java @@ -48,7 +48,7 @@ class UnmooredEgoEffect extends OneShotEffect { public UnmooredEgoEffect() { super(Outcome.Benefit); - this.staticText = "Search target opponent's graveyard, hand, and library for up to four cards with that name and exile them. That player shuffles their library, then draws a card for each card exiled from their hand this way"; + this.staticText = "Search target opponent's graveyard, hand, and library for up to four cards with that name and exile them. That player shuffles, then draws a card for each card exiled from their hand this way"; } public UnmooredEgoEffect(final UnmooredEgoEffect effect) { diff --git a/Mage.Sets/src/mage/cards/u/UnrulyMob.java b/Mage.Sets/src/mage/cards/u/UnrulyMob.java index 84024dfa1036..97add7d85cfa 100644 --- a/Mage.Sets/src/mage/cards/u/UnrulyMob.java +++ b/Mage.Sets/src/mage/cards/u/UnrulyMob.java @@ -12,7 +12,7 @@ import mage.constants.TargetController; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/u/UnstableShapeshifter.java b/Mage.Sets/src/mage/cards/u/UnstableShapeshifter.java index a005cd06a1db..9a6b24adad42 100644 --- a/Mage.Sets/src/mage/cards/u/UnstableShapeshifter.java +++ b/Mage.Sets/src/mage/cards/u/UnstableShapeshifter.java @@ -16,7 +16,7 @@ import mage.constants.SetTargetPointer; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.util.functions.EmptyCopyApplier; diff --git a/Mage.Sets/src/mage/cards/u/UnwillingIngredient.java b/Mage.Sets/src/mage/cards/u/UnwillingIngredient.java new file mode 100644 index 000000000000..a8a75e2d28d2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UnwillingIngredient.java @@ -0,0 +1,54 @@ +package mage.cards.u; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.ExileSourceFromGraveCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.LoseLifeSourceControllerEffect; +import mage.abilities.keyword.MenaceAbility; +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 TheElk801 + */ +public final class UnwillingIngredient extends CardImpl { + + public UnwillingIngredient(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}"); + + this.subtype.add(SubType.FROG); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Menace + this.addAbility(new MenaceAbility()); + + // {2}{B}, Exile Unwilling Ingredient from your graveyard: You draw a card and you lose 1 life. + Ability ability = new SimpleActivatedAbility( + Zone.GRAVEYARD, + new DrawCardSourceControllerEffect(1) + .setText("you draw a card"), + new ManaCostsImpl("{2}{B}") + ); + ability.addEffect(new LoseLifeSourceControllerEffect(1).concatBy("and")); + ability.addCost(new ExileSourceFromGraveCost()); + this.addAbility(ability); + } + + private UnwillingIngredient(final UnwillingIngredient card) { + super(card); + } + + @Override + public UnwillingIngredient copy() { + return new UnwillingIngredient(this); + } +} diff --git a/Mage.Sets/src/mage/cards/u/UroTitanOfNaturesWrath.java b/Mage.Sets/src/mage/cards/u/UroTitanOfNaturesWrath.java index 1f3853c1d2b2..7051cd1de457 100644 --- a/Mage.Sets/src/mage/cards/u/UroTitanOfNaturesWrath.java +++ b/Mage.Sets/src/mage/cards/u/UroTitanOfNaturesWrath.java @@ -40,8 +40,8 @@ public UroTitanOfNaturesWrath(UUID ownerId, CardSetInfo setInfo) { // Whenever Uro enters the battlefield or attacks, you gain 3 life and draw a card, then you may put a land card from your hand onto the battlefield. Ability ability = new EntersBattlefieldOrAttacksSourceTriggeredAbility(new GainLifeEffect(3)); - ability.addEffect(new DrawCardSourceControllerEffect(1).setText("and draw a card, then")); - ability.addEffect(new PutCardFromHandOntoBattlefieldEffect(StaticFilters.FILTER_CARD_LAND_A)); + ability.addEffect(new DrawCardSourceControllerEffect(1).concatBy("and")); + ability.addEffect(new PutCardFromHandOntoBattlefieldEffect(StaticFilters.FILTER_CARD_LAND_A).concatBy(", then")); this.addAbility(ability); // Escape-{G}{G}{U}{U}, Exile five other cards from your graveyard. diff --git a/Mage.Sets/src/mage/cards/u/UrzaAcademyHeadmaster.java b/Mage.Sets/src/mage/cards/u/UrzaAcademyHeadmaster.java index fc3a522e5678..43021ee3c4c2 100644 --- a/Mage.Sets/src/mage/cards/u/UrzaAcademyHeadmaster.java +++ b/Mage.Sets/src/mage/cards/u/UrzaAcademyHeadmaster.java @@ -186,7 +186,7 @@ public boolean apply(Game game, Ability source) { break; case 11: // KARN LIBERATED 1 sb.append("Target player exiles a card from their hand."); - effects.add(new ExileFromZoneTargetEffect(Zone.HAND, null, "", new FilterCard())); + effects.add(new ExileFromZoneTargetEffect(Zone.HAND, true)); target = new TargetPlayer(); break; case 12: // NISSA SAGE ANIMIST 1 @@ -286,7 +286,7 @@ public boolean apply(Game game, Ability source) { effects.add(new RevealLibraryPutIntoHandEffect(5, filter3, Zone.LIBRARY)); break; case 12: // (altered) LILIANA VESS 2 - sb.append("Search your library for a card and put that card into your hand. Then shuffle your library."); + sb.append("Search your library for a card and put that card into your hand. Then shuffle."); effects.add(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(new FilterCard("a card")), false, true)); break; case 13: // (double) LILIANA OF THE VEIL 2 diff --git a/Mage.Sets/src/mage/cards/u/UrzasFactory.java b/Mage.Sets/src/mage/cards/u/UrzasFactory.java index c1632f5ccc09..a38b31e00611 100644 --- a/Mage.Sets/src/mage/cards/u/UrzasFactory.java +++ b/Mage.Sets/src/mage/cards/u/UrzasFactory.java @@ -1,4 +1,3 @@ - package mage.cards.u; import mage.abilities.Ability; @@ -28,7 +27,8 @@ public UrzasFactory(UUID ownerId, CardSetInfo setInfo) { // {tap}: Add {C}. this.addAbility(new ColorlessManaAbility()); - // {7}, {tap}: Create a 2/2 colorless Assembly-Worker artifact creature token. + + // {7}, {T}: Create a 2/2 colorless Assembly-Worker artifact creature token. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new AssemblyWorkerToken()), new GenericManaCost(7)); ability.addCost(new TapSourceCost()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/u/UrzasHotTub.java b/Mage.Sets/src/mage/cards/u/UrzasHotTub.java index 8dc539a4b347..57b1c8ada01c 100644 --- a/Mage.Sets/src/mage/cards/u/UrzasHotTub.java +++ b/Mage.Sets/src/mage/cards/u/UrzasHotTub.java @@ -50,7 +50,7 @@ class UrzasHotTubEffect extends OneShotEffect { public UrzasHotTubEffect() { super(Outcome.ReturnToHand); - this.staticText = "Search your library for a card that shares a complete word in its name with the discarded card, reveal it, put it into your hand, then shuffle your library"; + this.staticText = "Search your library for a card that shares a complete word in its name with the discarded card, reveal it, put it into your hand, then shuffle"; } public UrzasHotTubEffect(final UrzasHotTubEffect effect) { diff --git a/Mage.Sets/src/mage/cards/u/UrzasRage.java b/Mage.Sets/src/mage/cards/u/UrzasRage.java index a2941e876ba1..509b32122906 100644 --- a/Mage.Sets/src/mage/cards/u/UrzasRage.java +++ b/Mage.Sets/src/mage/cards/u/UrzasRage.java @@ -1,7 +1,5 @@ - package mage.cards.u; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.KickedCondition; @@ -16,8 +14,9 @@ import mage.constants.Zone; import mage.target.common.TargetAnyTarget; +import java.util.UUID; + /** - * * @author FenrisulfrX */ public final class UrzasRage extends CardImpl { @@ -36,9 +35,11 @@ public UrzasRage(UUID ownerId, CardSetInfo setInfo) { this.addAbility(ability); // Urza's Rage deals 3 damage to any target. If Urza's Rage was kicked, instead it deals 10 damage to that creature or player and the damage can't be prevented. - this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new DamageTargetEffect(10, false), - new DamageTargetEffect(3), KickedCondition.instance, - "{this} deals 3 damage to any target. if this spell was kicked, instead it deals 10 damage to that permanent or player and the damage can't be prevented.")); + this.getSpellAbility().addEffect(new ConditionalOneShotEffect( + new DamageTargetEffect(10, false), new DamageTargetEffect(3), + KickedCondition.instance, "{this} deals 3 damage to any target. If this spell was kicked, " + + "instead it deals 10 damage to that permanent or player and the damage can't be prevented." + )); this.getSpellAbility().addTarget(new TargetAnyTarget()); } diff --git a/Mage.Sets/src/mage/cards/u/UvildaDeanOfPerfection.java b/Mage.Sets/src/mage/cards/u/UvildaDeanOfPerfection.java new file mode 100644 index 000000000000..7f88faf0fd4e --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UvildaDeanOfPerfection.java @@ -0,0 +1,324 @@ +package mage.cards.u; + +import mage.ApprovingObject; +import mage.MageObject; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; +import mage.cards.*; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInHand; +import mage.util.CardUtil; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UvildaDeanOfPerfection extends ModalDoubleFacesCard { + + public UvildaDeanOfPerfection(UUID ownerId, CardSetInfo setInfo) { + super( + ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.DJINN, SubType.WIZARD}, "{2}{U}", + "Nassari, Dean of Expression", + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.EFREET, SubType.SHAMAN}, "{3}{R}{R}" + ); + + // 1. + // Uvilda, Dean of Perfection + // Legendary Creature - Djinn Wizard + this.getLeftHalfCard().addSuperType(SuperType.LEGENDARY); + this.getLeftHalfCard().setPT(2, 2); + + // {T}: You may exile an instant or sorcery card from your hand and put three hone counters on it. It gains "At the beginning of your upkeep, if this card is exiled, remove a hone counter from it" and "When the last hone counter is removed from this card, if it's exiled, you may cast it. It costs {4} less to cast this way." + this.getLeftHalfCard().addAbility(new SimpleActivatedAbility( + new UvildaDeanOfPerfectionEffect(), new TapSourceCost() + )); + + // 2. + // Nassari, Dean of Expression + // Legendary Creature - Efreet Shaman + this.getRightHalfCard().addSuperType(SuperType.LEGENDARY); + this.getRightHalfCard().setPT(4, 4); + + // At the beginning of your upkeep, exile the top card of each opponent's library. Until end of turn, you may cast spells from among those exiled cards, and you many spend mana as though it were mana of any color to cast those spells. + this.getRightHalfCard().addAbility(new BeginningOfUpkeepTriggeredAbility( + new NassariDeanOfExpressionEffect(), TargetController.YOU, false + )); + + // Whenever you cast a spell from exile, put a +1/+1 counter on Nassari, Dean of Expression. + this.getRightHalfCard().addAbility(new NassariDeanOfExpressionTriggeredAbility()); + } + + private UvildaDeanOfPerfection(final UvildaDeanOfPerfection card) { + super(card); + } + + @Override + public UvildaDeanOfPerfection copy() { + return new UvildaDeanOfPerfection(this); + } +} + +class UvildaDeanOfPerfectionEffect extends OneShotEffect { + + UvildaDeanOfPerfectionEffect() { + super(Outcome.Benefit); + staticText = "you may exile an instant or sorcery card from your hand and put three hone counters on it. " + + "It gains \"At the beginning of your upkeep, if this card is exiled, " + + "remove a hone counter from it\" and \"When the last hone counter is removed from this card, " + + "if it's exiled, you may cast it. It costs {4} less to cast this way.\""; + } + + private UvildaDeanOfPerfectionEffect(final UvildaDeanOfPerfectionEffect effect) { + super(effect); + } + + @Override + public UvildaDeanOfPerfectionEffect copy() { + return new UvildaDeanOfPerfectionEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null || player.getHand().count( + StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, game + ) < 1) { + return false; + } + TargetCard target = new TargetCardInHand( + 0, 1, StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY + ); + player.choose(outcome, player.getHand(), target, game); + Card card = game.getCard(target.getFirstTarget()); + if (card == null) { + return false; + } + player.moveCards(card, Zone.EXILED, source, game); + if (game.getState().getZone(card.getId()) != Zone.EXILED) { + return false; + } + card.addCounters(CounterType.HONE.createInstance(3), source.getControllerId(), source, game); + game.addEffect(new UvildaDeanOfPerfectionGainAbilityEffect(card, game), source); + return true; + } +} + +class UvildaDeanOfPerfectionGainAbilityEffect extends ContinuousEffectImpl { + + private final MageObjectReference mor; + + UvildaDeanOfPerfectionGainAbilityEffect(Card card, Game game) { + super(Duration.Custom, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + this.mor = new MageObjectReference(card, game); + } + + private UvildaDeanOfPerfectionGainAbilityEffect(final UvildaDeanOfPerfectionGainAbilityEffect effect) { + super(effect); + this.mor = effect.mor; + } + + @Override + public UvildaDeanOfPerfectionGainAbilityEffect copy() { + return new UvildaDeanOfPerfectionGainAbilityEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Card card = mor.getCard(game); + if (card == null) { + discard(); + return true; + } + Ability ability = new ConditionalInterveningIfTriggeredAbility( + new BeginningOfUpkeepTriggeredAbility( + Zone.EXILED, new RemoveCounterSourceEffect(CounterType.HONE.createInstance()), + TargetController.YOU, false + ), UvildaDeanOfPerfectionCondition.instance, "At the beginning of your upkeep, " + + "if this card is exiled, remove a hone counter from it." + ); + ability.setSourceId(card.getId()); + ability.setControllerId(source.getControllerId()); + game.getState().addOtherAbility(card, ability); + ability = new UvildaDeanOfPerfectionTriggeredAbility(); + ability.setSourceId(card.getId()); + ability.setControllerId(source.getControllerId()); + game.getState().addOtherAbility(card, ability); + return true; + } +} + +enum UvildaDeanOfPerfectionCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + return game.getState().getZone(source.getSourceId()) == Zone.EXILED; + } +} + +class UvildaDeanOfPerfectionTriggeredAbility extends TriggeredAbilityImpl { + + UvildaDeanOfPerfectionTriggeredAbility() { + super(Zone.EXILED, new UvildaDeanOfPerfectionCastEffect()); + } + + private UvildaDeanOfPerfectionTriggeredAbility(final UvildaDeanOfPerfectionTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.COUNTERS_REMOVED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + MageObject sourceObject = getSourceObjectIfItStillExists(game); + return event.getTargetId().equals(this.getSourceId()) + && sourceObject instanceof Card + && ((Card) sourceObject).getCounters(game).getCount(CounterType.HONE) == 0 + && event.getAmount() > 0 + && event.getData().equals("hone"); + } + + @Override + public boolean checkInterveningIfClause(Game game) { + return game.getState().getZone(getSourceId()) == Zone.EXILED; + } + + @Override + public UvildaDeanOfPerfectionTriggeredAbility copy() { + return new UvildaDeanOfPerfectionTriggeredAbility(this); + } + + @Override + public String getRule() { + return "When the last hone counter is removed from this card, if it's exiled, " + + "you may cast it. It costs {4} less to cast this way."; + } +} + +class UvildaDeanOfPerfectionCastEffect extends OneShotEffect { + + UvildaDeanOfPerfectionCastEffect() { + super(Outcome.Benefit); + } + + private UvildaDeanOfPerfectionCastEffect(final UvildaDeanOfPerfectionCastEffect effect) { + super(effect); + } + + @Override + public UvildaDeanOfPerfectionCastEffect copy() { + return new UvildaDeanOfPerfectionCastEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObjectIfItStillExists(game); + if (player == null || !(sourceObject instanceof Card)) { + return false; + } + Card card = (Card) sourceObject; + if (!player.chooseUse(outcome, "Cast " + card.getName() + '?', source, game)) { + return false; + } + SpellAbility spellAbility = player.chooseAbilityForCast(card, game, true); + if (spellAbility == null) { + return false; + } + CardUtil.reduceCost(spellAbility, 4); + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); + player.cast(spellAbility, game, false, new ApprovingObject(source, game)); + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); + return true; + } +} + +class NassariDeanOfExpressionEffect extends OneShotEffect { + + NassariDeanOfExpressionEffect() { + super(Outcome.Benefit); + staticText = "exile the top card of each opponent's library. Until end of turn, " + + "you may cast spells from among those exiled cards, and you may spend mana " + + "as though it were mana of any color to cast those spells"; + } + + private NassariDeanOfExpressionEffect(final NassariDeanOfExpressionEffect effect) { + super(effect); + } + + @Override + public NassariDeanOfExpressionEffect copy() { + return new NassariDeanOfExpressionEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(); + game.getOpponents(source.getControllerId()) + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .map(Player::getLibrary) + .map(p -> p.getFromTop(game)) + .forEach(cards::add); + player.moveCards(cards, Zone.EXILED, source, game); + cards.retainZone(Zone.EXILED, game); + if (cards.isEmpty()) { + return false; + } + for (Card card : cards.getCards(game)) { + CardUtil.makeCardPlayable(game, source, card, Duration.EndOfTurn, true); + } + return true; + } +} + +class NassariDeanOfExpressionTriggeredAbility extends SpellCastControllerTriggeredAbility { + + NassariDeanOfExpressionTriggeredAbility() { + super(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false); + this.rule = "Whenever you cast a spell from exile, put a +1/+1 counter on {this}."; + } + + private NassariDeanOfExpressionTriggeredAbility(final NassariDeanOfExpressionTriggeredAbility ability) { + super(ability); + } + + @Override + public NassariDeanOfExpressionTriggeredAbility copy() { + return new NassariDeanOfExpressionTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getZone() == Zone.EXILED && super.checkTrigger(event, game); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VadrokApexOfThunder.java b/Mage.Sets/src/mage/cards/v/VadrokApexOfThunder.java index 51df92511119..fe4d5059d370 100644 --- a/Mage.Sets/src/mage/cards/v/VadrokApexOfThunder.java +++ b/Mage.Sets/src/mage/cards/v/VadrokApexOfThunder.java @@ -15,7 +15,7 @@ import mage.constants.SuperType; import mage.filter.FilterCard; import mage.filter.common.FilterNoncreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInYourGraveyard; import java.util.UUID; @@ -26,11 +26,11 @@ public final class VadrokApexOfThunder extends CardImpl { private static final FilterCard filter = new FilterNoncreatureCard( - "noncreature card with converted mana cost 3 or less from your graveyard" + "noncreature card with mana value 3 or less from your graveyard" ); static { - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public VadrokApexOfThunder(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/v/ValentinDeanOfTheVein.java b/Mage.Sets/src/mage/cards/v/ValentinDeanOfTheVein.java new file mode 100644 index 000000000000..1203c71a6af6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/ValentinDeanOfTheVein.java @@ -0,0 +1,122 @@ +package mage.cards.v; + +import mage.abilities.Ability; +import mage.abilities.common.GainLifeControllerTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.MenaceAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.token.WitherbloomToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ValentinDeanOfTheVein extends ModalDoubleFacesCard { + + public ValentinDeanOfTheVein(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.VAMPIRE, SubType.WARLOCK}, "{B}", + "Lisette Dean of the Root", new CardType[]{CardType.CREATURE}, new SubType[]{SubType.HUMAN, SubType.DRUID}, "{2}{G}{G}" + ); + + // 1. + // Valentin, Dean of the Vein + // Legendary Creature - Vampire Warlock + this.getLeftHalfCard().addSuperType(SuperType.LEGENDARY); + this.getLeftHalfCard().setPT(1, 1); + + // Menace + this.getLeftHalfCard().addAbility(new MenaceAbility()); + + // Lifelink + this.getLeftHalfCard().addAbility(LifelinkAbility.getInstance()); + + // If a nontoken creature an opponent controls would die, exile it instead. When you do, you may pay {2}. If you do, create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life." + this.getLeftHalfCard().addAbility(new SimpleStaticAbility(new ValentinDeanOfTheVeinEffect())); + + // 2. + // Lisette, Dean of the Root + // Legendary Creature - Human Druid + this.getRightHalfCard().addSuperType(SuperType.LEGENDARY); + this.getRightHalfCard().setPT(4, 4); + + // Whenever you gain life, you may pay {1}. If you do, put a +1/+1 counter on each creature you control and those creatures gain trample until end of turn. + this.getRightHalfCard().addAbility(new GainLifeControllerTriggeredAbility( + new DoIfCostPaid(new AddCountersAllEffect( + CounterType.P1P1.createInstance(), StaticFilters.FILTER_CONTROLLED_CREATURE + ), new GenericManaCost(1)).addEffect(new GainAbilityControlledEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_CONTROLLED_CREATURE + ).setText("and those creatures gain trample until end of turn")), false + )); + } + + private ValentinDeanOfTheVein(final ValentinDeanOfTheVein card) { + super(card); + } + + @Override + public ValentinDeanOfTheVein copy() { + return new ValentinDeanOfTheVein(this); + } +} + +class ValentinDeanOfTheVeinEffect extends ReplacementEffectImpl { + + ValentinDeanOfTheVeinEffect() { + super(Duration.WhileOnBattlefield, Outcome.Exile); + staticText = "if a nontoken creature an opponent controls would die, exile it instead. When you do, " + + "you may pay {2}. If you do, create a 1/1 black and green Pest creature token " + + "with \"When this creature dies, you gain 1 life.\""; + } + + private ValentinDeanOfTheVeinEffect(final ValentinDeanOfTheVeinEffect effect) { + super(effect); + } + + @Override + public ValentinDeanOfTheVeinEffect copy() { + return new ValentinDeanOfTheVeinEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + ((ZoneChangeEvent) event).setToZone(Zone.EXILED); + game.fireReflexiveTriggeredAbility(new ReflexiveTriggeredAbility( + new DoIfCostPaid( + new CreateTokenEffect(new WitherbloomToken()), new GenericManaCost(2) + ), false, "you may pay {2}. If you do, create a 1/1 black and green " + + "Pest creature token with \"When this creature dies, you gain 1 life.\"" + ), source); + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return ((ZoneChangeEvent) event).isDiesEvent() + && game.getOpponents(game.getControllerId(event.getTargetId())).contains(source.getControllerId()); + } + +} diff --git a/Mage.Sets/src/mage/cards/v/ValkiGodOfLies.java b/Mage.Sets/src/mage/cards/v/ValkiGodOfLies.java index 7942ebf91115..8d2d2538ac2f 100644 --- a/Mage.Sets/src/mage/cards/v/ValkiGodOfLies.java +++ b/Mage.Sets/src/mage/cards/v/ValkiGodOfLies.java @@ -25,10 +25,10 @@ import mage.cards.Cards; import mage.filter.FilterCard; import mage.filter.StaticFilters; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.ExileZone; import mage.game.Game; -import mage.game.command.emblems.TibaltCosmicImposterEmblem; +import mage.game.command.emblems.TibaltCosmicImpostorEmblem; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; @@ -48,7 +48,7 @@ public final class ValkiGodOfLies extends ModalDoubleFacesCard { public ValkiGodOfLies(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, new SubType[]{SubType.GOD}, "{1}{B}", - "Tibalt, Cosmic Imposter", new CardType[]{CardType.PLANESWALKER}, new SubType[]{SubType.TIBALT}, "{5}{B}{R}" + "Tibalt, Cosmic Impostor", new CardType[]{CardType.PLANESWALKER}, new SubType[]{SubType.TIBALT}, "{5}{B}{R}" ); // 1. @@ -70,7 +70,7 @@ public ValkiGodOfLies(UUID ownerId, CardSetInfo setInfo) { this.getRightHalfCard().addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); // As Tibalt enters the battlefield, you get an emblem with “You may play cards exiled with Tibalt, Cosmic Impostor, and you may spend mana as though it were mana of any color to cast those spells.” - this.getRightHalfCard().addAbility(new AsEntersBattlefieldAbility(new GetEmblemEffect(new TibaltCosmicImposterEmblem()))); + this.getRightHalfCard().addAbility(new AsEntersBattlefieldAbility(new GetEmblemEffect(new TibaltCosmicImpostorEmblem()))); // +2: Exile the top card of each player’s library. this.getRightHalfCard().addAbility(new LoyaltyAbility(new ExileTopCardEachPlayersLibrary(), 2)); @@ -124,6 +124,7 @@ public boolean apply(Game game, Ability source) { if (opponent != null) { opponent.revealCards(source, opponent.getHand(), game); TargetCard targetToExile = new TargetCard(Zone.HAND, StaticFilters.FILTER_CARD_CREATURE); + targetToExile.withChooseHint("card to exile"); targetToExile.setNotTarget(true); if (controller.choose(Outcome.Exile, opponent.getHand(), targetToExile, game)) { Card targetedCardToExile = game.getCard(targetToExile.getFirstTarget()); @@ -196,11 +197,11 @@ public ValkiGodOfLiesReturnExiledCardEffect copy() { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - MageObject Valki = game.getObject(source.getSourceId()); + MageObject ValkiOnBattlefield = game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); if (controller != null - && Valki != null) { - // Valki, God of Lies has changed zone, so make sure to get the exile zone information from its previous state - UUID exileId = CardUtil.getExileZoneId(source.getSourceId().toString() + (Valki.getZoneChangeCounter(game) - 1), game); + && ValkiOnBattlefield != null) { + // Valki, God of Lies has changed zone, so make sure to get the exile zone via its last known battlefield state + UUID exileId = CardUtil.getExileZoneId(source.getSourceId().toString() + (ValkiOnBattlefield.getZoneChangeCounter(game)), game); ExileZone exile = game.getExile().getExileZone(exileId); Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (exile != null @@ -217,7 +218,7 @@ class ValkiGodOfLiesCopyExiledEffect extends OneShotEffect { public ValkiGodOfLiesCopyExiledEffect() { super(Outcome.Benefit); - this.staticText = "Choose a creature card exiled with Valki with converted mana cost X. Valki becomes a copy of that card."; + this.staticText = "Choose a creature card exiled with Valki with mana value X. Valki becomes a copy of that card."; } public ValkiGodOfLiesCopyExiledEffect(final ValkiGodOfLiesCopyExiledEffect effect) { @@ -237,10 +238,11 @@ public boolean apply(Game game, Ability source) { && Valki != null) { UUID exileId = CardUtil.getExileZoneId(source.getSourceId().toString() + (Valki.getZoneChangeCounter(game)), game); FilterCard filter = new FilterCard(); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, source.getManaCostsToPay().getX())); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, source.getManaCostsToPay().getX())); TargetCardInExile target = new TargetCardInExile(filter, exileId); Cards cards = game.getExile().getExileZone(exileId); - if (!cards.isEmpty() + if (cards != null + && !cards.isEmpty() && controller.choose(Outcome.Benefit, cards, target, game)) { Card chosenExiledCard = game.getCard(target.getFirstTarget()); if (chosenExiledCard != null) { @@ -259,7 +261,7 @@ class ExileTopCardEachPlayersLibrary extends OneShotEffect { public ExileTopCardEachPlayersLibrary() { super(Outcome.Benefit); - this.staticText = "Exile the top card of each player’s library"; + this.staticText = "Exile the top card of each player's library"; } public ExileTopCardEachPlayersLibrary(final ExileTopCardEachPlayersLibrary effect) { diff --git a/Mage.Sets/src/mage/cards/v/ValkyrieHarbinger.java b/Mage.Sets/src/mage/cards/v/ValkyrieHarbinger.java index 75b40b42efea..ab4cd6f78314 100644 --- a/Mage.Sets/src/mage/cards/v/ValkyrieHarbinger.java +++ b/Mage.Sets/src/mage/cards/v/ValkyrieHarbinger.java @@ -5,6 +5,7 @@ import mage.abilities.condition.Condition; import mage.abilities.condition.common.YouGainedLifeCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.dynamicvalue.common.ControllerGotLifeCount; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.LifelinkAbility; @@ -47,7 +48,7 @@ public ValkyrieHarbinger(UUID ownerId, CardSetInfo setInfo) { TargetController.ANY, false ), condition, "At the beginning of each end step, if you gained 4 or more life this turn, " + "create a 4/4 white Angel creature token with flying and vigilance." - ), new PlayerGainedLifeWatcher()); + ).addHint(ControllerGotLifeCount.getHint()), new PlayerGainedLifeWatcher()); } private ValkyrieHarbinger(final ValkyrieHarbinger card) { diff --git a/Mage.Sets/src/mage/cards/v/VanishingVerse.java b/Mage.Sets/src/mage/cards/v/VanishingVerse.java new file mode 100644 index 000000000000..194b7cf2b1d3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VanishingVerse.java @@ -0,0 +1,40 @@ +package mage.cards.v; + +import mage.abilities.effects.common.ExileTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.mageobject.MonocoloredPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VanishingVerse extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("monocolored permanent"); + + static { + filter.add(MonocoloredPredicate.instance); + } + + public VanishingVerse(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}{B}"); + + // Exile target monocolored permanent. + this.getSpellAbility().addEffect(new ExileTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + } + + private VanishingVerse(final VanishingVerse card) { + super(card); + } + + @Override + public VanishingVerse copy() { + return new VanishingVerse(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VaporSnare.java b/Mage.Sets/src/mage/cards/v/VaporSnare.java index 1f5aa7e7dac6..2fba8df65792 100644 --- a/Mage.Sets/src/mage/cards/v/VaporSnare.java +++ b/Mage.Sets/src/mage/cards/v/VaporSnare.java @@ -76,16 +76,17 @@ class VaporSnareEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { boolean targetChosen = false; - Player player = game.getPlayer(source.getControllerId()); + Player controller = game.getPlayer(source.getControllerId()); TargetPermanent target = new TargetPermanent(1, 1, filter, false); - if (player != null && target.canChoose(source.getSourceId(), player.getId(), game)) { - player.choose(Outcome.Sacrifice, target, source.getSourceId(), game); + if (controller != null + && target.canChoose(source.getSourceId(), controller.getId(), game)) { + controller.choose(Outcome.Sacrifice, target, source.getSourceId(), game); Permanent permanent = game.getPermanent(target.getFirstTarget()); if ( permanent != null ) { targetChosen = true; - permanent.moveToZone(Zone.HAND, source, game, false); + controller.moveCards(permanent, Zone.HAND, source, game); } } diff --git a/Mage.Sets/src/mage/cards/v/VaporousDjinn.java b/Mage.Sets/src/mage/cards/v/VaporousDjinn.java index 5ceb10e9d1b6..70df115b41fa 100644 --- a/Mage.Sets/src/mage/cards/v/VaporousDjinn.java +++ b/Mage.Sets/src/mage/cards/v/VaporousDjinn.java @@ -70,7 +70,7 @@ public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { Cost cost = new ManaCostsImpl("{U}{U}"); - String message = "Would you like to pay {U}{U} to prevent {this} from phasing out?"; + String message = "Pay {U}{U} to prevent this permanent from phasing out?"; if (!(controller.chooseUse(Outcome.Benefit, message, source, game) && cost.pay(source, game, source, controller.getId(), false, null))) { permanent.phaseOut(game); diff --git a/Mage.Sets/src/mage/cards/v/VarchildBetrayerOfKjeldor.java b/Mage.Sets/src/mage/cards/v/VarchildBetrayerOfKjeldor.java index 6a8276fa83a9..c0ade46159ad 100644 --- a/Mage.Sets/src/mage/cards/v/VarchildBetrayerOfKjeldor.java +++ b/Mage.Sets/src/mage/cards/v/VarchildBetrayerOfKjeldor.java @@ -64,7 +64,7 @@ public VarchildBetrayerOfKjeldor(UUID ownerId, CardSetInfo setInfo) { ); ability.addEffect(new CantAttackYouOrPlaneswalkerAllEffect( Duration.WhileOnBattlefield, filter1 - ).setText("and can't attack you or a planeswalker you control")); + ).setText("and can't attack you or planeswalkers you control")); this.addAbility(ability); // When Varchild leaves the battlefield, gain control of all Survivors. diff --git a/Mage.Sets/src/mage/cards/v/VarragothBloodskySire.java b/Mage.Sets/src/mage/cards/v/VarragothBloodskySire.java index 5eb5b1e72a22..587e80fa5eaf 100644 --- a/Mage.Sets/src/mage/cards/v/VarragothBloodskySire.java +++ b/Mage.Sets/src/mage/cards/v/VarragothBloodskySire.java @@ -57,7 +57,7 @@ class VarragothBloodskySireEffect extends SearchEffect { public VarragothBloodskySireEffect() { super(new TargetCardInLibrary(), Outcome.DrawCard); - this.staticText = "Target player searches their library for a card, then shuffles their library and puts that card on top of it"; + this.staticText = "Target player searches their library for a card, then shuffles and puts that card on top"; } private VarragothBloodskySireEffect(final VarragothBloodskySireEffect effect) { diff --git a/Mage.Sets/src/mage/cards/v/VeiledCrocodile.java b/Mage.Sets/src/mage/cards/v/VeiledCrocodile.java index 152e7b7f45e5..74ebd459c6ff 100644 --- a/Mage.Sets/src/mage/cards/v/VeiledCrocodile.java +++ b/Mage.Sets/src/mage/cards/v/VeiledCrocodile.java @@ -80,10 +80,10 @@ public boolean canTrigger(Game game) { } @Override - public void trigger(Game game, UUID controllerId) { + public void trigger(Game game, UUID controllerId, GameEvent triggeringEvent) { //20100716 - 603.8 game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.TRUE); - super.trigger(game, controllerId); + super.trigger(game, controllerId, triggeringEvent); } @Override diff --git a/Mage.Sets/src/mage/cards/v/VeiledSentry.java b/Mage.Sets/src/mage/cards/v/VeiledSentry.java index 7481a2f557a8..f8b69de88ed2 100644 --- a/Mage.Sets/src/mage/cards/v/VeiledSentry.java +++ b/Mage.Sets/src/mage/cards/v/VeiledSentry.java @@ -28,7 +28,7 @@ public VeiledSentry(UUID ownerId, CardSetInfo setInfo) { // When an opponent casts a spell, if Veiled Sentry is an enchantment, Veiled Sentry becomes an Illusion creature with power and toughness each equal to that spell's converted mana cost. TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(Zone.BATTLEFIELD, new VeiledSentryEffect(), new FilterSpell(), false, SetTargetPointer.SPELL); this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), - "Whenever an opponent casts a spell, if Veiled Sentry is an enchantment, Veil Sentry becomes an Illusion creature with power and toughness equal to that spell's converted mana cost.")); + "Whenever an opponent casts a spell, if Veiled Sentry is an enchantment, Veil Sentry becomes an Illusion creature with power and toughness equal to that spell's mana value.")); } @@ -46,7 +46,7 @@ class VeiledSentryEffect extends ContinuousEffectImpl { public VeiledSentryEffect() { super(Duration.Custom, Outcome.BecomeCreature); - staticText = "{this} becomes an Illusion creature with power and toughness equal to that spell's converted mana cost"; + staticText = "{this} becomes an Illusion creature with power and toughness equal to that spell's mana value"; } public VeiledSentryEffect(final VeiledSentryEffect effect) { @@ -63,7 +63,7 @@ public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) Permanent veiledSentry = game.getPermanent(source.getSourceId()); Spell spellCast = game.getSpell(targetPointer.getFirst(game, source)); if (spellCast != null) { - game.getState().setValue(source + "cmcSpell", spellCast.getConvertedManaCost()); + game.getState().setValue(source + "cmcSpell", spellCast.getManaValue()); } if (veiledSentry == null) { return false; diff --git a/Mage.Sets/src/mage/cards/v/VeilstoneAmulet.java b/Mage.Sets/src/mage/cards/v/VeilstoneAmulet.java index 83089e5d6e1c..d7a4fb0e353f 100644 --- a/Mage.Sets/src/mage/cards/v/VeilstoneAmulet.java +++ b/Mage.Sets/src/mage/cards/v/VeilstoneAmulet.java @@ -11,6 +11,7 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; @@ -25,7 +26,7 @@ public VeilstoneAmulet(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); // Whenever you cast a spell, creatures you control can't be the targets of spells or abilities your opponents control this turn. - this.addAbility(new SpellCastControllerTriggeredAbility(new VeilstoneAmuletEffect(), new FilterSpell("a spell"), false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new VeilstoneAmuletEffect(), StaticFilters.FILTER_SPELL_A, false)); } private VeilstoneAmulet(final VeilstoneAmulet card) { diff --git a/Mage.Sets/src/mage/cards/v/VeinwitchCoven.java b/Mage.Sets/src/mage/cards/v/VeinwitchCoven.java new file mode 100644 index 000000000000..c7df40fe6b7c --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VeinwitchCoven.java @@ -0,0 +1,51 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.GainLifeControllerTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VeinwitchCoven extends CardImpl { + + public VeinwitchCoven(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Menace + this.addAbility(new MenaceAbility()); + + // Whenever you gain life, you pay {B}. If you do, return target creature card from your graveyard to your hand. + Ability ability = new GainLifeControllerTriggeredAbility(new DoIfCostPaid( + new ReturnFromGraveyardToHandTargetEffect(), new ManaCostsImpl<>("{B}") + ), false); + ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); + this.addAbility(ability); + } + + private VeinwitchCoven(final VeinwitchCoven card) { + super(card); + } + + @Override + public VeinwitchCoven copy() { + return new VeinwitchCoven(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VelomachusLorehold.java b/Mage.Sets/src/mage/cards/v/VelomachusLorehold.java new file mode 100644 index 000000000000..1e3e42c0fad3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VelomachusLorehold.java @@ -0,0 +1,110 @@ +package mage.cards.v; + +import mage.ApprovingObject; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.*; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.common.FilterInstantOrSorceryCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInLibrary; + +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VelomachusLorehold extends CardImpl { + + public VelomachusLorehold(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{R}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ELDER); + this.subtype.add(SubType.DRAGON); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Whenever Velomachus Lorehold attacks, look at the top seven cards of your library. You may cast an instant or sorcery spell with mana value less than or equal to Velomachus Lorehold's power from among them without paying its mana cost. Put the rest on the bottom of your library in a random order. + this.addAbility(new AttacksTriggeredAbility(new VelomachusLoreholdEffect(), false)); + } + + private VelomachusLorehold(final VelomachusLorehold card) { + super(card); + } + + @Override + public VelomachusLorehold copy() { + return new VelomachusLorehold(this); + } +} + +class VelomachusLoreholdEffect extends OneShotEffect { + + VelomachusLoreholdEffect() { + super(Outcome.Benefit); + staticText = "look at the top seven cards of your library. You may cast an instant or sorcery spell " + + "with mana value less than or equal to {this}'s power from among them without paying its mana cost. " + + "Put the rest on the bottom of your library in a random order"; + } + + private VelomachusLoreholdEffect(final VelomachusLoreholdEffect effect) { + super(effect); + } + + @Override + public VelomachusLoreholdEffect copy() { + return new VelomachusLoreholdEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent permanent = source.getSourcePermanentOrLKI(game); + if (controller == null || permanent == null) { + return false; + } + Set cardsSet = controller.getLibrary().getTopCards(game, 7); + Cards cards = new CardsImpl(cardsSet); + FilterCard filter = new FilterInstantOrSorceryCard( + "instant or sorcery card with mana value " + permanent.getPower().getValue() + " or less" + ); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, permanent.getPower().getValue() + 1)); + TargetCard target = new TargetCardInLibrary(0, 1, filter); + controller.choose(Outcome.PlayForFree, cards, target, game); + Card card = controller.getLibrary().getCard(target.getFirstTarget(), game); + if (card == null) { + controller.putCardsOnBottomOfLibrary(cards, game, source, false); + return true; + } + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); + Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(card, game, true), + game, true, new ApprovingObject(source, game)); + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); + if (cardWasCast) { + cards.remove(card); + } + controller.putCardsOnBottomOfLibrary(cards, game, source, false); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/v/VenarianGlimmer.java b/Mage.Sets/src/mage/cards/v/VenarianGlimmer.java index 9d612593afb3..e8ae651d73f5 100644 --- a/Mage.Sets/src/mage/cards/v/VenarianGlimmer.java +++ b/Mage.Sets/src/mage/cards/v/VenarianGlimmer.java @@ -14,7 +14,7 @@ import mage.constants.TargetController; import mage.filter.FilterCard; import mage.filter.common.FilterNonlandCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; @@ -47,7 +47,7 @@ class VenarianGlimmerEffect extends OneShotEffect { public VenarianGlimmerEffect() { super(Outcome.Discard); - this.staticText = "Target player reveals their hand. You choose a nonland card with converted mana cost X or less from it. That player discards that card"; + this.staticText = "Target player reveals their hand. You choose a nonland card with mana value X or less from it. That player discards that card"; } public VenarianGlimmerEffect(final VenarianGlimmerEffect effect) { @@ -64,7 +64,7 @@ public boolean apply(Game game, Ability source) { Player player = game.getPlayer(targetPointer.getFirst(game, source)); if (player != null) { FilterCard filter = new FilterNonlandCard(); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, source.getManaCostsToPay().getX() + 1)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, source.getManaCostsToPay().getX() + 1)); Effect effect = new DiscardCardYouChooseTargetEffect(filter, TargetController.ANY); effect.setTargetPointer(targetPointer); effect.apply(game, source); diff --git a/Mage.Sets/src/mage/cards/v/VenerableWarsinger.java b/Mage.Sets/src/mage/cards/v/VenerableWarsinger.java new file mode 100644 index 000000000000..269eb2ba72a6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VenerableWarsinger.java @@ -0,0 +1,101 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.game.events.DamagedEvent; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VenerableWarsinger extends CardImpl { + + public VenerableWarsinger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{W}"); + + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Whenever Venerable Warsinger deals combat damage to a player, you may return target creature card with mana value X or less from your graveyard to the battlefield, where X is the amount of damage that Venerable Warsinger dealt to that player. + this.addAbility(new VenerableWarsingerTriggeredAbility()); + } + + private VenerableWarsinger(final VenerableWarsinger card) { + super(card); + } + + @Override + public VenerableWarsinger copy() { + return new VenerableWarsinger(this); + } +} + +class VenerableWarsingerTriggeredAbility extends TriggeredAbilityImpl { + + VenerableWarsingerTriggeredAbility() { + super(Zone.BATTLEFIELD, new ReturnFromGraveyardToBattlefieldTargetEffect(), true); + } + + private VenerableWarsingerTriggeredAbility(final VenerableWarsingerTriggeredAbility ability) { + super(ability); + } + + @Override + public VenerableWarsingerTriggeredAbility copy() { + return new VenerableWarsingerTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Player player = game.getPlayer(event.getPlayerId()); + if (player == null + || !event.getSourceId().equals(getSourceId()) + || !((DamagedEvent) event).isCombatDamage()) { + return false; + } + FilterCard filter = new FilterCreatureCard( + "creature card with mana value " + event.getAmount() + " less from your graveyard" + ); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, event.getAmount() + 1)); + this.getTargets().clear(); + this.addTarget(new TargetCardInYourGraveyard(filter)); + return true; + } + + @Override + public String getRule() { + return "Whenever {this} deals combat damage to a player, you may return target creature card " + + "with mana value X or less from your graveyard to the battlefield, " + + "where X is the amount of damage {this} dealt to that player."; + } +} diff --git a/Mage.Sets/src/mage/cards/v/VengefulPharaoh.java b/Mage.Sets/src/mage/cards/v/VengefulPharaoh.java index bc0b6465965c..1cd277059823 100644 --- a/Mage.Sets/src/mage/cards/v/VengefulPharaoh.java +++ b/Mage.Sets/src/mage/cards/v/VengefulPharaoh.java @@ -1,7 +1,6 @@ package mage.cards.v; -import java.util.UUID; import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; @@ -12,26 +11,26 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.game.turn.Step; import mage.players.Player; import mage.target.common.TargetAttackingCreature; +import java.util.UUID; + /** - * * @author North */ public final class VengefulPharaoh extends CardImpl { public VengefulPharaoh(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}{B}"); this.subtype.add(SubType.ZOMBIE); this.power = new MageInt(5); @@ -94,7 +93,8 @@ public boolean checkInterveningIfClause(Game game) { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLAYER || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + return event.getType() == GameEvent.EventType.DAMAGED_PLAYER + || event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override @@ -107,9 +107,9 @@ public boolean checkTrigger(GameEvent event, Game game) { return true; } } - if (event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER && ((DamagedEvent) event).isCombatDamage()) { + if (event.getType() == GameEvent.EventType.DAMAGED_PERMANENT && ((DamagedEvent) event).isCombatDamage()) { Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.isControlledBy(this.getControllerId())) { + if (permanent != null && permanent.isPlaneswalker() && permanent.isControlledBy(this.getControllerId())) { if (!game.getPhase().getStep().equals(stepTriggeredPlansewalker) || game.getTurnNum() != turnTriggeredPlaneswalker) { stepTriggeredPlansewalker = game.getPhase().getStep(); turnTriggeredPlaneswalker = game.getTurnNum(); diff --git a/Mage.Sets/src/mage/cards/v/VengefulRebirth.java b/Mage.Sets/src/mage/cards/v/VengefulRebirth.java index e45dc1c07dbd..730d161b3491 100644 --- a/Mage.Sets/src/mage/cards/v/VengefulRebirth.java +++ b/Mage.Sets/src/mage/cards/v/VengefulRebirth.java @@ -31,7 +31,7 @@ public VengefulRebirth(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new VengefulRebirthEffect()); // Exile Vengeful Rebirth. - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private VengefulRebirth(final VengefulRebirth card) { @@ -50,7 +50,7 @@ public VengefulRebirthEffect() { super(Outcome.DrawCard); staticText = "Return target card from your graveyard to your hand. " + "If you return a nonland card to your hand this way, " + - "{this} deals damage equal to that card's converted mana cost to any target"; + "{this} deals damage equal to that card's mana value to any target"; } public VengefulRebirthEffect(final VengefulRebirthEffect effect) { @@ -75,7 +75,7 @@ public boolean apply(Game game, Ability source) { if (card.isLand()) { return true; } - int damage = card.getConvertedManaCost(); + int damage = card.getManaValue(); Permanent permanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); if (permanent != null) { permanent.damage(damage, source.getSourceId(), source, game, false, true); diff --git a/Mage.Sets/src/mage/cards/v/VentifactBottle.java b/Mage.Sets/src/mage/cards/v/VentifactBottle.java index 6fa695a78b37..94c97a0cf282 100644 --- a/Mage.Sets/src/mage/cards/v/VentifactBottle.java +++ b/Mage.Sets/src/mage/cards/v/VentifactBottle.java @@ -46,7 +46,7 @@ public VentifactBottle(UUID ownerId, CardSetInfo setInfo) { new SourceHasCounterCondition(CounterType.CHARGE, 1, Integer.MAX_VALUE), "At the beginning of your precombat main phase, " + "if {this} has a charge counter on it, tap it and remove all charge counters from it. " - + "Add {C} for each charge counter removed this way")); + + "Add {C} for each charge counter removed this way.")); } private VentifactBottle(final VentifactBottle card) { diff --git a/Mage.Sets/src/mage/cards/v/VerazolTheSplitCurrent.java b/Mage.Sets/src/mage/cards/v/VerazolTheSplitCurrent.java index 287f964d6af6..9e0901867976 100644 --- a/Mage.Sets/src/mage/cards/v/VerazolTheSplitCurrent.java +++ b/Mage.Sets/src/mage/cards/v/VerazolTheSplitCurrent.java @@ -40,7 +40,7 @@ public VerazolTheSplitCurrent(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new SpellCastControllerTriggeredAbility( new DoIfCostPaid( new CopyTargetSpellEffect(false, true) - .setText("copy that spell. You may choose new targets for that copy"), + .setText("copy that spell. You may choose new targets for the copy"), new RemoveCountersSourceCost(CounterType.P1P1.createInstance(2)) ), StaticFilters.FILTER_SPELL_KICKED_A, false, true )); diff --git a/Mage.Sets/src/mage/cards/v/VerdantCatacombs.java b/Mage.Sets/src/mage/cards/v/VerdantCatacombs.java index 8adfc3c365a9..ef83373fa403 100644 --- a/Mage.Sets/src/mage/cards/v/VerdantCatacombs.java +++ b/Mage.Sets/src/mage/cards/v/VerdantCatacombs.java @@ -20,7 +20,7 @@ public final class VerdantCatacombs extends CardImpl { public VerdantCatacombs(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.LAND},null); this.frameColor = new ObjectColor("BG"); - this.addAbility(new FetchLandActivatedAbility(EnumSet.of(SubType.FOREST, SubType.SWAMP))); + this.addAbility(new FetchLandActivatedAbility(SubType.SWAMP, SubType.FOREST)); } private VerdantCatacombs(final VerdantCatacombs card) { diff --git a/Mage.Sets/src/mage/cards/v/VerdantMastery.java b/Mage.Sets/src/mage/cards/v/VerdantMastery.java new file mode 100644 index 000000000000..30e5a0c1d7f5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VerdantMastery.java @@ -0,0 +1,114 @@ +package mage.cards.v; + +import mage.abilities.Ability; +import mage.abilities.costs.AlternativeCostSourceAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VerdantMastery extends CardImpl { + + public VerdantMastery(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{G}"); + + // You may pay {3}{G} rather than pay this spell's mana cost. + Ability costAbility = new AlternativeCostSourceAbility(new ManaCostsImpl<>("{3}{G}")); + this.addAbility(costAbility); + + // Search your library for up to four basic land cards and reveal them. Put one of them onto the battlefield tapped under an opponent's control if the {3}{G} cost was paid. Put two of them onto the battlefield tapped under your control and the rest into your hand. Then shuffle. + this.getSpellAbility().addEffect(new VerdantMasteryEffect(costAbility.getOriginalId())); + } + + private VerdantMastery(final VerdantMastery card) { + super(card); + } + + @Override + public VerdantMastery copy() { + return new VerdantMastery(this); + } +} + +class VerdantMasteryEffect extends OneShotEffect { + + private final UUID alternativeCostOriginalID; + + VerdantMasteryEffect(UUID alternativeCostOriginalID) { + super(Outcome.Detriment); + staticText = "search your library for up to four basic land cards and reveal them. Put one of them " + + "onto the battlefield tapped under an opponent's control if the {3}{G} cost was paid. Put two of " + + "them onto the battlefield tapped under your control and the rest into your hand. Then shuffle"; + this.alternativeCostOriginalID = alternativeCostOriginalID; + } + + private VerdantMasteryEffect(VerdantMasteryEffect effect) { + super(effect); + this.alternativeCostOriginalID = effect.alternativeCostOriginalID; + } + + @Override + public VerdantMasteryEffect copy() { + return new VerdantMasteryEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + TargetCardInLibrary target = new TargetCardInLibrary(4, StaticFilters.FILTER_CARD_BASIC_LAND); + player.searchLibrary(target, source, game); + Cards cards = new CardsImpl(target.getTargets()); + player.revealCards(source, cards, game); + if (cards.isEmpty()) { + player.shuffleLibrary(source, game); + return true; + } + if (AlternativeCostSourceAbility.getActivatedStatus( + game, source, this.alternativeCostOriginalID, false + )) { + TargetOpponent targetOpponent = new TargetOpponent(true); + player.chooseTarget(Outcome.DrawCard, targetOpponent, source, game); + Player opponent = game.getPlayer(targetOpponent.getFirstTarget()); + if (opponent != null) { + target = new TargetCardInLibrary(1, StaticFilters.FILTER_CARD_BASIC_LAND); + target.withChooseHint("to give to " + opponent.getName()); + player.choose(outcome, cards, target, game); + Card card = game.getCard(target.getFirstTarget()); + opponent.moveCards( + card, Zone.BATTLEFIELD, source, game, true, + false, false, null + ); + } + } + cards.removeIf(uuid -> game.getState().getZone(uuid) != Zone.LIBRARY); + if (cards.isEmpty()) { + player.shuffleLibrary(source, game); + return true; + } + target = new TargetCardInLibrary(0, 2, StaticFilters.FILTER_CARD_BASIC_LAND); + player.choose(outcome, cards, target, game); + player.moveCards( + new CardsImpl(target.getTargets()).getCards(game), Zone.BATTLEFIELD, source, + game, true, false, false, null + ); + cards.removeIf(uuid -> game.getState().getZone(uuid) != Zone.LIBRARY); + player.moveCards(cards, Zone.HAND, source, game); + player.shuffleLibrary(source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/v/VerdantSuccession.java b/Mage.Sets/src/mage/cards/v/VerdantSuccession.java index d28abc447718..a5094c23c088 100644 --- a/Mage.Sets/src/mage/cards/v/VerdantSuccession.java +++ b/Mage.Sets/src/mage/cards/v/VerdantSuccession.java @@ -80,8 +80,8 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD - && ((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD) { + ZoneChangeEvent zoneChangeEvent = (ZoneChangeEvent) event; + if (zoneChangeEvent.isDiesEvent()) { Permanent permanent = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); MageObject mageObject = game.getObject(sourceId); if (permanent != null && mageObject != null @@ -95,7 +95,7 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { - return "Whenever a green nontoken creature dies, that creature's controller may search their library for a card with the same name as that creature and put it onto the battlefield. If that player does, they shuffle their library."; + return "Whenever a green nontoken creature dies, that creature's controller may search their library for a card with the same name as that creature and put it onto the battlefield. If that player does, they shuffle."; } } diff --git a/Mage.Sets/src/mage/cards/v/VergeRangers.java b/Mage.Sets/src/mage/cards/v/VergeRangers.java index f4b9228294b6..37743c747d0e 100644 --- a/Mage.Sets/src/mage/cards/v/VergeRangers.java +++ b/Mage.Sets/src/mage/cards/v/VergeRangers.java @@ -11,21 +11,22 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; +import mage.filter.FilterCard; import mage.filter.StaticFilters; +import mage.filter.common.FilterLandCard; import mage.game.Game; import java.util.Comparator; import java.util.UUID; /** - * * @author htrajan */ public final class VergeRangers extends CardImpl { public VergeRangers(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); - + this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SCOUT); this.power = new MageInt(3); @@ -53,9 +54,11 @@ public VergeRangers copy() { class VergeRangersEffect extends PlayTheTopCardEffect { + private static final FilterCard filter = new FilterLandCard("play lands"); + public VergeRangersEffect() { - super(StaticFilters.FILTER_CARD_LAND); - staticText = "As long as an opponent controls more lands than you, you may play lands from the top of your library."; + super(filter, false); + staticText = "As long as an opponent controls more lands than you, you may play lands from the top of your library"; } public VergeRangersEffect(final VergeRangersEffect effect) { @@ -68,15 +71,16 @@ public VergeRangersEffect copy() { } @Override - public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) { - if (super.applies(objectId, affectedAbility, source, game, playerId)) { - int myLandCount = game.getBattlefield().countAll(StaticFilters.FILTER_LAND, playerId, game); - int maxOpponentLandCount = game.getOpponents(playerId).stream() - .map(opponentId -> game.getBattlefield().countAll(StaticFilters.FILTER_LAND, opponentId, game)) - .max(Comparator.naturalOrder()) - .orElse(0); - return maxOpponentLandCount > myLandCount; + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + if (!super.applies(objectId, source, affectedControllerId, game)) { + return false; } - return false; + + int myLandCount = game.getBattlefield().countAll(StaticFilters.FILTER_LAND, source.getControllerId(), game); + int maxOpponentLandCount = game.getOpponents(source.getControllerId()).stream() + .map(opponentId -> game.getBattlefield().countAll(StaticFilters.FILTER_LAND, opponentId, game)) + .max(Comparator.naturalOrder()) + .orElse(0); + return maxOpponentLandCount > myLandCount; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/v/VeryCrypticCommandD.java b/Mage.Sets/src/mage/cards/v/VeryCrypticCommandD.java index 616bd9c44ab2..898f3d128121 100644 --- a/Mage.Sets/src/mage/cards/v/VeryCrypticCommandD.java +++ b/Mage.Sets/src/mage/cards/v/VeryCrypticCommandD.java @@ -21,7 +21,7 @@ import mage.filter.FilterStackObject; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.NumberOfTargetsPredicate; +import mage.filter.predicate.other.NumberOfTargetsPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/v/VesselOfMalignity.java b/Mage.Sets/src/mage/cards/v/VesselOfMalignity.java index 665f6493dfca..c6a0cc4cada0 100644 --- a/Mage.Sets/src/mage/cards/v/VesselOfMalignity.java +++ b/Mage.Sets/src/mage/cards/v/VesselOfMalignity.java @@ -1,7 +1,5 @@ - package mage.cards.v; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.ActivateAsSorceryActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; @@ -11,22 +9,26 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class VesselOfMalignity extends CardImpl { public VesselOfMalignity(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}"); // {1}{B}, Sacrifice Vessel of Malignity: Target opponent exiles two cards from their hand. Activate this ability only any time you could cast a sorcery. - Ability ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, - new ExileFromZoneTargetEffect(Zone.HAND, null, "", new FilterCard("cards"), 2), - new ManaCostsImpl("{1}{B}")); + Ability ability = new ActivateAsSorceryActivatedAbility( + Zone.BATTLEFIELD, + new ExileFromZoneTargetEffect( + Zone.HAND, StaticFilters.FILTER_CARD_CARDS, 2, false + ), new ManaCostsImpl("{1}{B}") + ); ability.addCost(new SacrificeSourceCost()); ability.addTarget(new TargetOpponent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/v/VesuvanShapeshifter.java b/Mage.Sets/src/mage/cards/v/VesuvanShapeshifter.java index 165d8404aa87..69e6e3c51935 100644 --- a/Mage.Sets/src/mage/cards/v/VesuvanShapeshifter.java +++ b/Mage.Sets/src/mage/cards/v/VesuvanShapeshifter.java @@ -3,8 +3,8 @@ import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.AsTurnedFaceUpEffect; @@ -18,7 +18,7 @@ import mage.constants.*; import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -39,20 +39,22 @@ public VesuvanShapeshifter(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(0); // As Vesuvan Shapeshifter turned face up, may choose another creature. If you do, until Vesuvan Shapeshifter is turned face down, it becomes a copy of that creature - Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new AsTurnedFaceUpEffect(new VesuvanShapeshifterEffect(), false)); + Ability ability = new SimpleStaticAbility(new AsTurnedFaceUpEffect( + new VesuvanShapeshifterEffect(), false + ).setText("As {this} enters the battlefield or is turned face up, " + + "you may choose another creature on the battlefield. If you do, " + + "until {this} is turned face down, it becomes a copy of that creature, " + + "except it has \"At the beginning of your upkeep, you may turn this creature face down.\"") + ); ability.setWorksFaceDown(true); this.addAbility(ability); // As Vesuvan Shapeshifter etbs, you may choose another creature. If you do, until Vesuvan Shapeshifter is turned face down, it becomes a copy of that creature - Effect effect = new CopyPermanentEffect(StaticFilters.FILTER_PERMANENT_CREATURE, new VesuvanShapeShifterFaceUpCopyApplier()); - effect.setText("as a copy of any creature on the battlefield until {this} is turned faced down"); - ability = new EntersBattlefieldAbility(effect, true); + ability = new AsEntersBattlefieldAbility(new CopyPermanentEffect( + StaticFilters.FILTER_PERMANENT_CREATURE, new VesuvanShapeShifterFaceUpCopyApplier() + )); ability.setWorksFaceDown(false); - this.addAbility(ability); - - // and has "At the beginning of your upkeep, you may turn this creature face down". - effect = new VesuvanShapeshifterFaceDownEffect(); - ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, effect, TargetController.YOU, true); + ability.setRuleVisible(false); this.addAbility(ability); // Morph {1}{U} @@ -82,12 +84,12 @@ public boolean apply(Game game, MageObject blueprint, Ability source, UUID copyT class VesuvanShapeshifterEffect extends OneShotEffect { - public VesuvanShapeshifterEffect() { + VesuvanShapeshifterEffect() { super(Outcome.Copy); staticText = "have {this} become a copy of a creature, except it has this ability"; } - public VesuvanShapeshifterEffect(final VesuvanShapeshifterEffect effect) { + private VesuvanShapeshifterEffect(final VesuvanShapeshifterEffect effect) { super(effect); } @@ -123,12 +125,12 @@ public boolean apply(Game game, Ability source) { class VesuvanShapeshifterFaceDownEffect extends OneShotEffect { - public VesuvanShapeshifterFaceDownEffect() { + VesuvanShapeshifterFaceDownEffect() { super(Outcome.Copy); - staticText = "have {this} become a morphed, faced down creature"; + staticText = "turn this creature face down"; } - public VesuvanShapeshifterFaceDownEffect(final VesuvanShapeshifterFaceDownEffect effect) { + private VesuvanShapeshifterFaceDownEffect(final VesuvanShapeshifterFaceDownEffect effect) { super(effect); } @@ -142,25 +144,25 @@ public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanent(source.getSourceId()); - if (controller != null && permanent != null) { - permanent.removeAllAbilities(source.getSourceId(), game); - - // Set any previous copy effects to 'discarded' - for (Effect effect : game.getState().getContinuousEffects().getLayeredEffects(game)) { - if (effect instanceof CopyEffect) { - CopyEffect copyEffect = (CopyEffect) effect; - if (copyEffect.getSourceId().equals(permanent.getId())) { - copyEffect.discard(); - } + if (controller == null || permanent == null) { + return false; + } + permanent.removeAllAbilities(source.getSourceId(), game); + + // Set any previous copy effects to 'discarded' + for (Effect effect : game.getState().getContinuousEffects().getLayeredEffects(game)) { + if (effect instanceof CopyEffect) { + CopyEffect copyEffect = (CopyEffect) effect; + if (copyEffect.getSourceId().equals(permanent.getId())) { + copyEffect.discard(); } } - - permanent.turnFaceDown(source, game, source.getControllerId()); - permanent.setManifested(false); - permanent.setMorphed(true); - return permanent.isFaceDown(game); } - return false; + permanent.turnFaceDown(source, game, source.getControllerId()); + permanent.setManifested(false); + permanent.setMorphed(true); + return permanent.isFaceDown(game); + } } diff --git a/Mage.Sets/src/mage/cards/v/VeteranExplorer.java b/Mage.Sets/src/mage/cards/v/VeteranExplorer.java index 4e3087d24663..0f6eca57efe9 100644 --- a/Mage.Sets/src/mage/cards/v/VeteranExplorer.java +++ b/Mage.Sets/src/mage/cards/v/VeteranExplorer.java @@ -55,7 +55,8 @@ class VeteranExplorerEffect extends OneShotEffect { public VeteranExplorerEffect() { super(Outcome.Detriment); - this.staticText = "each player may search their library for up to two basic land cards and put them onto the battlefield. Then each player who searched their library this way shuffles it"; + this.staticText = "each player may search their library for up to two basic land cards, " + + "put them onto the battlefield, then shuffle"; } public VeteranExplorerEffect(final VeteranExplorerEffect effect) { diff --git a/Mage.Sets/src/mage/cards/v/VeteranWarleader.java b/Mage.Sets/src/mage/cards/v/VeteranWarleader.java index e9e1f5415fea..778c9361504f 100644 --- a/Mage.Sets/src/mage/cards/v/VeteranWarleader.java +++ b/Mage.Sets/src/mage/cards/v/VeteranWarleader.java @@ -22,7 +22,7 @@ import mage.constants.*; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/v/VeyranVoiceOfDuality.java b/Mage.Sets/src/mage/cards/v/VeyranVoiceOfDuality.java new file mode 100644 index 000000000000..e2ff76afff8d --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VeyranVoiceOfDuality.java @@ -0,0 +1,100 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.NumberOfTriggersEvent; +import mage.game.stack.Spell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VeyranVoiceOfDuality extends CardImpl { + + public VeyranVoiceOfDuality(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.EFREET); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, Veyran, Voice of Duality gets +1/+1 until end of turn. + this.addAbility(new MagecraftAbility(new BoostSourceEffect(1, 1, Duration.EndOfTurn))); + + // If you casting or copying an instant or sorcery spell causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time. + this.addAbility(new SimpleStaticAbility(new VeyranVoiceOfDualityEffect())); + } + + private VeyranVoiceOfDuality(final VeyranVoiceOfDuality card) { + super(card); + } + + @Override + public VeyranVoiceOfDuality copy() { + return new VeyranVoiceOfDuality(this); + } +} + +class VeyranVoiceOfDualityEffect extends ReplacementEffectImpl { + + VeyranVoiceOfDualityEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "if you casting or copying an instant or sorcery spell causes a triggered ability " + + "of a permanent you control to trigger, that ability triggers an additional time"; + } + + private VeyranVoiceOfDualityEffect(final VeyranVoiceOfDualityEffect effect) { + super(effect); + } + + @Override + public VeyranVoiceOfDualityEffect copy() { + return new VeyranVoiceOfDualityEffect(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) { + NumberOfTriggersEvent numberOfTriggersEvent = (NumberOfTriggersEvent) event; + if (!source.isControlledBy(event.getPlayerId())) { + return false; + } + GameEvent sourceEvent = numberOfTriggersEvent.getSourceEvent(); + if (sourceEvent == null) { + return false; + } + if (sourceEvent.getType() != GameEvent.EventType.COPIED_STACKOBJECT + && sourceEvent.getType() != GameEvent.EventType.SPELL_CAST) { + return false; + } + // Only for entering artifacts or creatures + Spell spell = game.getSpell(sourceEvent.getTargetId()); + if (spell == null || !spell.isInstantOrSorcery()) { + return false; + } + // Only for triggers of permanents + return game.getPermanent(numberOfTriggersEvent.getSourceId()) != null; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + event.setAmount(event.getAmount() + 1); + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/v/VialSmasherTheFierce.java b/Mage.Sets/src/mage/cards/v/VialSmasherTheFierce.java index 8767426e2bb1..4c842b52364c 100644 --- a/Mage.Sets/src/mage/cards/v/VialSmasherTheFierce.java +++ b/Mage.Sets/src/mage/cards/v/VialSmasherTheFierce.java @@ -84,7 +84,7 @@ public boolean checkTrigger(GameEvent event, Game game) { Spell spell = game.getStack().getSpell(event.getTargetId()); if (spell != null) { for (Effect effect : getEffects()) { - effect.setValue("VialSmasherTheFierceCMC", spell.getConvertedManaCost()); + effect.setValue("VialSmasherTheFierceCMC", spell.getManaValue()); } return true; } @@ -97,7 +97,7 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { return "Whenever you cast your first spell each turn, choose an opponent at random. " - + "{this} deals damage equal to that spell's converted mana cost to that player or a planeswalker that player controls."; + + "{this} deals damage equal to that spell's mana value to that player or a planeswalker that player controls."; } } @@ -105,7 +105,7 @@ class VialSmasherTheFierceEffect extends OneShotEffect { public VialSmasherTheFierceEffect() { super(Outcome.Damage); - this.staticText = "{this} choose an opponent at random. {this} deals damage equal to that spell's converted mana cost to that player or a planeswalker that player controls"; + this.staticText = "{this} choose an opponent at random. {this} deals damage equal to that spell's mana value to that player or a planeswalker that player controls"; } public VialSmasherTheFierceEffect(final VialSmasherTheFierceEffect effect) { diff --git a/Mage.Sets/src/mage/cards/v/ViashinoHeretic.java b/Mage.Sets/src/mage/cards/v/ViashinoHeretic.java index 65f534b6d86d..4c6d85ead97c 100644 --- a/Mage.Sets/src/mage/cards/v/ViashinoHeretic.java +++ b/Mage.Sets/src/mage/cards/v/ViashinoHeretic.java @@ -70,7 +70,7 @@ public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getFirstTarget()); if (permanent != null) { - int couvertedManaCost = permanent.getConvertedManaCost(); + int couvertedManaCost = permanent.getManaValue(); Player player = game.getPlayer(permanent.getControllerId()); permanent.destroy(source, game, false); if (player != null) { @@ -83,6 +83,6 @@ public boolean apply(Game game, Ability source) { @Override public String getText(Mode mode) { - return "Destroy target artifact. Viashino Heretic deals damage to that artifact's controller equal to the artifact's converted mana cost"; + return "Destroy target artifact. Viashino Heretic deals damage to that artifact's controller equal to the artifact's mana value"; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/v/VictorysEnvoy.java b/Mage.Sets/src/mage/cards/v/VictorysEnvoy.java index e7ef5946adb5..8f205787a917 100644 --- a/Mage.Sets/src/mage/cards/v/VictorysEnvoy.java +++ b/Mage.Sets/src/mage/cards/v/VictorysEnvoy.java @@ -11,7 +11,7 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/v/VigilForTheLost.java b/Mage.Sets/src/mage/cards/v/VigilForTheLost.java index b3875175f7f4..2a92c5cffe26 100644 --- a/Mage.Sets/src/mage/cards/v/VigilForTheLost.java +++ b/Mage.Sets/src/mage/cards/v/VigilForTheLost.java @@ -63,8 +63,8 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD && - ((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD) { + ZoneChangeEvent zoneChangeEvent = (ZoneChangeEvent) event; + if (zoneChangeEvent.isDiesEvent()) { Permanent p = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); if (p.isControlledBy(this.getControllerId()) && p.isCreature()) { return true; diff --git a/Mage.Sets/src/mage/cards/v/VigilantMartyr.java b/Mage.Sets/src/mage/cards/v/VigilantMartyr.java index 4865a6eb2522..bad0fef4536d 100644 --- a/Mage.Sets/src/mage/cards/v/VigilantMartyr.java +++ b/Mage.Sets/src/mage/cards/v/VigilantMartyr.java @@ -16,7 +16,7 @@ import mage.constants.Zone; import mage.filter.FilterSpell; import mage.filter.StaticFilters; -import mage.filter.predicate.other.TargetsPermanentPredicate; +import mage.filter.predicate.mageobject.TargetsPermanentPredicate; import mage.target.TargetSpell; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Sets/src/mage/cards/v/Vigor.java b/Mage.Sets/src/mage/cards/v/Vigor.java index 9772a51914e0..a010a90d1ef6 100644 --- a/Mage.Sets/src/mage/cards/v/Vigor.java +++ b/Mage.Sets/src/mage/cards/v/Vigor.java @@ -82,12 +82,15 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGE_PERMANENT; } @Override public boolean applies(GameEvent event, Ability source, Game game) { - return event.getPlayerId().equals(source.getControllerId()) + Permanent permanent = game.getPermanent(event.getTargetId()); + return permanent != null + && permanent.isCreature() + && permanent.isControlledBy(source.getControllerId()) && !event.getTargetId().equals(source.getSourceId()); } diff --git a/Mage.Sets/src/mage/cards/v/VigorMortis.java b/Mage.Sets/src/mage/cards/v/VigorMortis.java index 72da7a8f6154..5be2dccf0c12 100644 --- a/Mage.Sets/src/mage/cards/v/VigorMortis.java +++ b/Mage.Sets/src/mage/cards/v/VigorMortis.java @@ -33,7 +33,7 @@ public VigorMortis(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new VigorMortisReplacementEffect()); // has to be added before the moving effect this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); this.getSpellAbility().addEffect(new InfoEffect("If {G} was spent to cast this spell, that creature enters the battlefield with an additional +1/+1 counter on it")); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE)); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); } diff --git a/Mage.Sets/src/mage/cards/v/VigorousCharge.java b/Mage.Sets/src/mage/cards/v/VigorousCharge.java index cc39a0d8ee91..c9aa8c211fa0 100644 --- a/Mage.Sets/src/mage/cards/v/VigorousCharge.java +++ b/Mage.Sets/src/mage/cards/v/VigorousCharge.java @@ -70,9 +70,8 @@ public VigorousChargeTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE - || event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT + || event.getType() == GameEvent.EventType.DAMAGED_PLAYER; } @Override diff --git a/Mage.Sets/src/mage/cards/v/VildinPackAlpha.java b/Mage.Sets/src/mage/cards/v/VildinPackAlpha.java index 81e0868c527e..a567911d8a42 100644 --- a/Mage.Sets/src/mage/cards/v/VildinPackAlpha.java +++ b/Mage.Sets/src/mage/cards/v/VildinPackAlpha.java @@ -1,17 +1,10 @@ - package mage.cards.v; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.TransformSourceEffect; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -20,14 +13,15 @@ import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class VildinPackAlpha extends CardImpl { public VildinPackAlpha(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.WEREWOLF); this.power = new MageInt(4); this.toughness = new MageInt(3); @@ -40,9 +34,7 @@ public VildinPackAlpha(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new VildinPackAlphaEffect(), new FilterCreaturePermanent(SubType.WEREWOLF, "a Werewolf"), true, SetTargetPointer.PERMANENT, null)); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Vildin-Pack Alpha. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); - + this.addAbility(new WerewolfBackTriggeredAbility()); } private VildinPackAlpha(final VildinPackAlpha card) { diff --git a/Mage.Sets/src/mage/cards/v/VillageIronsmith.java b/Mage.Sets/src/mage/cards/v/VillageIronsmith.java index 5dcc3d75566c..7bbf0b9366fa 100644 --- a/Mage.Sets/src/mage/cards/v/VillageIronsmith.java +++ b/Mage.Sets/src/mage/cards/v/VillageIronsmith.java @@ -1,21 +1,15 @@ - package mage.cards.v; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.keyword.FirstStrikeAbility; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** * @author nantuko @@ -37,8 +31,7 @@ public VillageIronsmith(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each upkeep, if no spells were cast last turn, transform Village Ironsmith. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private VillageIronsmith(final VillageIronsmith card) { diff --git a/Mage.Sets/src/mage/cards/v/VillageMessenger.java b/Mage.Sets/src/mage/cards/v/VillageMessenger.java index 9a6ebdf0db0a..72d6bc103108 100644 --- a/Mage.Sets/src/mage/cards/v/VillageMessenger.java +++ b/Mage.Sets/src/mage/cards/v/VillageMessenger.java @@ -1,21 +1,15 @@ - package mage.cards.v; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** * @author fireshoes @@ -37,8 +31,7 @@ public VillageMessenger(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each upkeep, if no spells were cast last turn, transform Village Messenger. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private VillageMessenger(final VillageMessenger card) { diff --git a/Mage.Sets/src/mage/cards/v/VillagersOfEstwald.java b/Mage.Sets/src/mage/cards/v/VillagersOfEstwald.java index 137b37721400..ed3cdb7f6aa4 100644 --- a/Mage.Sets/src/mage/cards/v/VillagersOfEstwald.java +++ b/Mage.Sets/src/mage/cards/v/VillagersOfEstwald.java @@ -1,20 +1,14 @@ - package mage.cards.v; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; + +import java.util.UUID; /** * @author nantuko @@ -34,8 +28,7 @@ public VillagersOfEstwald(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each upkeep, if no spells were cast last turn, transform Villagers of Estwald. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private VillagersOfEstwald(final VillagersOfEstwald card) { diff --git a/Mage.Sets/src/mage/cards/v/VillainousWealth.java b/Mage.Sets/src/mage/cards/v/VillainousWealth.java index 4b8443432deb..97f078d8586d 100644 --- a/Mage.Sets/src/mage/cards/v/VillainousWealth.java +++ b/Mage.Sets/src/mage/cards/v/VillainousWealth.java @@ -10,7 +10,7 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.common.FilterNonlandCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInExile; @@ -50,7 +50,7 @@ class VillainousWealthEffect extends OneShotEffect { public VillainousWealthEffect() { super(Outcome.PlayForFree); this.staticText = "Target opponent exiles the top X cards of their library. " - + "You may cast any number of spells with converted mana cost X " + + "You may cast any number of spells with mana value X " + "or less from among them without paying their mana costs"; } @@ -70,7 +70,7 @@ public boolean apply(Game game, Ability source) { if (controller != null && mageObject != null) { Player player = game.getPlayer(targetPointer.getFirst(game, source)); FilterCard filter = new FilterNonlandCard(); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, source.getManaCostsToPay().getX() + 1)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, source.getManaCostsToPay().getX() + 1)); UUID exileId = CardUtil.getCardExileZoneId(game, source); if (player != null) { Cards cardsToExile = new CardsImpl(); diff --git a/Mage.Sets/src/mage/cards/v/VindictiveLich.java b/Mage.Sets/src/mage/cards/v/VindictiveLich.java index bca605a19653..23608e375043 100644 --- a/Mage.Sets/src/mage/cards/v/VindictiveLich.java +++ b/Mage.Sets/src/mage/cards/v/VindictiveLich.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.filter.FilterOpponent; import mage.filter.StaticFilters; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.target.Target; import mage.target.common.TargetOpponent; diff --git a/Mage.Sets/src/mage/cards/v/VindictiveVampire.java b/Mage.Sets/src/mage/cards/v/VindictiveVampire.java index 617078741a3e..858676e73c71 100644 --- a/Mage.Sets/src/mage/cards/v/VindictiveVampire.java +++ b/Mage.Sets/src/mage/cards/v/VindictiveVampire.java @@ -10,7 +10,7 @@ import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; import mage.MageObject; import mage.abilities.TriggeredAbilityImpl; diff --git a/Mage.Sets/src/mage/cards/v/VineglimmerSnarl.java b/Mage.Sets/src/mage/cards/v/VineglimmerSnarl.java new file mode 100644 index 000000000000..a23a4411ed73 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VineglimmerSnarl.java @@ -0,0 +1,56 @@ +package mage.cards.v; + +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.costs.common.RevealTargetFromHandCost; +import mage.abilities.effects.common.TapSourceUnlessPaysEffect; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.target.common.TargetCardInHand; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VineglimmerSnarl extends CardImpl { + + private static final FilterCard filter = new FilterCard("a Forest or Island card from your hand"); + + static { + filter.add(Predicates.or( + SubType.FOREST.getPredicate(), + SubType.ISLAND.getPredicate() + )); + } + + public VineglimmerSnarl(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // As Vineglimmer Snarl enters the battlefield, you may reveal a Forest or Island card from your hand. If you don't, Vineglimmer Snarl enters the battlefield tapped. + this.addAbility(new AsEntersBattlefieldAbility( + new TapSourceUnlessPaysEffect( + new RevealTargetFromHandCost(new TargetCardInHand(filter)) + ), "you may reveal a Forest or Island card from your hand. " + + "If you don't, {this} enters the battlefield tapped" + )); + + // {T}: Add {G} or {U}. + this.addAbility(new GreenManaAbility()); + this.addAbility(new BlueManaAbility()); + } + + private VineglimmerSnarl(final VineglimmerSnarl card) { + super(card); + } + + @Override + public VineglimmerSnarl copy() { + return new VineglimmerSnarl(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VirtussManeuver.java b/Mage.Sets/src/mage/cards/v/VirtussManeuver.java index 11e29ff48728..8a31c0587e91 100644 --- a/Mage.Sets/src/mage/cards/v/VirtussManeuver.java +++ b/Mage.Sets/src/mage/cards/v/VirtussManeuver.java @@ -17,7 +17,7 @@ import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/v/VishKalBloodArbiter.java b/Mage.Sets/src/mage/cards/v/VishKalBloodArbiter.java index 989940dbddfb..6555fe255642 100644 --- a/Mage.Sets/src/mage/cards/v/VishKalBloodArbiter.java +++ b/Mage.Sets/src/mage/cards/v/VishKalBloodArbiter.java @@ -21,7 +21,7 @@ import mage.constants.SuperType; import mage.counters.CounterType; import mage.game.Game; -import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -51,7 +51,8 @@ public VishKalBloodArbiter(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new SimpleActivatedAbility( new AddCountersSourceEffect( CounterType.P1P1.createInstance(), SacrificeCostCreaturesPower.instance, true - ), new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT)) + ).setText("put X +1/+1 counters on {this}, where X is the sacrificed creature's power"), + new SacrificeTargetCost(new TargetControlledPermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT)) )); // Remove all +1/+1 counters from Vish Kal: Target creature gets -1/-1 until end of turn for each +1/+1 counter removed this way. @@ -59,7 +60,7 @@ public VishKalBloodArbiter(UUID ownerId, CardSetInfo setInfo) { VishKalBloodArbiterDynamicValue.instance, VishKalBloodArbiterDynamicValue.instance, Duration.EndOfTurn, true - ), new RemoveAllCountersSourceCost(CounterType.P1P1)); + ).setText("target creature gets -1/-1 until end of turn for each +1/+1 counter removed this way"), new RemoveAllCountersSourceCost(CounterType.P1P1)); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/v/VisionCharm.java b/Mage.Sets/src/mage/cards/v/VisionCharm.java index 772d63c5157c..fd572acead13 100644 --- a/Mage.Sets/src/mage/cards/v/VisionCharm.java +++ b/Mage.Sets/src/mage/cards/v/VisionCharm.java @@ -1,4 +1,3 @@ - package mage.cards.v; import mage.MageObjectReference; @@ -62,7 +61,7 @@ class VisionCharmEffect extends ContinuousEffectImpl { private SubType targetBasicLandType; VisionCharmEffect() { - super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Neutral); + super(Duration.EndOfTurn, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Neutral); staticText = "Choose a land type and a basic land type. Each land of the first chosen type becomes the second chosen type until end of turn."; } diff --git a/Mage.Sets/src/mage/cards/v/VividRevival.java b/Mage.Sets/src/mage/cards/v/VividRevival.java index fddf3bead425..af0c0aad952a 100644 --- a/Mage.Sets/src/mage/cards/v/VividRevival.java +++ b/Mage.Sets/src/mage/cards/v/VividRevival.java @@ -28,7 +28,7 @@ public VividRevival(UUID ownerId, CardSetInfo setInfo) { // Return up to three target multicolor cards from your graveyard to your hand. Exile Vivid Revival. this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 3, filter)); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private VividRevival(final VividRevival card) { diff --git a/Mage.Sets/src/mage/cards/v/VivienMonstersAdvocate.java b/Mage.Sets/src/mage/cards/v/VivienMonstersAdvocate.java index 959f89dcee37..bfc0d69358d3 100644 --- a/Mage.Sets/src/mage/cards/v/VivienMonstersAdvocate.java +++ b/Mage.Sets/src/mage/cards/v/VivienMonstersAdvocate.java @@ -18,7 +18,7 @@ import mage.counters.CounterType; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -27,6 +27,7 @@ import mage.game.stack.Spell; import mage.players.Player; import mage.target.common.TargetCardInLibrary; + import java.util.Arrays; import java.util.Locale; import java.util.Set; @@ -51,7 +52,7 @@ public VivienMonstersAdvocate(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect())); // You may cast creature spells from the top of your library. - this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter))); + this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false))); // +1: Create a 3/3 green Beast creature token. Put your choice of a vigilance // counter, a reach counter, or a trample counter on it. @@ -141,9 +142,9 @@ public boolean checkTrigger(GameEvent event, Game game) { Spell spell = game.getSpell(event.getTargetId()); if (spell != null && spell.isCreature()) { - int cmc = spell.getConvertedManaCost(); - FilterCard filter = new FilterCreatureCard("creature card with converted mana cost less than " + cmc); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, cmc)); + int cmc = spell.getManaValue(); + FilterCard filter = new FilterCreatureCard("creature card with mana value less than " + cmc); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, cmc)); this.getEffects().clear(); this.getEffects().add(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter))); return true; @@ -159,7 +160,7 @@ public DelayedTriggeredAbility copy() { @Override public String getRule() { return "When you cast your next creature spell this turn, " - + "search your library for a creature card with lesser converted mana cost, " - + "put it onto the battlefield, then shuffle your library."; + + "search your library for a creature card with lesser mana value, " + + "put it onto the battlefield, then shuffle."; } } diff --git a/Mage.Sets/src/mage/cards/v/ViviensArkbow.java b/Mage.Sets/src/mage/cards/v/ViviensArkbow.java index 1ebf9debe058..98cc6246800b 100644 --- a/Mage.Sets/src/mage/cards/v/ViviensArkbow.java +++ b/Mage.Sets/src/mage/cards/v/ViviensArkbow.java @@ -10,7 +10,7 @@ import mage.constants.*; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; @@ -52,7 +52,7 @@ class ViviensArkbowEffect extends OneShotEffect { ViviensArkbowEffect() { super(Outcome.PutCardInPlay); staticText = "Look at the top X cards of your library. " + - "You may put a creature card with converted mana cost X or less " + + "You may put a creature card with mana value X or less " + "from among them onto the battlefield. Put the rest on the bottom of your library in a random order."; } @@ -75,8 +75,8 @@ public boolean apply(Game game, Ability source) { Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, xValue)); player.lookAtCards(source, null, cards, game); - FilterCard filter = new FilterCreatureCard("creature card with converted mana cost " + xValue + " or less"); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, xValue + 1)); + FilterCard filter = new FilterCreatureCard("creature card with mana value " + xValue + " or less"); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); TargetCard target = new TargetCardInLibrary(0, 1, filter); if (player.choose(outcome, cards, target, game)) { diff --git a/Mage.Sets/src/mage/cards/v/ViviensJaguar.java b/Mage.Sets/src/mage/cards/v/ViviensJaguar.java index 626f9e9c5893..d045a89aec76 100644 --- a/Mage.Sets/src/mage/cards/v/ViviensJaguar.java +++ b/Mage.Sets/src/mage/cards/v/ViviensJaguar.java @@ -44,7 +44,7 @@ public ViviensJaguar(UUID ownerId, CardSetInfo setInfo) { new ManaCostsImpl("{2}{G}"), new PermanentsOnTheBattlefieldCondition(filter), "{2}{G}: Return {this} from your graveyard to your hand. " - + "Activate this ability only if you control a Vivien planeswalker" + + "Activate only if you control a Vivien planeswalker." )); } diff --git a/Mage.Sets/src/mage/cards/v/VizierOfTheAnointed.java b/Mage.Sets/src/mage/cards/v/VizierOfTheAnointed.java index 120bf5b9e3ee..c72bfbaf33fa 100644 --- a/Mage.Sets/src/mage/cards/v/VizierOfTheAnointed.java +++ b/Mage.Sets/src/mage/cards/v/VizierOfTheAnointed.java @@ -133,7 +133,7 @@ class SearchLibraryPutInGraveyard extends SearchEffect { public SearchLibraryPutInGraveyard(FilterCard filter) { super(new TargetCardInLibrary(filter), Outcome.Neutral); - staticText = "search for a creature card with eternalize or embalm, put that card into your graveyard, then shuffle your library."; + staticText = "search your library for a creature card with eternalize or embalm, put that card into your graveyard, then shuffle."; } public SearchLibraryPutInGraveyard(final SearchLibraryPutInGraveyard effect) { diff --git a/Mage.Sets/src/mage/cards/v/VizierOfTheMenagerie.java b/Mage.Sets/src/mage/cards/v/VizierOfTheMenagerie.java index 440412b62673..7ad95d44d152 100644 --- a/Mage.Sets/src/mage/cards/v/VizierOfTheMenagerie.java +++ b/Mage.Sets/src/mage/cards/v/VizierOfTheMenagerie.java @@ -36,8 +36,8 @@ public VizierOfTheMenagerie(UUID ownerId, CardSetInfo setInfo) { // You may look at the top card of your library. (You may do this at any time.) this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new LookAtTopCardOfLibraryAnyTimeEffect())); - // You may cast the top card of your library if it's a creature card. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayTheTopCardEffect(filter))); + // You may cast creature spells from the top of your library. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayTheTopCardEffect(filter, false))); // You may spend mana as though it were mana of any type to cast creature spells. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new VizierOfTheMenagerieManaEffect())); diff --git a/Mage.Sets/src/mage/cards/v/VizierOfTumblingSands.java b/Mage.Sets/src/mage/cards/v/VizierOfTumblingSands.java index 62d32be27b14..316131bcea32 100644 --- a/Mage.Sets/src/mage/cards/v/VizierOfTumblingSands.java +++ b/Mage.Sets/src/mage/cards/v/VizierOfTumblingSands.java @@ -17,7 +17,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; /** diff --git a/Mage.Sets/src/mage/cards/v/VodalianArcanist.java b/Mage.Sets/src/mage/cards/v/VodalianArcanist.java index 77f40878da2b..3d9efa6185d5 100644 --- a/Mage.Sets/src/mage/cards/v/VodalianArcanist.java +++ b/Mage.Sets/src/mage/cards/v/VodalianArcanist.java @@ -1,16 +1,16 @@ - package mage.cards.v; -import java.util.UUID; import mage.MageInt; -import mage.abilities.costs.common.TapSourceCost; import mage.abilities.mana.ConditionalColorlessManaAbility; -import mage.constants.SubType; +import mage.abilities.mana.builder.common.InstantOrSorcerySpellManaBuilder; 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 VodalianArcanist extends CardImpl { @@ -24,8 +24,7 @@ public VodalianArcanist(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(3); // {T}: Add {C}. Spend this mana only to cast an instant or sorcery spell. - this.addAbility(new ConditionalColorlessManaAbility(new TapSourceCost(), 1, - new mage.abilities.mana.builder.common.InstantOrSorcerySpellManaBuilder())); + this.addAbility(new ConditionalColorlessManaAbility(1, new InstantOrSorcerySpellManaBuilder())); } private VodalianArcanist(final VodalianArcanist card) { diff --git a/Mage.Sets/src/mage/cards/v/VoiceOfResurgence.java b/Mage.Sets/src/mage/cards/v/VoiceOfResurgence.java index 00396361e0b6..1097c4d4bec1 100644 --- a/Mage.Sets/src/mage/cards/v/VoiceOfResurgence.java +++ b/Mage.Sets/src/mage/cards/v/VoiceOfResurgence.java @@ -15,6 +15,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import mage.game.permanent.token.VoiceOfResurgenceToken; import java.util.UUID; @@ -34,7 +35,7 @@ public VoiceOfResurgence(UUID ownerId, CardSetInfo setInfo) { // Whenever an opponent casts a spell during your turn or when Voice of Resurgence dies, create a green and white Elemental creature token with "This creature's power and toughness are each equal to the number of creatures you control." OrTriggeredAbility ability = new OrTriggeredAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new VoiceOfResurgenceToken()), new ConditionalTriggeredAbility( - new SpellCastOpponentTriggeredAbility(null, new FilterSpell("a spell"), false), + new SpellCastOpponentTriggeredAbility(null, StaticFilters.FILTER_SPELL_A, false), MyTurnCondition.instance, "Whenever an opponent casts a spell during your turn, "), new DiesSourceTriggeredAbility(null, false)); diff --git a/Mage.Sets/src/mage/cards/v/Void.java b/Mage.Sets/src/mage/cards/v/Void.java index 4dd460d46792..56b1ba6e110a 100644 --- a/Mage.Sets/src/mage/cards/v/Void.java +++ b/Mage.Sets/src/mage/cards/v/Void.java @@ -10,7 +10,7 @@ import mage.constants.Outcome; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -45,7 +45,7 @@ class VoidEffect extends OneShotEffect { VoidEffect() { super(Outcome.DestroyPermanent); - this.staticText = "Choose a number. Destroy all artifacts and creatures with converted mana cost equal to that number. Then target player reveals their hand and discards all nonland cards with converted mana cost equal to the number"; + this.staticText = "Choose a number. Destroy all artifacts and creatures with mana value equal to that number. Then target player reveals their hand and discards all nonland cards with mana value equal to the number"; } private VoidEffect(final VoidEffect effect) { @@ -69,12 +69,12 @@ public boolean apply(Game game, Ability source) { for (Permanent permanent : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) { if ((permanent.isArtifact() || permanent.isCreature()) - && permanent.getConvertedManaCost() == number) { + && permanent.getManaValue() == number) { permanent.destroy(source, game, false); } } FilterCard filterCard = new FilterCard(); - filterCard.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, number)); + filterCard.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, number)); filterCard.add(Predicates.not(CardType.LAND.getPredicate())); Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source)); diff --git a/Mage.Sets/src/mage/cards/v/VoidGrafter.java b/Mage.Sets/src/mage/cards/v/VoidGrafter.java index a64020de2976..44f57c6e57ab 100644 --- a/Mage.Sets/src/mage/cards/v/VoidGrafter.java +++ b/Mage.Sets/src/mage/cards/v/VoidGrafter.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.constants.Duration; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/v/VoidMaw.java b/Mage.Sets/src/mage/cards/v/VoidMaw.java index 8091fd93f388..7b75a7e9d37b 100644 --- a/Mage.Sets/src/mage/cards/v/VoidMaw.java +++ b/Mage.Sets/src/mage/cards/v/VoidMaw.java @@ -138,7 +138,8 @@ public boolean pay(Ability ability, Game game, Ability source, UUID controllerId TargetCardInExile target = new TargetCardInExile(new FilterCard(), CardUtil.getCardExileZoneId(game, ability)); target.setNotTarget(true); Cards cards = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, ability)); - if (!cards.isEmpty() + if (cards != null + && !cards.isEmpty() && controller.choose(Outcome.Benefit, cards, target, game)) { Card card = game.getCard(target.getFirstTarget()); if (card != null) { diff --git a/Mage.Sets/src/mage/cards/v/VoidWinnower.java b/Mage.Sets/src/mage/cards/v/VoidWinnower.java index 6dcaa9dad7d3..16c0efe7357d 100644 --- a/Mage.Sets/src/mage/cards/v/VoidWinnower.java +++ b/Mage.Sets/src/mage/cards/v/VoidWinnower.java @@ -11,7 +11,6 @@ import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.game.stack.Spell; @@ -50,7 +49,7 @@ class VoidWinnowerCantCastEffect extends ContinuousRuleModifyingEffectImpl { public VoidWinnowerCantCastEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "Your opponent can't cast spells with even converted mana costs. (Zero is even.)"; + staticText = "Your opponent can't cast spells with even mana values. (Zero is even.)"; } public VoidWinnowerCantCastEffect(final VoidWinnowerCantCastEffect effect) { @@ -71,7 +70,7 @@ public boolean apply(Game game, Ability source) { public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source.getSourceId()); if (mageObject != null) { - return "You can't cast spells with even converted mana costs (" + mageObject.getIdName() + ")."; + return "You can't cast spells with even mana values (" + mageObject.getIdName() + ")."; } return null; } @@ -87,7 +86,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { Spell spell = game.getStack().getSpell(event.getTargetId()); if (spell != null) { // the low bit will always be set on an odd number. - return (spell.getConvertedManaCost() & 1) == 0; + return (spell.getManaValue() & 1) == 0; } } return false; @@ -98,7 +97,7 @@ class VoidWinnowerCantBlockEffect extends RestrictionEffect { public VoidWinnowerCantBlockEffect() { super(Duration.WhileOnBattlefield); - staticText = "Your opponents can't block with creatures with even converted mana costs"; + staticText = "Your opponents can't block with creatures with even mana values"; } public VoidWinnowerCantBlockEffect(final VoidWinnowerCantBlockEffect effect) { @@ -114,7 +113,7 @@ public VoidWinnowerCantBlockEffect copy() { public boolean applies(Permanent permanent, Ability source, Game game) { if (game.getOpponents(source.getControllerId()).contains(permanent.getControllerId())) { // the low bit will always be set on an odd number. - return (permanent.getConvertedManaCost() & 1) == 0; + return (permanent.getManaValue() & 1) == 0; } return false; } diff --git a/Mage.Sets/src/mage/cards/v/VolatileRig.java b/Mage.Sets/src/mage/cards/v/VolatileRig.java index 73dc22d2ed95..0b271238269e 100644 --- a/Mage.Sets/src/mage/cards/v/VolatileRig.java +++ b/Mage.Sets/src/mage/cards/v/VolatileRig.java @@ -2,18 +2,18 @@ import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.AttacksEachCombatStaticAbility; +import mage.abilities.common.DealtDamageToSourceTriggeredAbility; import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; @@ -39,7 +39,7 @@ public VolatileRig(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new AttacksEachCombatStaticAbility()); // Whenever Volatile Rig is dealt damage, flip a coin. If you lose the flip, sacrifice Volatile Rig. - this.addAbility(new VolatileRigTriggeredAbility()); + this.addAbility(new DealtDamageToSourceTriggeredAbility(new VolatileRigEffect(), false)); // When Volatile Rig dies, flip a coin. If you lose the flip, it deals 4 damage to each creature and each player. this.addAbility(new DiesSourceTriggeredAbility(new VolatileRigEffect2())); @@ -56,60 +56,6 @@ public VolatileRig copy() { } } -class VolatileRigTriggeredAbility extends TriggeredAbilityImpl { - - private boolean triggerdThisCombatStep = false; - - public VolatileRigTriggeredAbility() { - super(Zone.BATTLEFIELD, new VolatileRigEffect()); - } - - public VolatileRigTriggeredAbility(final VolatileRigTriggeredAbility effect) { - super(effect); - this.triggerdThisCombatStep = effect.triggerdThisCombatStep; - } - - @Override - public VolatileRigTriggeredAbility copy() { - return new VolatileRigTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_POST - || event.getType() == GameEvent.EventType.DAMAGED_CREATURE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - /* - * If Volatile Rig is dealt damage by multiple sources at the same time - * (for example, multiple blocking creatures), its first triggered ability - * will trigger only once. - */ - if (triggerdThisCombatStep && event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_POST) { - triggerdThisCombatStep = false; - } - - if (event.getType() == GameEvent.EventType.DAMAGED_CREATURE && event.getTargetId().equals(this.sourceId)) { - if (game.getPhase().getStep().getType() == PhaseStep.COMBAT_DAMAGE) { - if (triggerdThisCombatStep) { - return false; - } else { - triggerdThisCombatStep = true; - } - } - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} is dealt damage, " + super.getRule(); - } -} - class VolatileRigEffect extends OneShotEffect { VolatileRigEffect() { @@ -117,7 +63,7 @@ class VolatileRigEffect extends OneShotEffect { staticText = "flip a coin. If you lose the flip, sacrifice {this}"; } - VolatileRigEffect(final VolatileRigEffect effect) { + private VolatileRigEffect(final VolatileRigEffect effect) { super(effect); } diff --git a/Mage.Sets/src/mage/cards/v/VolcanicVision.java b/Mage.Sets/src/mage/cards/v/VolcanicVision.java index f7a6595b3b63..7db71625e67b 100644 --- a/Mage.Sets/src/mage/cards/v/VolcanicVision.java +++ b/Mage.Sets/src/mage/cards/v/VolcanicVision.java @@ -31,7 +31,7 @@ public VolcanicVision(UUID ownerId, CardSetInfo setInfo) { // Return target instant or sorcery card from your graveyard to your hand. Volcanic Visions deals damage equal to that card's converted mana cost to each creature your opponents control. Exile Volcanic Vision. this.getSpellAbility().addEffect(new VolcanicVisionReturnToHandTargetEffect()); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(new FilterInstantOrSorceryCard("instant or sorcery card from your graveyard"))); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private VolcanicVision(final VolcanicVision card) { @@ -54,7 +54,7 @@ class VolcanicVisionReturnToHandTargetEffect extends OneShotEffect { public VolcanicVisionReturnToHandTargetEffect() { super(Outcome.ReturnToHand); - staticText = "Return target instant or sorcery card from your graveyard to your hand. {this} deals damage equal to that card's converted mana cost to each creature your opponents control"; + staticText = "Return target instant or sorcery card from your graveyard to your hand. {this} deals damage equal to that card's mana value to each creature your opponents control"; } public VolcanicVisionReturnToHandTargetEffect(final VolcanicVisionReturnToHandTargetEffect effect) { @@ -78,7 +78,7 @@ public boolean apply(Game game, Ability source) { Card card = game.getCard(targetId); if (card != null) { controller.moveCards(card, Zone.HAND, source, game); - int damage = card.getConvertedManaCost(); + int damage = card.getManaValue(); if (damage > 0) { for (Permanent creature : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { creature.damage(damage, source.getSourceId(), source, game, false, true); diff --git a/Mage.Sets/src/mage/cards/v/VolcanoHellion.java b/Mage.Sets/src/mage/cards/v/VolcanoHellion.java index 66e5ff2148fa..c1f828f3ef18 100644 --- a/Mage.Sets/src/mage/cards/v/VolcanoHellion.java +++ b/Mage.Sets/src/mage/cards/v/VolcanoHellion.java @@ -72,7 +72,7 @@ public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getFirstTarget()); if (controller != null) { int amount; - if (!controller.isHuman() && !controller.isTestMode()) { + if (controller.isComputer()) { // AI hint: have much life and can destroy target permanent int safeLifeToLost = Math.min(6, controller.getLife() / 2); if (permanent != null && permanent.getToughness().getValue() <= safeLifeToLost) { diff --git a/Mage.Sets/src/mage/cards/v/VoldarenPariah.java b/Mage.Sets/src/mage/cards/v/VoldarenPariah.java index 4cf90e5459b1..09dbf6d09aad 100644 --- a/Mage.Sets/src/mage/cards/v/VoldarenPariah.java +++ b/Mage.Sets/src/mage/cards/v/VoldarenPariah.java @@ -17,7 +17,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledPermanent; /** diff --git a/Mage.Sets/src/mage/cards/v/VolrathTheFallen.java b/Mage.Sets/src/mage/cards/v/VolrathTheFallen.java index bcda2e8a5ed4..f104a610a1c5 100644 --- a/Mage.Sets/src/mage/cards/v/VolrathTheFallen.java +++ b/Mage.Sets/src/mage/cards/v/VolrathTheFallen.java @@ -34,7 +34,7 @@ public VolrathTheFallen(UUID ownerId, CardSetInfo setInfo) { // {1}{B}, Discard a creature card: // Volrath the Fallen gets +X/+X until end of turn, where X is the discarded card's converted mana cost. Effect effect = new BoostSourceEffect(DiscardCostCardConvertedMana.instance, DiscardCostCardConvertedMana.instance, Duration.EndOfTurn); - effect.setText("{this} gets +X/+X until end of turn, where X is the discarded card's converted mana cost"); + effect.setText("{this} gets +X/+X until end of turn, where X is the discarded card's mana value"); Ability ability = new SimpleActivatedAbility( Zone.BATTLEFIELD, diff --git a/Mage.Sets/src/mage/cards/v/VolrathsDungeon.java b/Mage.Sets/src/mage/cards/v/VolrathsDungeon.java index 99d0737061a9..bc2e745ea1a2 100644 --- a/Mage.Sets/src/mage/cards/v/VolrathsDungeon.java +++ b/Mage.Sets/src/mage/cards/v/VolrathsDungeon.java @@ -18,9 +18,7 @@ import mage.constants.Outcome; import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.FilterCard; import mage.game.Game; -import mage.game.events.GameEvent; import mage.players.Player; import mage.target.TargetPlayer; import mage.target.common.TargetCardInHand; @@ -45,10 +43,11 @@ public VolrathsDungeon(UUID ownerId, CardSetInfo setInfo) { this.addAbility(ability); // Discard a card: Target player puts a card from their hand on top of their library. Activate this ability only any time you could cast a sorcery. - FilterCard filter = new FilterCard("a card for payment"); - Ability ability2 = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new VolrathsDungeonEffect(), new DiscardCardCost(filter)); - ability2.addTarget(new TargetPlayer()); - this.addAbility(ability2); + ability = new ActivateAsSorceryActivatedAbility( + Zone.BATTLEFIELD, new VolrathsDungeonEffect(), new DiscardCardCost() + ); + ability.addTarget(new TargetPlayer()); + this.addAbility(ability); } private VolrathsDungeon(final VolrathsDungeon card) { @@ -94,7 +93,7 @@ public boolean pay(Ability ability, Game game, Ability source, UUID controllerId } int lifeToPayAmount = amount.calculate(game, ability, null); - if (activatingPlayer.chooseUse(Outcome.LoseLife, "Do you wish to pay " + lifeToPayAmount + " life?", ability, game)) { + if (activatingPlayer.chooseUse(Outcome.LoseLife, "Pay " + lifeToPayAmount + " life?", ability, game)) { this.paid = CardUtil.tryPayLife(lifeToPayAmount, activatingPlayer, source, game); return this.paid; } diff --git a/Mage.Sets/src/mage/cards/v/VoodooDoll.java b/Mage.Sets/src/mage/cards/v/VoodooDoll.java index 7b43e5dced57..5cbc1565b80a 100644 --- a/Mage.Sets/src/mage/cards/v/VoodooDoll.java +++ b/Mage.Sets/src/mage/cards/v/VoodooDoll.java @@ -1,18 +1,16 @@ - package mage.cards.v; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.condition.InvertCondition; import mage.abilities.condition.common.SourceTappedCondition; -import mage.abilities.costs.VariableCost; +import mage.abilities.costs.CostAdjuster; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.costs.mana.VariableManaCost; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.effects.common.DamageControllerEffect; import mage.abilities.effects.common.DamageTargetEffect; @@ -22,51 +20,44 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.TargetController; -import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetAnyTarget; +import java.util.UUID; + /** - * * @author L_J */ public final class VoodooDoll extends CardImpl { public VoodooDoll(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{6}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{6}"); // At the beginning of your upkeep, put a pin counter on Voodoo Doll. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new AddCountersSourceEffect(CounterType.PIN.createInstance()), TargetController.YOU, false)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new AddCountersSourceEffect(CounterType.PIN.createInstance()), TargetController.YOU, false + )); + // At the beginning of your end step, if Voodoo Doll is untapped, destroy Voodoo Doll and it deals damage to you equal to the number of pin counters on it. - Ability ability = new BeginningOfEndStepTriggeredAbility(Zone.BATTLEFIELD, new DestroySourceEffect(), TargetController.YOU, - new InvertCondition(SourceTappedCondition.instance), false); + Ability ability = new ConditionalInterveningIfTriggeredAbility( + new BeginningOfEndStepTriggeredAbility( + new DestroySourceEffect(), TargetController.YOU, false + ), new InvertCondition(SourceTappedCondition.instance), "At the beginning of your end step, " + + "if {this} is untapped, destroy {this} and it deals damage to you equal to the number of pin counters on it." + ); ability.addEffect(new DamageControllerEffect(new CountersSourceCount(CounterType.PIN))); this.addAbility(ability); - // {X}{X}, {T}: Voodoo Doll deals damage equal to the number of pin counters on it to any target. X is the number of pin counters on Voodoo Doll. - Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(new CountersSourceCount(CounterType.PIN)), new ManaCostsImpl("{X}{X}")); - ability2.addCost(new TapSourceCost()); - ability2.addTarget(new TargetAnyTarget()); - for (VariableCost cost : ability2.getManaCosts().getVariableCosts()) { - if (cost instanceof VariableManaCost) { - ((VariableManaCost) cost).setMaxX(0); - break; - } - } - this.addAbility(ability2); - } - @Override - public void adjustCosts(Ability ability, Game game) { - if (ability instanceof SimpleActivatedAbility) { - Permanent sourcePermanent = game.getPermanent(ability.getSourceId()); - if (sourcePermanent != null) { - int pin = sourcePermanent.getCounters(game).getCount(CounterType.PIN); - ability.getManaCostsToPay().clear(); - ability.getManaCostsToPay().add(0, new GenericManaCost(pin * 2)); - } - } + // {X}{X}, {T}: Voodoo Doll deals damage equal to the number of pin counters on it to any target. X is the number of pin counters on Voodoo Doll. + ability = new SimpleActivatedAbility( + new DamageTargetEffect(new CountersSourceCount(CounterType.PIN)), new ManaCostsImpl("{X}{X}") + ); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetAnyTarget()); + ability.setCostAdjuster(VoodooDollAdjuster.instance); + this.addAbility(ability); } private VoodooDoll(final VoodooDoll card) { @@ -78,3 +69,17 @@ public VoodooDoll copy() { return new VoodooDoll(this); } } + +enum VoodooDollAdjuster implements CostAdjuster { + instance; + + @Override + public void adjustCosts(Ability ability, Game game) { + Permanent sourcePermanent = game.getPermanent(ability.getSourceId()); + if (sourcePermanent != null) { + int pin = sourcePermanent.getCounters(game).getCount(CounterType.PIN); + ability.getManaCostsToPay().clear(); + ability.getManaCostsToPay().add(0, new GenericManaCost(pin * 2)); + } + } +} diff --git a/Mage.Sets/src/mage/cards/v/VoraciousHydra.java b/Mage.Sets/src/mage/cards/v/VoraciousHydra.java index 088ba3cb4373..35e7a097750a 100644 --- a/Mage.Sets/src/mage/cards/v/VoraciousHydra.java +++ b/Mage.Sets/src/mage/cards/v/VoraciousHydra.java @@ -5,19 +5,16 @@ import mage.abilities.Mode; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DoubleCountersSourceEffect; import mage.abilities.effects.common.EntersBattlefieldWithXCountersEffect; import mage.abilities.effects.common.FightTargetSourceEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import java.util.UUID; @@ -44,7 +41,7 @@ public VoraciousHydra(UUID ownerId, CardSetInfo setInfo) { // When Voracious Hydra enters the battlefield, choose one — // • Double the number of +1/+1 counters on Voracious Hydra. - Ability ability = new EntersBattlefieldTriggeredAbility(new VoraciousHydraEffect(), false); + Ability ability = new EntersBattlefieldTriggeredAbility(new DoubleCountersSourceEffect(CounterType.P1P1), false); // • Voracious Hydra fights target creature you don't control. Mode mode = new Mode( @@ -65,31 +62,3 @@ public VoraciousHydra copy() { return new VoraciousHydra(this); } } - -class VoraciousHydraEffect extends OneShotEffect { - - VoraciousHydraEffect() { - super(Outcome.Benefit); - staticText = "Double the number of +1/+1 counters on {this}"; - } - - private VoraciousHydraEffect(final VoraciousHydraEffect effect) { - super(effect); - } - - @Override - public VoraciousHydraEffect copy() { - return new VoraciousHydraEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent == null) { - return false; - } - return permanent.addCounters(CounterType.P1P1.createInstance( - permanent.getCounters(game).getCount(CounterType.P1P1) - ), source.getControllerId(), source, game); - } -} diff --git a/Mage.Sets/src/mage/cards/v/VoraciousWurm.java b/Mage.Sets/src/mage/cards/v/VoraciousWurm.java index e8c14b1ee228..289a7ef2ed23 100644 --- a/Mage.Sets/src/mage/cards/v/VoraciousWurm.java +++ b/Mage.Sets/src/mage/cards/v/VoraciousWurm.java @@ -1,7 +1,5 @@ - package mage.cards.v; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.dynamicvalue.common.ControllerGotLifeCount; @@ -13,6 +11,8 @@ import mage.counters.CounterType; import mage.watchers.common.PlayerGainedLifeWatcher; +import java.util.UUID; + /** * @author LevelX2 */ @@ -26,9 +26,9 @@ public VoraciousWurm(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(2); // Voracious Wurm enters the battlefield with X +1/+1 counters on it, where X is the amount of life you've gained this turn. - this.addAbility(new EntersBattlefieldAbility( - new AddCountersSourceEffect(CounterType.P1P1.createInstance(0), ControllerGotLifeCount.getInstance(), true)), - new PlayerGainedLifeWatcher()); + this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect( + CounterType.P1P1.createInstance(0), ControllerGotLifeCount.instance, true + )).addHint(ControllerGotLifeCount.getHint()), new PlayerGainedLifeWatcher()); } private VoraciousWurm(final VoraciousWurm card) { diff --git a/Mage.Sets/src/mage/cards/v/VortexElemental.java b/Mage.Sets/src/mage/cards/v/VortexElemental.java index 73adfa06293a..40e2e810ff66 100644 --- a/Mage.Sets/src/mage/cards/v/VortexElemental.java +++ b/Mage.Sets/src/mage/cards/v/VortexElemental.java @@ -60,7 +60,7 @@ class VortexElementalEffect extends OneShotEffect { public VortexElementalEffect() { super(Outcome.Benefit); - this.staticText = "Put {this} and each creature blocking or blocked by it on top of their owners' libraries, then those players shuffle their libraries"; + this.staticText = "Put {this} and each creature blocking or blocked by it on top of their owners' libraries, then those players shuffle"; } public VortexElementalEffect(final VortexElementalEffect effect) { diff --git a/Mage.Sets/src/mage/cards/v/VortexRunner.java b/Mage.Sets/src/mage/cards/v/VortexRunner.java new file mode 100644 index 000000000000..2462152f1c09 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VortexRunner.java @@ -0,0 +1,58 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.decorator.ConditionalRestrictionEffect; +import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.common.LandsYouControlHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VortexRunner extends CardImpl { + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, ComparisonType.MORE_THAN, 7 + ); + + public VortexRunner(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // As long as you control eight or more lands, Vortex Runner gets +1/+0 and can't be blocked. + Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostSourceEffect(1, 0, Duration.WhileOnBattlefield), + condition, "as long as you control eight or more lands, {this} gets +1/+0")); + ability.addEffect(new ConditionalRestrictionEffect( + new CantBeBlockedSourceEffect(), condition, "and can't be blocked" + )); + this.addAbility(ability.addHint(LandsYouControlHint.instance)); + } + + private VortexRunner(final VortexRunner card) { + super(card); + } + + @Override + public VortexRunner copy() { + return new VortexRunner(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VowOfDuty.java b/Mage.Sets/src/mage/cards/v/VowOfDuty.java index ea709b0c4df2..e3fb89591c8a 100644 --- a/Mage.Sets/src/mage/cards/v/VowOfDuty.java +++ b/Mage.Sets/src/mage/cards/v/VowOfDuty.java @@ -45,7 +45,7 @@ public VowOfDuty(UUID ownerId, CardSetInfo setInfo) { effect.setText(", has vigilance"); ability.addEffect(effect); effect = new CantAttackControllerAttachedEffect(AttachmentType.AURA); - effect.setText(", and can't attack you or a planeswalker you control"); + effect.setText(", and can't attack you or planeswalkers you control"); ability.addEffect(effect); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/v/VowOfFlight.java b/Mage.Sets/src/mage/cards/v/VowOfFlight.java index 33cd5c549a26..7ec8c6ba9e3e 100644 --- a/Mage.Sets/src/mage/cards/v/VowOfFlight.java +++ b/Mage.Sets/src/mage/cards/v/VowOfFlight.java @@ -45,7 +45,7 @@ public VowOfFlight(UUID ownerId, CardSetInfo setInfo) { effect.setText(", has flying"); ability.addEffect(effect); effect = new CantAttackControllerAttachedEffect(AttachmentType.AURA); - effect.setText(", and can't attack you or a planeswalker you control"); + effect.setText(", and can't attack you or planeswalkers you control"); ability.addEffect(effect); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/v/VowOfLightning.java b/Mage.Sets/src/mage/cards/v/VowOfLightning.java index 9b50da9ac7d0..a2133bcd8a17 100644 --- a/Mage.Sets/src/mage/cards/v/VowOfLightning.java +++ b/Mage.Sets/src/mage/cards/v/VowOfLightning.java @@ -45,7 +45,7 @@ public VowOfLightning(UUID ownerId, CardSetInfo setInfo) { effect.setText(", has first strike"); ability.addEffect(effect); effect = new CantAttackControllerAttachedEffect(AttachmentType.AURA); - effect.setText(", and can't attack you or a planeswalker you control"); + effect.setText(", and can't attack you or planeswalkers you control"); ability.addEffect(effect); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/v/VowOfMalice.java b/Mage.Sets/src/mage/cards/v/VowOfMalice.java index 18cc278fc727..c1333be375fe 100644 --- a/Mage.Sets/src/mage/cards/v/VowOfMalice.java +++ b/Mage.Sets/src/mage/cards/v/VowOfMalice.java @@ -45,7 +45,7 @@ public VowOfMalice(UUID ownerId, CardSetInfo setInfo) { effect.setText(", has intimidate"); ability.addEffect(effect); effect = new CantAttackControllerAttachedEffect(AttachmentType.AURA); - effect.setText(", and can't attack you or a planeswalker you control"); + effect.setText(", and can't attack you or planeswalkers you control"); ability.addEffect(effect); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/v/VowOfTorment.java b/Mage.Sets/src/mage/cards/v/VowOfTorment.java index 7b993b8601c5..d78825945074 100644 --- a/Mage.Sets/src/mage/cards/v/VowOfTorment.java +++ b/Mage.Sets/src/mage/cards/v/VowOfTorment.java @@ -39,7 +39,7 @@ public VowOfTorment(UUID ownerId, CardSetInfo setInfo) { new MenaceAbility(), AttachmentType.AURA, Duration.WhileOnBattlefield ).setText(", has menace")); ability.addEffect(new CantAttackControllerAttachedEffect(AttachmentType.AURA) - .setText(", and can't attack you or a planeswalker you control")); + .setText(", and can't attack you or planeswalkers you control")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/v/VowOfWildness.java b/Mage.Sets/src/mage/cards/v/VowOfWildness.java index ab6128747f4d..2ef58cd2fe36 100644 --- a/Mage.Sets/src/mage/cards/v/VowOfWildness.java +++ b/Mage.Sets/src/mage/cards/v/VowOfWildness.java @@ -46,7 +46,7 @@ public VowOfWildness(UUID ownerId, CardSetInfo setInfo) { effect.setText(", has trample"); ability.addEffect(effect); effect = new CantAttackControllerAttachedEffect(AttachmentType.AURA); - effect.setText(", and can't attack you or a planeswalker you control"); + effect.setText(", and can't attack you or planeswalkers you control"); ability.addEffect(effect); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/v/VraskaGolgariQueen.java b/Mage.Sets/src/mage/cards/v/VraskaGolgariQueen.java index 071c77d450c2..209f27da5284 100644 --- a/Mage.Sets/src/mage/cards/v/VraskaGolgariQueen.java +++ b/Mage.Sets/src/mage/cards/v/VraskaGolgariQueen.java @@ -19,8 +19,8 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterNonlandPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.command.emblems.VraskaGolgariQueenEmblem; import mage.target.TargetPermanent; import mage.target.common.TargetControlledPermanent; @@ -34,11 +34,11 @@ public final class VraskaGolgariQueen extends CardImpl { private static final FilterControlledPermanent filter1 = new FilterControlledPermanent("another permanent"); private static final FilterPermanent filter2 - = new FilterNonlandPermanent("nonland permanent with converted mana cost 3 or less"); + = new FilterNonlandPermanent("nonland permanent with mana value 3 or less"); static { filter1.add(AnotherPredicate.instance); - filter2.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter2.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public VraskaGolgariQueen(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/v/VraskaSwarmsEminence.java b/Mage.Sets/src/mage/cards/v/VraskaSwarmsEminence.java index 3dc008f4edb5..2285bde5e1b4 100644 --- a/Mage.Sets/src/mage/cards/v/VraskaSwarmsEminence.java +++ b/Mage.Sets/src/mage/cards/v/VraskaSwarmsEminence.java @@ -1,6 +1,5 @@ package mage.cards.v; -import java.util.UUID; import mage.abilities.LoyaltyAbility; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; @@ -24,6 +23,8 @@ import mage.game.permanent.token.AssassinToken2; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** * @author TheElk801 */ @@ -84,21 +85,23 @@ public VraskaSwarmsEminenceTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + || event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { Permanent permanent = game.getPermanent(event.getSourceId()); - if (permanent != null && filter.match(permanent, getSourceId(), getControllerId(), game)) { - for (Effect effect : this.getEffects()) { - effect.setValue("damage", event.getAmount()); - effect.setValue("sourceId", event.getSourceId()); - effect.setTargetPointer(new FixedTarget(permanent.getId(), permanent.getZoneChangeCounter(game))); - } - return true; + if (permanent == null || !filter.match(permanent, getSourceId(), getControllerId(), game)) { + return false; + } + Permanent damaged = game.getPermanentOrLKIBattlefield(event.getTargetId()); + if (damaged != null && !damaged.isPlaneswalker()) { + return false; } - return false; + getEffects().setValue("damage", event.getAmount()); + getEffects().setValue("sourceId", event.getSourceId()); + getEffects().setTargetPointer(new FixedTarget(permanent.getId(), permanent.getZoneChangeCounter(game))); + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/v/VraskaTheUnseen.java b/Mage.Sets/src/mage/cards/v/VraskaTheUnseen.java index 552e6dccd247..d329426ab12c 100644 --- a/Mage.Sets/src/mage/cards/v/VraskaTheUnseen.java +++ b/Mage.Sets/src/mage/cards/v/VraskaTheUnseen.java @@ -12,9 +12,8 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; -import mage.game.events.DamagedPlaneswalkerEvent; +import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.game.permanent.token.AssassinToken; import mage.target.common.TargetNonlandPermanent; @@ -115,12 +114,12 @@ public VraskaTheUnseenTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (((DamagedPlaneswalkerEvent) event).isCombatDamage() && getSourceId().equals(event.getTargetId())) { + if (((DamagedEvent) event).isCombatDamage() && getSourceId().equals(event.getTargetId())) { Permanent sourceOfDamage = game.getPermanent(event.getSourceId()); if (sourceOfDamage != null && sourceOfDamage.isCreature()) { Effect effect = this.getEffects().get(0); diff --git a/Mage.Sets/src/mage/cards/v/VraskasScorn.java b/Mage.Sets/src/mage/cards/v/VraskasScorn.java index f71b5bdf4c3b..aaf1a1274ded 100644 --- a/Mage.Sets/src/mage/cards/v/VraskasScorn.java +++ b/Mage.Sets/src/mage/cards/v/VraskasScorn.java @@ -32,7 +32,7 @@ public VraskasScorn(UUID ownerId, CardSetInfo setInfo) { // You may search your library and/or graveyard for a card named Vraska, Scheming Gorgon, reveal it, and put it into your hand. If you search your library this way, shuffle it. this.getSpellAbility().addEffect(new SearchLibraryGraveyardPutInHandEffect(filter) - .setText("You may search your library and/or graveyard for a card named Vraska, Scheming Gorgon, reveal it, and put it into your hand. If you search your library this way, shuffle it")); + .setText("You may search your library and/or graveyard for a card named Vraska, Scheming Gorgon, reveal it, and put it into your hand. If you search your library this way, shuffle")); } private VraskasScorn(final VraskasScorn card) { diff --git a/Mage.Sets/src/mage/cards/w/WakeThePast.java b/Mage.Sets/src/mage/cards/w/WakeThePast.java new file mode 100644 index 000000000000..bfa6b9954002 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WakeThePast.java @@ -0,0 +1,81 @@ +package mage.cards.w; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.HasteAbility; +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.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.targetpointer.FixedTargets; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WakeThePast extends CardImpl { + + public WakeThePast(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{R}{W}"); + + // Return all artifact cards from your graveyard to the battlefield. They gain haste until end of turn. + this.getSpellAbility().addEffect(new WakeThePastEffect()); + } + + private WakeThePast(final WakeThePast card) { + super(card); + } + + @Override + public WakeThePast copy() { + return new WakeThePast(this); + } +} + +class WakeThePastEffect extends OneShotEffect { + + WakeThePastEffect() { + super(Outcome.Benefit); + staticText = "return all artifact cards from your graveyard to the battlefield. " + + "They gain haste until end of turn"; + } + + private WakeThePastEffect(final WakeThePastEffect effect) { + super(effect); + } + + @Override + public WakeThePastEffect copy() { + return new WakeThePastEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(player.getGraveyard().getCards(StaticFilters.FILTER_CARD_ARTIFACT, game)); + if (cards.isEmpty()) { + return false; + } + player.moveCards(cards, Zone.BATTLEFIELD, source, game); + cards.removeIf(uuid -> game.getState().getZone(uuid) != Zone.BATTLEFIELD); + if (cards.isEmpty()) { + return true; + } + game.addEffect(new GainAbilityTargetEffect( + HasteAbility.getInstance(), Duration.EndOfTurn + ).setTargetPointer(new FixedTargets(cards, game)), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/w/WalkTheAeons.java b/Mage.Sets/src/mage/cards/w/WalkTheAeons.java index 1e11490898f9..f9608b5dc93b 100644 --- a/Mage.Sets/src/mage/cards/w/WalkTheAeons.java +++ b/Mage.Sets/src/mage/cards/w/WalkTheAeons.java @@ -1,43 +1,37 @@ - package mage.cards.w; -import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.costs.common.SacrificeTargetCost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.turn.AddExtraTurnTargetEffect; import mage.abilities.keyword.BuybackAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.filter.common.FilterControlledLandPermanent; -import mage.game.Game; -import mage.game.turn.TurnMod; +import mage.filter.common.FilterControlledPermanent; import mage.target.TargetPlayer; import mage.target.common.TargetControlledPermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class WalkTheAeons extends CardImpl { - private static final FilterControlledLandPermanent filter = new FilterControlledLandPermanent("three Islands"); + private static final FilterControlledPermanent filter = new FilterControlledPermanent("Islands"); static { filter.add(SubType.ISLAND.getPredicate()); } public WalkTheAeons(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{U}{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{U}{U}"); // Buyback—Sacrifice three Islands. (You may sacrifice three Islands in addition to any other costs as you cast this spell. If you do, put this card into your hand as it resolves.) - this.addAbility(new BuybackAbility(new SacrificeTargetCost(new TargetControlledPermanent(3,3, filter, true)))); + this.addAbility(new BuybackAbility(new SacrificeTargetCost(new TargetControlledPermanent(3, filter)))); // Target player takes an extra turn after this one. - this.getSpellAbility().addEffect(new ExtraTurnEffect()); + this.getSpellAbility().addEffect(new AddExtraTurnTargetEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); } @@ -50,27 +44,3 @@ public WalkTheAeons copy() { return new WalkTheAeons(this); } } - -class ExtraTurnEffect extends OneShotEffect { - - public ExtraTurnEffect() { - super(Outcome.ExtraTurn); - staticText = "Target player takes an extra turn after this one"; - } - - public ExtraTurnEffect(final ExtraTurnEffect effect) { - super(effect); - } - - @Override - public ExtraTurnEffect copy() { - return new ExtraTurnEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.getState().getTurnMods().add(new TurnMod(getTargetPointer().getFirst(game, source), false)); - return true; - } - -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/w/WallOfEssence.java b/Mage.Sets/src/mage/cards/w/WallOfEssence.java index 096019800550..bb45ba86be50 100644 --- a/Mage.Sets/src/mage/cards/w/WallOfEssence.java +++ b/Mage.Sets/src/mage/cards/w/WallOfEssence.java @@ -1,7 +1,6 @@ package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; @@ -10,23 +9,24 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamagedCreatureEvent; +import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class WallOfEssence extends CardImpl { public WallOfEssence(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); this.subtype.add(SubType.WALL); this.power = new MageInt(0); @@ -65,16 +65,20 @@ public WallOfEssenceTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.sourceId) && ((DamagedCreatureEvent)event).isCombatDamage() ) { - this.getEffects().get(0).setValue("damageAmount", event.getAmount()); - return true; + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent == null + || !permanent.isCreature() + || !event.getTargetId().equals(this.sourceId) + || !((DamagedEvent) event).isCombatDamage()) { + return false; } - return false; + this.getEffects().setValue("damageAmount", event.getAmount()); + return true; } @Override @@ -86,10 +90,10 @@ public String getRule() { class PiousWarriorGainLifeEffect extends OneShotEffect { - public PiousWarriorGainLifeEffect() { - super(Outcome.GainLife); - staticText = "you gain that much life"; - } + public PiousWarriorGainLifeEffect() { + super(Outcome.GainLife); + staticText = "you gain that much life"; + } public PiousWarriorGainLifeEffect(final PiousWarriorGainLifeEffect effect) { super(effect); diff --git a/Mage.Sets/src/mage/cards/w/WallOfForgottenPharaohs.java b/Mage.Sets/src/mage/cards/w/WallOfForgottenPharaohs.java index 33a104d8e00d..87eebd0b6f99 100644 --- a/Mage.Sets/src/mage/cards/w/WallOfForgottenPharaohs.java +++ b/Mage.Sets/src/mage/cards/w/WallOfForgottenPharaohs.java @@ -50,7 +50,7 @@ public WallOfForgottenPharaohs(UUID ownerId, CardSetInfo setInfo) { new DamageTargetEffect(1), new TapSourceCost(), new OrCondition( - "only if you control a Desert or there is a Desert card in your graveyard", + "you control a Desert or there is a Desert card in your graveyard", new PermanentsOnTheBattlefieldCondition(new FilterControlledPermanent(filterDesertPermanent)), new CardsInControllerGraveyardCondition(1, filterDesertCard) ) diff --git a/Mage.Sets/src/mage/cards/w/WallOfHope.java b/Mage.Sets/src/mage/cards/w/WallOfHope.java index bacf6084db1d..748e6eb814c6 100644 --- a/Mage.Sets/src/mage/cards/w/WallOfHope.java +++ b/Mage.Sets/src/mage/cards/w/WallOfHope.java @@ -64,13 +64,13 @@ public WallOfHopeTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getTargetId().equals(this.sourceId)) { - this.getEffects().get(0).setValue("damageAmount", event.getAmount()); + this.getEffects().setValue("damageAmount", event.getAmount()); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/w/WallOfNets.java b/Mage.Sets/src/mage/cards/w/WallOfNets.java index bea0bb6dfb3f..ddc5a01be73d 100644 --- a/Mage.Sets/src/mage/cards/w/WallOfNets.java +++ b/Mage.Sets/src/mage/cards/w/WallOfNets.java @@ -1,7 +1,5 @@ - package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.EndOfCombatTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; @@ -16,27 +14,29 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.BlockedByIdPredicate; +import java.util.UUID; + /** - * * @author LoneFox */ public final class WallOfNets extends CardImpl { public WallOfNets(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{W}"); this.subtype.add(SubType.WALL); this.power = new MageInt(0); this.toughness = new MageInt(7); // Defender this.addAbility(DefenderAbility.getInstance()); + // At end of combat, exile all creatures blocked by Wall of Nets. FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures blocked by {this}"); filter.add(new BlockedByIdPredicate(this.getId())); - this.addAbility(new EndOfCombatTriggeredAbility(new ExileAllEffect(filter, this.getId(), this.getIdName()), false)); + this.addAbility(new EndOfCombatTriggeredAbility(new ExileAllEffect(filter, true), false)); + // When Wall of Nets leaves the battlefield, return all cards exiled with Wall of Nets to the battlefield under their owners' control. - this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileEffect(this.getId(), - Zone.BATTLEFIELD, "return all cards exiled with {this} to the battlefield under their owners' control"), false)); + this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileEffect(Zone.BATTLEFIELD, "return all cards exiled with {this} to the battlefield under their owners' control"), false)); } private WallOfNets(final WallOfNets card) { diff --git a/Mage.Sets/src/mage/cards/w/WallOfShadows.java b/Mage.Sets/src/mage/cards/w/WallOfShadows.java index 1184812f5bf6..e02a55a677ce 100644 --- a/Mage.Sets/src/mage/cards/w/WallOfShadows.java +++ b/Mage.Sets/src/mage/cards/w/WallOfShadows.java @@ -23,7 +23,7 @@ import mage.filter.predicate.Predicate; import mage.filter.predicate.permanent.BlockedByIdPredicate; import mage.game.Game; -import mage.game.events.DamageCreatureEvent; +import mage.game.events.DamageEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.stack.StackObject; @@ -87,11 +87,11 @@ public WallOfShadowsEffect copy() { @Override public boolean applies(GameEvent event, Ability source, Game game) { if (!super.applies(event, source, game) - || !(event instanceof DamageCreatureEvent) + || !(event instanceof DamageEvent) || event.getAmount() <= 0) { return false; } - DamageCreatureEvent damageEvent = (DamageCreatureEvent) event; + DamageEvent damageEvent = (DamageEvent) event; if (!event.getTargetId().equals(source.getSourceId())) { return false; } diff --git a/Mage.Sets/src/mage/cards/w/WallOfSouls.java b/Mage.Sets/src/mage/cards/w/WallOfSouls.java index dbb5ed0d818a..212be31f1b19 100644 --- a/Mage.Sets/src/mage/cards/w/WallOfSouls.java +++ b/Mage.Sets/src/mage/cards/w/WallOfSouls.java @@ -14,9 +14,8 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamagedCreatureEvent; +import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.target.common.TargetOpponentOrPlaneswalker; /** @@ -67,12 +66,12 @@ public WallOfSoulsTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.sourceId) && ((DamagedCreatureEvent) event).isCombatDamage()) { + if (event.getTargetId().equals(this.sourceId) && ((DamagedEvent) event).isCombatDamage()) { this.getEffects().get(0).setValue("damage", event.getAmount()); return true; } diff --git a/Mage.Sets/src/mage/cards/w/WallOfVapor.java b/Mage.Sets/src/mage/cards/w/WallOfVapor.java index 8b57b717dbad..5146cec754f6 100644 --- a/Mage.Sets/src/mage/cards/w/WallOfVapor.java +++ b/Mage.Sets/src/mage/cards/w/WallOfVapor.java @@ -16,8 +16,8 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.BlockedByIdPredicate; import mage.game.Game; +import mage.game.events.DamageEvent; import mage.game.events.GameEvent; -import mage.game.events.DamageCreatureEvent; import mage.game.permanent.Permanent; /** @@ -67,8 +67,8 @@ public WallOfVaporEffect copy() { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (super.applies(event, source, game) && event instanceof DamageCreatureEvent && event.getAmount() > 0) { - DamageCreatureEvent damageEvent = (DamageCreatureEvent) event; + if (super.applies(event, source, game) && event instanceof DamageEvent && event.getAmount() > 0) { + DamageEvent damageEvent = (DamageEvent) event; if (event.getTargetId().equals(source.getSourceId())) { Permanent permanent = game.getPermanentOrLKIBattlefield(damageEvent.getSourceId()); FilterCreaturePermanent filter = new FilterCreaturePermanent(); diff --git a/Mage.Sets/src/mage/cards/w/WandOfIth.java b/Mage.Sets/src/mage/cards/w/WandOfIth.java index bc852fb6aab9..8b4032d75128 100644 --- a/Mage.Sets/src/mage/cards/w/WandOfIth.java +++ b/Mage.Sets/src/mage/cards/w/WandOfIth.java @@ -49,7 +49,7 @@ class WandOfIthEffect extends OneShotEffect { public WandOfIthEffect() { super(Outcome.Discard); - staticText = "Target player reveals a card at random from their hand. If it's a land card, that player discards it unless they pay 1 life. If it isn't a land card, the player discards it unless they pay life equal to its converted mana cost"; + staticText = "Target player reveals a card at random from their hand. If it's a land card, that player discards it unless they pay 1 life. If it isn't a land card, the player discards it unless they pay life equal to its mana value"; } public WandOfIthEffect(final WandOfIthEffect effect) { @@ -66,7 +66,7 @@ public boolean apply(Game game, Ability source) { if (card != null) { revealed.add(card); player.revealCards(sourcePermanent.getName(), revealed, game); - int lifeToPay = card.isLand() ? 1 : card.getConvertedManaCost(); + int lifeToPay = card.isLand() ? 1 : card.getManaValue(); PayLifeCost cost = new PayLifeCost(lifeToPay); if (cost.canPay(source, source, player.getId(), game) && player.chooseUse(outcome, "Pay " + lifeToPay + " life to prevent discarding " + card.getLogName() + "?", source, game) diff --git a/Mage.Sets/src/mage/cards/w/WanderingArchaic.java b/Mage.Sets/src/mage/cards/w/WanderingArchaic.java new file mode 100644 index 000000000000..5322bb6b0973 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WanderingArchaic.java @@ -0,0 +1,195 @@ +package mage.cards.w; + +import mage.abilities.Ability; +import mage.abilities.common.SpellCastOpponentTriggeredAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.cards.*; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInLibrary; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WanderingArchaic extends ModalDoubleFacesCard { + + public WanderingArchaic(UUID ownerId, CardSetInfo setInfo) { + super( + ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.AVATAR}, "{5}", + "Explore the Vastlands", + new CardType[]{CardType.SORCERY}, new SubType[]{}, "{3}" + ); + + // 1. + // Wandering Archaic + // Creature - Avatar + this.getLeftHalfCard().setPT(4, 4); + + // Whenever an opponent casts an instant or sorcery spell, they may pay {2}. If they don't, you may copy that spell. You may choose new targets for the copy. + this.getLeftHalfCard().addAbility(new SpellCastOpponentTriggeredAbility( + Zone.BATTLEFIELD, new WanderingArchaicEffect(), + StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, + false, SetTargetPointer.PLAYER + )); + + // 2. + // Explore the Vastlands + // Sorcery + // Each player looks at the top five cards of their library, reveals a land card and/or an instant or sorcery card from among them, then puts the cards they revealed this way into their hand and the rest on the bottom of their library in a random order. Each player gains 3 life. + this.getSpellAbility().addEffect(new ExploreTheVastlandsEffect()); + } + + private WanderingArchaic(final WanderingArchaic card) { + super(card); + } + + @Override + public WanderingArchaic copy() { + return new WanderingArchaic(this); + } +} + +class WanderingArchaicEffect extends OneShotEffect { + + WanderingArchaicEffect() { + super(Outcome.Benefit); + staticText = "they may pay {2}. If they don't, you may copy that spell. You may choose new targets for the copy"; + } + + private WanderingArchaicEffect(final WanderingArchaicEffect effect) { + super(effect); + } + + @Override + public WanderingArchaicEffect copy() { + return new WanderingArchaicEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player opponent = game.getPlayer(targetPointer.getFirst(game, source)); + Spell spell = (Spell) getValue("spellCast"); + if (controller == null || opponent == null || spell == null) { + return false; + } + Cost cost = new GenericManaCost(2); + if (cost.canPay( + source, source, opponent.getId(), game + ) && opponent.chooseUse( + outcome, "Pay {2} to prevent " + controller.getName() + + " from copying " + spell.getName() + "?", source, game + ) && cost.pay( + source, game, source, opponent.getId(), false + ) && controller.chooseUse( + outcome, "Copy " + spell.getName() + "?", source, game + )) { + spell.createCopyOnStack(game, source, controller.getId(), true); + } + return true; + } +} + +class ExploreTheVastlandsEffect extends OneShotEffect { + + ExploreTheVastlandsEffect() { + super(Outcome.Benefit); + staticText = "each player looks at the top five cards of their library " + + "and may reveal a land card and/or an instant or sorcery card from among them. " + + "Each player puts the cards they revealed this way into their hand and the rest " + + "on the bottom of their library in a random order. Each player gains 3 life"; + } + + private ExploreTheVastlandsEffect(final ExploreTheVastlandsEffect effect) { + super(effect); + } + + @Override + public ExploreTheVastlandsEffect copy() { + return new ExploreTheVastlandsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 5)); + TargetCard target = new ExploreTheVastlandsTarget(); + player.choose(outcome, cards, target, game); + Cards toHand = new CardsImpl(target.getTargets()); + cards.removeIf(target.getTargets()::contains); + player.revealCards(source, toHand, game); + player.moveCards(toHand, Zone.HAND, source, game); + player.putCardsOnBottomOfLibrary(cards, game, source, false); + } + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + player.gainLife(3, game, source); + } + } + return true; + } +} + +class ExploreTheVastlandsTarget extends TargetCardInLibrary { + + private static final FilterCard filter + = new FilterCard("a land card and/or an instant or sorcery card"); + + static { + filter.add(Predicates.or( + CardType.LAND.getPredicate(), + CardType.SORCERY.getPredicate(), + CardType.INSTANT.getPredicate() + )); + } + + ExploreTheVastlandsTarget() { + super(0, 2, filter); + } + + private ExploreTheVastlandsTarget(final ExploreTheVastlandsTarget target) { + super(target); + } + + @Override + public ExploreTheVastlandsTarget copy() { + return new ExploreTheVastlandsTarget(this); + } + + @Override + public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) { + if (!super.canTarget(playerId, id, source, game)) { + return false; + } + Card card = game.getCard(id); + if (card == null) { + return false; + } + if (this.getTargets().isEmpty()) { + return true; + } + boolean isLand = card.isLand(); + return this.getTargets() + .stream() + .map(game::getCard) + .filter(Objects::nonNull) + .noneMatch(c -> card.isLand() && c.isLand() || card.isInstantOrSorcery() && c.isInstantOrSorcery()); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WardenOfTheChained.java b/Mage.Sets/src/mage/cards/w/WardenOfTheChained.java index e91d79992da6..fe71b3fd5f82 100644 --- a/Mage.Sets/src/mage/cards/w/WardenOfTheChained.java +++ b/Mage.Sets/src/mage/cards/w/WardenOfTheChained.java @@ -16,7 +16,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/w/WarrenPilferers.java b/Mage.Sets/src/mage/cards/w/WarrenPilferers.java index f8063b7dc2e0..f803464c094b 100644 --- a/Mage.Sets/src/mage/cards/w/WarrenPilferers.java +++ b/Mage.Sets/src/mage/cards/w/WarrenPilferers.java @@ -1,4 +1,3 @@ - package mage.cards.w; import java.util.UUID; @@ -14,6 +13,7 @@ import mage.constants.*; import mage.filter.StaticFilters; import mage.game.Game; +import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; /** @@ -65,8 +65,10 @@ public WarrenPilferersReturnEffect copy() { @Override public boolean apply(Game game, Ability source) { Card card = game.getCard(source.getFirstTarget()); - if (card != null) { - card.moveToZone(Zone.HAND, source, game, false); + Player controller = game.getPlayer(source.getControllerId()); + if (card != null + && controller != null + && controller.moveCards(card, Zone.HAND, source, game)) { if (card.hasSubtype(SubType.GOBLIN, game)) { game.addEffect(new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.EndOfTurn), source); } diff --git a/Mage.Sets/src/mage/cards/w/WashOut.java b/Mage.Sets/src/mage/cards/w/WashOut.java index 35d6ca2f23b9..78674b76efaa 100644 --- a/Mage.Sets/src/mage/cards/w/WashOut.java +++ b/Mage.Sets/src/mage/cards/w/WashOut.java @@ -1,10 +1,12 @@ - package mage.cards.w; +import java.util.LinkedHashSet; +import java.util.Set; import java.util.UUID; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.choices.ChoiceColor; @@ -63,15 +65,17 @@ public WashOutEffect(final WashOutEffect effect) { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); + Set cardsToReturn = new LinkedHashSet<>(); ChoiceColor choice = new ChoiceColor(); - if (controller != null && controller.choose(Outcome.ReturnToHand, choice, game)) { + if (controller != null + && controller.choose(Outcome.ReturnToHand, choice, game)) { ObjectColor color = choice.getColor(); FilterPermanent filter = new FilterPermanent(); filter.add(new ColorPredicate(color)); for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - permanent.moveToZone(Zone.HAND, source, game, true); + cardsToReturn.add((Card) permanent); } - return true; + return controller.moveCards(cardsToReturn, Zone.HAND, source, game); } return false; } diff --git a/Mage.Sets/src/mage/cards/w/WatcherOfTheSpheres.java b/Mage.Sets/src/mage/cards/w/WatcherOfTheSpheres.java index 828f6625cef2..951def8948aa 100644 --- a/Mage.Sets/src/mage/cards/w/WatcherOfTheSpheres.java +++ b/Mage.Sets/src/mage/cards/w/WatcherOfTheSpheres.java @@ -16,7 +16,7 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/w/WaterfallAerialist.java b/Mage.Sets/src/mage/cards/w/WaterfallAerialist.java new file mode 100644 index 000000000000..75d092a7b1e1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WaterfallAerialist.java @@ -0,0 +1,42 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.WardAbility; +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 WaterfallAerialist extends CardImpl { + + public WaterfallAerialist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.DJINN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Ward {2} + this.addAbility(new WardAbility(new ManaCostsImpl<>("{2}"))); + } + + private WaterfallAerialist(final WaterfallAerialist card) { + super(card); + } + + @Override + public WaterfallAerialist copy() { + return new WaterfallAerialist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WaterspoutElemental.java b/Mage.Sets/src/mage/cards/w/WaterspoutElemental.java index 31db6a1d3566..e811aefba93a 100644 --- a/Mage.Sets/src/mage/cards/w/WaterspoutElemental.java +++ b/Mage.Sets/src/mage/cards/w/WaterspoutElemental.java @@ -15,7 +15,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/w/WaveOfTerror.java b/Mage.Sets/src/mage/cards/w/WaveOfTerror.java index f992a32a349a..7d218498b060 100644 --- a/Mage.Sets/src/mage/cards/w/WaveOfTerror.java +++ b/Mage.Sets/src/mage/cards/w/WaveOfTerror.java @@ -16,7 +16,7 @@ import mage.constants.TargetController; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; @@ -50,7 +50,7 @@ class WaveOfTerrorEffect extends OneShotEffect { WaveOfTerrorEffect() { super(Outcome.DestroyPermanent); - this.staticText = "destroy each creature with converted mana cost equal to the number of age counters on {this}. They can't be regenerated."; + this.staticText = "destroy each creature with mana value equal to the number of age counters on {this}. They can't be regenerated."; } WaveOfTerrorEffect(final WaveOfTerrorEffect effect) { @@ -69,7 +69,7 @@ public boolean apply(Game game, Ability source) { return false; } FilterCreaturePermanent filter = new FilterCreaturePermanent(); - filter.add(new ConvertedManaCostPredicate( + filter.add(new ManaValuePredicate( ComparisonType.EQUAL_TO, permanent.getCounters(game).getCount(CounterType.AGE) )); diff --git a/Mage.Sets/src/mage/cards/w/WaveOfVitriol.java b/Mage.Sets/src/mage/cards/w/WaveOfVitriol.java index e756da84f0ce..6c681600ef12 100644 --- a/Mage.Sets/src/mage/cards/w/WaveOfVitriol.java +++ b/Mage.Sets/src/mage/cards/w/WaveOfVitriol.java @@ -61,7 +61,7 @@ class WaveOfVitriolEffect extends OneShotEffect { public WaveOfVitriolEffect() { super(Outcome.Benefit); - this.staticText = "Each player sacrifices all artifacts, enchantments, and nonbasic lands they control. For each land sacrificed this way, its controller may search their library for a basic land card and put it onto the battlefield tapped. Then each player who searched their library this way shuffles it"; + this.staticText = "Each player sacrifices all artifacts, enchantments, and nonbasic lands they control. For each land sacrificed this way, its controller may search their library for a basic land card and put it onto the battlefield tapped. Then each player who searched their library this way shuffles"; } public WaveOfVitriolEffect(final WaveOfVitriolEffect effect) { diff --git a/Mage.Sets/src/mage/cards/w/WaywardServant.java b/Mage.Sets/src/mage/cards/w/WaywardServant.java index e861e2680227..a3b6fb49d4fb 100644 --- a/Mage.Sets/src/mage/cards/w/WaywardServant.java +++ b/Mage.Sets/src/mage/cards/w/WaywardServant.java @@ -13,7 +13,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/w/WeaverOfLies.java b/Mage.Sets/src/mage/cards/w/WeaverOfLies.java index a7fe419fd276..d5c53665d303 100644 --- a/Mage.Sets/src/mage/cards/w/WeaverOfLies.java +++ b/Mage.Sets/src/mage/cards/w/WeaverOfLies.java @@ -18,7 +18,7 @@ import mage.filter.predicate.Predicate; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.PermanentIdPredicate; import mage.game.Game; import mage.target.Target; diff --git a/Mage.Sets/src/mage/cards/w/WeedPrunerPoplar.java b/Mage.Sets/src/mage/cards/w/WeedPrunerPoplar.java index 7af6ef83c17f..fed421972e00 100644 --- a/Mage.Sets/src/mage/cards/w/WeedPrunerPoplar.java +++ b/Mage.Sets/src/mage/cards/w/WeedPrunerPoplar.java @@ -13,7 +13,7 @@ import mage.constants.Duration; import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/w/WeightOfConscience.java b/Mage.Sets/src/mage/cards/w/WeightOfConscience.java index 80d41598119b..c8fae8d52f59 100644 --- a/Mage.Sets/src/mage/cards/w/WeightOfConscience.java +++ b/Mage.Sets/src/mage/cards/w/WeightOfConscience.java @@ -16,7 +16,7 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicate; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.SharesCreatureTypePredicate; +import mage.filter.predicate.mageobject.SharesCreatureTypePredicate; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/w/WeirdHarvest.java b/Mage.Sets/src/mage/cards/w/WeirdHarvest.java index 730dff432a61..d77c0e7170b1 100644 --- a/Mage.Sets/src/mage/cards/w/WeirdHarvest.java +++ b/Mage.Sets/src/mage/cards/w/WeirdHarvest.java @@ -45,7 +45,7 @@ class WeirdHarvestEffect extends OneShotEffect { public WeirdHarvestEffect() { super(Outcome.Detriment); - this.staticText = "Each player may search their library for up to X creature cards, reveal those cards, and put them into their hand. Then each player who searched their library this way shuffles it"; + this.staticText = "each player may search their library for up to X creature cards, reveal those cards, put them into their hand, then shuffle"; } public WeirdHarvestEffect(final WeirdHarvestEffect effect) { diff --git a/Mage.Sets/src/mage/cards/w/WellLaidPlans.java b/Mage.Sets/src/mage/cards/w/WellLaidPlans.java index dd5a9b1b74ae..4ca87d2804be 100644 --- a/Mage.Sets/src/mage/cards/w/WellLaidPlans.java +++ b/Mage.Sets/src/mage/cards/w/WellLaidPlans.java @@ -55,7 +55,7 @@ public WellLaidPlansPreventionEffect copy() { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() != GameEvent.EventType.DAMAGE_CREATURE) { + if (event.getType() != GameEvent.EventType.DAMAGE_PERMANENT) { return false; } Permanent attacker = game.getPermanentOrLKIBattlefield(event.getSourceId()); diff --git a/Mage.Sets/src/mage/cards/w/WerewolfOfAncientHunger.java b/Mage.Sets/src/mage/cards/w/WerewolfOfAncientHunger.java index 0a79e7986b50..511f969c939c 100644 --- a/Mage.Sets/src/mage/cards/w/WerewolfOfAncientHunger.java +++ b/Mage.Sets/src/mage/cards/w/WerewolfOfAncientHunger.java @@ -1,37 +1,30 @@ - package mage.cards.w; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.condition.common.TransformedCondition; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.dynamicvalue.common.CardsInAllHandsCount; -import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; import mage.abilities.keyword.TrampleAbility; -import mage.abilities.keyword.TransformAbility; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.TargetController; +import mage.constants.SubType; import mage.constants.Zone; +import java.util.UUID; + /** - * * @author fireshoes */ public final class WerewolfOfAncientHunger extends CardImpl { public WerewolfOfAncientHunger(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.WEREWOLF); this.power = new MageInt(0); this.toughness = new MageInt(0); @@ -47,13 +40,12 @@ public WerewolfOfAncientHunger(UUID ownerId, CardSetInfo setInfo) { this.addAbility(TrampleAbility.getInstance()); // Werewolf of Ancient Hunger's power and toughness are each equal to the total number of cards in all players' hands. - this.addAbility(new SimpleStaticAbility(Zone.ALL, + this.addAbility(new SimpleStaticAbility(Zone.ALL, new ConditionalContinuousEffect(new SetPowerToughnessSourceEffect(CardsInAllHandsCount.instance, Duration.EndOfGame), - new TransformedCondition(false), "{this}'s power and toughness are each equal to the total number of cards in all players' hands"))); + new TransformedCondition(false), "{this}'s power and toughness are each equal to the total number of cards in all players' hands"))); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Werewolf of Ancient Hunger. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private WerewolfOfAncientHunger(final WerewolfOfAncientHunger card) { diff --git a/Mage.Sets/src/mage/cards/w/WerewolfRansacker.java b/Mage.Sets/src/mage/cards/w/WerewolfRansacker.java index a38cf8f9174e..bc1614ded9f1 100644 --- a/Mage.Sets/src/mage/cards/w/WerewolfRansacker.java +++ b/Mage.Sets/src/mage/cards/w/WerewolfRansacker.java @@ -2,20 +2,17 @@ import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.TransformSourceEffect; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetArtifactPermanent; @@ -43,8 +40,7 @@ public WerewolfRansacker(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new WerewolfRansackerAbility()); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Werewolf Ransacker. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } private WerewolfRansacker(final WerewolfRansacker card) { @@ -59,14 +55,12 @@ public WerewolfRansacker copy() { class WerewolfRansackerAbility extends TriggeredAbilityImpl { - static final String RULE_TEXT = "Whenever this creature transforms into Werewolf Ransacker, you may destroy target artifact. If that artifact is put into a graveyard this way, Werewolf Ransacker deals 3 damage to that artifact's controller"; - - public WerewolfRansackerAbility() { + WerewolfRansackerAbility() { super(Zone.BATTLEFIELD, new WerewolfRansackerEffect(), true); this.addTarget(new TargetArtifactPermanent()); } - public WerewolfRansackerAbility(final WerewolfRansackerAbility ability) { + private WerewolfRansackerAbility(final WerewolfRansackerAbility ability) { super(ability); } @@ -91,17 +85,18 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { - return RULE_TEXT + '.'; + return "Whenever this creature transforms into {this}, you may destroy target artifact. If that artifact " + + "is put into a graveyard this way, {this} deals 3 damage to that artifact's controller"; } } class WerewolfRansackerEffect extends OneShotEffect { - public WerewolfRansackerEffect() { + WerewolfRansackerEffect() { super(Outcome.DestroyPermanent); } - public WerewolfRansackerEffect(final WerewolfRansackerEffect effect) { + private WerewolfRansackerEffect(final WerewolfRansackerEffect effect) { super(effect); } @@ -112,23 +107,16 @@ public WerewolfRansackerEffect copy() { @Override public boolean apply(Game game, Ability source) { - int affectedTargets = 0; - if (this.targetPointer != null && !this.targetPointer.getTargets(game, source).isEmpty()) { - for (UUID permanentId : this.targetPointer.getTargets(game, source)) { - Permanent permanent = game.getPermanent(permanentId); - if (permanent != null) { - if (permanent.destroy(source, game, false)) { - affectedTargets++; - if (game.getState().getZone(permanent.getId()) == Zone.GRAVEYARD) { - Player player = game.getPlayer(permanent.getControllerId()); - if (player != null) { - player.damage(3, source.getSourceId(), source, game); - } - } - } - } - } + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + Player player = game.getPlayer(permanent.getControllerId()); + permanent.destroy(source, game, false); + if (game.getState().getZone(permanent.getId()) != Zone.GRAVEYARD || player == null) { + return true; } - return affectedTargets > 0; + player.damage(3, source.getSourceId(), source, game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/w/WhipOfErebos.java b/Mage.Sets/src/mage/cards/w/WhipOfErebos.java index 820ee7478123..a33e03bb005f 100644 --- a/Mage.Sets/src/mage/cards/w/WhipOfErebos.java +++ b/Mage.Sets/src/mage/cards/w/WhipOfErebos.java @@ -42,7 +42,7 @@ public WhipOfErebos(UUID ownerId, CardSetInfo setInfo) { // Creatures you control have lifelink. this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( LifelinkAbility.getInstance(), Duration.WhileOnBattlefield, - StaticFilters.FILTER_CONTROLLED_CREATURES + StaticFilters.FILTER_PERMANENT_CREATURES ))); // {2}{B}{B}, {T}: Return target creature card from your graveyard to the battlefield. diff --git a/Mage.Sets/src/mage/cards/w/WhisperwoodElemental.java b/Mage.Sets/src/mage/cards/w/WhisperwoodElemental.java index 417c2d707b54..05e94095c1a3 100644 --- a/Mage.Sets/src/mage/cards/w/WhisperwoodElemental.java +++ b/Mage.Sets/src/mage/cards/w/WhisperwoodElemental.java @@ -20,7 +20,7 @@ import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.other.FaceDownPredicate; +import mage.filter.predicate.card.FaceDownPredicate; import mage.filter.predicate.permanent.TokenPredicate; /** diff --git a/Mage.Sets/src/mage/cards/w/WickedGuardian.java b/Mage.Sets/src/mage/cards/w/WickedGuardian.java index 927389b34e6f..cb562cf9af9f 100644 --- a/Mage.Sets/src/mage/cards/w/WickedGuardian.java +++ b/Mage.Sets/src/mage/cards/w/WickedGuardian.java @@ -11,7 +11,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/w/WildDefiance.java b/Mage.Sets/src/mage/cards/w/WildDefiance.java index 322e75148e96..34d41a633626 100644 --- a/Mage.Sets/src/mage/cards/w/WildDefiance.java +++ b/Mage.Sets/src/mage/cards/w/WildDefiance.java @@ -67,7 +67,7 @@ public boolean checkTrigger(GameEvent event, Game game) { MageObject object = game.getObject(event.getSourceId()); if (object instanceof Spell) { Card c = (Spell) object; - if (c.isInstant() || c.isSorcery()) { + if (c.isInstantOrSorcery()) { if (getTargets().isEmpty()) { for (Effect effect : getEffects()) { effect.setTargetPointer(new FixedTarget(event.getTargetId())); diff --git a/Mage.Sets/src/mage/cards/w/WildPair.java b/Mage.Sets/src/mage/cards/w/WildPair.java index 7deefadacc02..5017e553a88b 100644 --- a/Mage.Sets/src/mage/cards/w/WildPair.java +++ b/Mage.Sets/src/mage/cards/w/WildPair.java @@ -41,7 +41,7 @@ public WildPair(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new ConditionalInterveningIfTriggeredAbility( new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new WildPairEffect(), filter, true, SetTargetPointer.PERMANENT, ""), new CastFromHandTargetCondition(), - "Whenever a creature enters the battlefield, if you cast it from your hand, you may search your library for a creature card with the same total power and toughness and put it onto the battlefield. If you do, shuffle your library." + "Whenever a creature enters the battlefield, if you cast it from your hand, you may search your library for a creature card with the same total power and toughness, put it onto the battlefield, then shuffle." ), new CastFromHandWatcher()); } diff --git a/Mage.Sets/src/mage/cards/w/WildResearch.java b/Mage.Sets/src/mage/cards/w/WildResearch.java index 712da1483275..6a0ede40b724 100644 --- a/Mage.Sets/src/mage/cards/w/WildResearch.java +++ b/Mage.Sets/src/mage/cards/w/WildResearch.java @@ -61,7 +61,7 @@ class WildResearchEffect extends OneShotEffect { WildResearchEffect(FilterCard filter) { super(Outcome.DrawCard); - this.staticText = "Search your library for an " + filter.getMessage() + " and reveal that card. Put it into your hand, then discard a card at random. Then shuffle your library."; + this.staticText = "Search your library for an " + filter.getMessage() + " and reveal that card. Put it into your hand, then discard a card at random. Then shuffle."; this.filter = filter; } diff --git a/Mage.Sets/src/mage/cards/w/WildWanderer.java b/Mage.Sets/src/mage/cards/w/WildWanderer.java index 33e2ca90ed72..a63db7564eac 100644 --- a/Mage.Sets/src/mage/cards/w/WildWanderer.java +++ b/Mage.Sets/src/mage/cards/w/WildWanderer.java @@ -27,7 +27,7 @@ public WildWanderer(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(2); // When Wild Wanderer enters the battlefield, you may search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library. - this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true, true))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true, true), true)); } private WildWanderer(final WildWanderer card) { diff --git a/Mage.Sets/src/mage/cards/w/WildbloodPack.java b/Mage.Sets/src/mage/cards/w/WildbloodPack.java index 7c989d9e94a8..07981c1cdd08 100644 --- a/Mage.Sets/src/mage/cards/w/WildbloodPack.java +++ b/Mage.Sets/src/mage/cards/w/WildbloodPack.java @@ -1,21 +1,18 @@ - package mage.cards.w; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.keyword.TrampleAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.common.FilterAttackingCreature; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; /** * @author nantuko @@ -23,7 +20,7 @@ public final class WildbloodPack extends CardImpl { public WildbloodPack(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.WEREWOLF); this.color.setRed(true); @@ -37,11 +34,13 @@ public WildbloodPack(UUID ownerId, CardSetInfo setInfo) { this.addAbility(TrampleAbility.getInstance()); // Attacking creatures you control get +3/+0. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(3, 0, Duration.WhileOnBattlefield, new FilterAttackingCreature(), false))); + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 3, 0, Duration.WhileOnBattlefield, + StaticFilters.FILTER_ATTACKING_CREATURES, false + ))); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Wildblood Pack. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.instance, TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfBackTriggeredAbility()); } diff --git a/Mage.Sets/src/mage/cards/w/WildbornPreserver.java b/Mage.Sets/src/mage/cards/w/WildbornPreserver.java index 2a34ec63014c..04608291c29d 100644 --- a/Mage.Sets/src/mage/cards/w/WildbornPreserver.java +++ b/Mage.Sets/src/mage/cards/w/WildbornPreserver.java @@ -20,7 +20,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/w/WildestDreams.java b/Mage.Sets/src/mage/cards/w/WildestDreams.java index 7390cc04d1ac..427cb2b71aa4 100644 --- a/Mage.Sets/src/mage/cards/w/WildestDreams.java +++ b/Mage.Sets/src/mage/cards/w/WildestDreams.java @@ -29,7 +29,7 @@ public WildestDreams(UUID ownerId, CardSetInfo setInfo) { effect.setText("Return X target cards from your graveyard to your hand"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().setTargetAdjuster(WildestDreamsAdjuster.instance); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private WildestDreams(final WildestDreams card) { diff --git a/Mage.Sets/src/mage/cards/w/WildwoodTracker.java b/Mage.Sets/src/mage/cards/w/WildwoodTracker.java index 28ab06ef891d..80efe9ee458a 100644 --- a/Mage.Sets/src/mage/cards/w/WildwoodTracker.java +++ b/Mage.Sets/src/mage/cards/w/WildwoodTracker.java @@ -14,7 +14,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/w/Willbender.java b/Mage.Sets/src/mage/cards/w/Willbender.java index 1e8247bb5b6d..e5549ae89f60 100644 --- a/Mage.Sets/src/mage/cards/w/Willbender.java +++ b/Mage.Sets/src/mage/cards/w/Willbender.java @@ -13,7 +13,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.FilterStackObject; -import mage.filter.predicate.mageobject.NumberOfTargetsPredicate; +import mage.filter.predicate.other.NumberOfTargetsPredicate; import mage.target.TargetStackObject; /** diff --git a/Mage.Sets/src/mage/cards/w/WillowduskEssenceSeer.java b/Mage.Sets/src/mage/cards/w/WillowduskEssenceSeer.java new file mode 100644 index 000000000000..c6cc1294e717 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WillowduskEssenceSeer.java @@ -0,0 +1,108 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.ControllerGotLifeCount; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.hint.Hint; +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.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.game.Game; +import mage.target.TargetPermanent; +import mage.watchers.common.PlayerGainedLifeWatcher; +import mage.watchers.common.PlayerLostLifeWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WillowduskEssenceSeer extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("another creature"); + + static { + filter.add(AnotherPredicate.instance); + } + + public WillowduskEssenceSeer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.DRYAD); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // {1}, {T}: Choose another target creature. Put a number of +1/+1 counters on it equal to the amount of life you gained this turn or the amount of life you lost this turn, whichever is greater. Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility(new AddCountersTargetEffect( + CounterType.P1P1.createInstance(), WillowduskEssenceSeerValue.instance + ).setText("choose another target creature. Put a number of +1/+1 counters on it " + + "equal to the amount of life you gained this turn or the amount of " + + "life you lost this turn, whichever is greater"), new GenericManaCost(1)); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetPermanent(filter)); + ability.addHint(ControllerGotLifeCount.getHint()); + ability.addHint(WillowduskEssenceSeerHint.instance); + this.addAbility(ability, new PlayerGainedLifeWatcher()); + } + + private WillowduskEssenceSeer(final WillowduskEssenceSeer card) { + super(card); + } + + @Override + public WillowduskEssenceSeer copy() { + return new WillowduskEssenceSeer(this); + } +} + +enum WillowduskEssenceSeerValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + PlayerLostLifeWatcher watcher1 = game.getState().getWatcher(PlayerLostLifeWatcher.class); + int lifeLost = watcher1 != null ? watcher1.getLifeLost(sourceAbility.getControllerId()) : 0; + PlayerGainedLifeWatcher watcher2 = game.getState().getWatcher(PlayerGainedLifeWatcher.class); + int lifeGained = watcher2 != null ? watcher2.getLifeGained(sourceAbility.getControllerId()) : 0; + return Math.max(lifeLost, lifeGained); + } + + @Override + public WillowduskEssenceSeerValue copy() { + return instance; + } + + @Override + public String getMessage() { + return ""; + } +} + +enum WillowduskEssenceSeerHint implements Hint { + instance; + + @Override + public String getText(Game game, Ability ability) { + PlayerLostLifeWatcher watcher = game.getState().getWatcher(PlayerLostLifeWatcher.class); + return "Life lost this turn: " + (watcher != null ? watcher.getLifeLost(ability.getControllerId()) : 0); + } + + @Override + public WillowduskEssenceSeerHint copy() { + return instance; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/w/WindsOfAbandon.java b/Mage.Sets/src/mage/cards/w/WindsOfAbandon.java index f425edc8b324..68d31a6c17d1 100644 --- a/Mage.Sets/src/mage/cards/w/WindsOfAbandon.java +++ b/Mage.Sets/src/mage/cards/w/WindsOfAbandon.java @@ -57,7 +57,7 @@ class WindsOfAbandonEffect extends OneShotEffect { super(Outcome.Exile); staticText = "Exile target creature you don't control. For each creature exiled this way, " + "its controller searches their library for a basic land card. " + - "Those players put those cards onto the battlefield tapped, then shuffle their libraries."; + "Those players put those cards onto the battlefield tapped, then shuffle."; } private WindsOfAbandonEffect(final WindsOfAbandonEffect effect) { @@ -103,7 +103,7 @@ class WindsOfAbandonOverloadEffect extends OneShotEffect { super(Outcome.Exile); staticText = "Exile each creature you don't control. For each creature exiled this way, " + "its controller searches their library for a basic land card. " + - "Those players put those cards onto the battlefield tapped, then shuffle their libraries."; + "Those players put those cards onto the battlefield tapped, then shuffle."; } private WindsOfAbandonOverloadEffect(final WindsOfAbandonOverloadEffect effect) { diff --git a/Mage.Sets/src/mage/cards/w/WindsweptHeath.java b/Mage.Sets/src/mage/cards/w/WindsweptHeath.java index b8b271cb2c7a..9a289965c3ff 100644 --- a/Mage.Sets/src/mage/cards/w/WindsweptHeath.java +++ b/Mage.Sets/src/mage/cards/w/WindsweptHeath.java @@ -21,7 +21,7 @@ public WindsweptHeath(UUID ownerId, CardSetInfo setInfo) { this.frameColor = new ObjectColor("GW"); // {tap}, Pay 1 life, Sacrifice Windswept Heath: Search your library for a Forest or Plains card and put it onto the battlefield. Then shuffle your library. - this.addAbility(new FetchLandActivatedAbility(EnumSet.of(SubType.FOREST, SubType.PLAINS))); + this.addAbility(new FetchLandActivatedAbility(SubType.FOREST, SubType.PLAINS)); } private WindsweptHeath(final WindsweptHeath card) { diff --git a/Mage.Sets/src/mage/cards/w/WingsOfTheCosmos.java b/Mage.Sets/src/mage/cards/w/WingsOfTheCosmos.java index a1f8a4c4db44..4424e703e877 100644 --- a/Mage.Sets/src/mage/cards/w/WingsOfTheCosmos.java +++ b/Mage.Sets/src/mage/cards/w/WingsOfTheCosmos.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.cards.w; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/w/WintermoorCommander.java b/Mage.Sets/src/mage/cards/w/WintermoorCommander.java index 4ebf50678aef..389a4b4c7bd6 100644 --- a/Mage.Sets/src/mage/cards/w/WintermoorCommander.java +++ b/Mage.Sets/src/mage/cards/w/WintermoorCommander.java @@ -18,7 +18,7 @@ import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/w/WintersRest.java b/Mage.Sets/src/mage/cards/w/WintersRest.java index e133795cdf87..546d6443a72d 100644 --- a/Mage.Sets/src/mage/cards/w/WintersRest.java +++ b/Mage.Sets/src/mage/cards/w/WintersRest.java @@ -15,7 +15,7 @@ import mage.constants.SuperType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.target.TargetPermanent; diff --git a/Mage.Sets/src/mage/cards/w/WirewoodHivemaster.java b/Mage.Sets/src/mage/cards/w/WirewoodHivemaster.java index 4a72125097dd..50d849e60858 100644 --- a/Mage.Sets/src/mage/cards/w/WirewoodHivemaster.java +++ b/Mage.Sets/src/mage/cards/w/WirewoodHivemaster.java @@ -12,7 +12,7 @@ import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.permanent.token.InsectToken; diff --git a/Mage.Sets/src/mage/cards/w/WirewoodSymbiote.java b/Mage.Sets/src/mage/cards/w/WirewoodSymbiote.java index 8e68f5b53c33..588bcb072780 100644 --- a/Mage.Sets/src/mage/cards/w/WirewoodSymbiote.java +++ b/Mage.Sets/src/mage/cards/w/WirewoodSymbiote.java @@ -21,7 +21,7 @@ */ public final class WirewoodSymbiote extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("Elf"); + private static final FilterControlledPermanent filter = new FilterControlledPermanent("an Elf"); static { filter.add(SubType.ELF.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/w/WisdomOfTheJedi.java b/Mage.Sets/src/mage/cards/w/WisdomOfTheJedi.java index 75da31c129f4..d764e71ed493 100644 --- a/Mage.Sets/src/mage/cards/w/WisdomOfTheJedi.java +++ b/Mage.Sets/src/mage/cards/w/WisdomOfTheJedi.java @@ -14,7 +14,7 @@ import mage.constants.ComparisonType; import mage.constants.Duration; import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetSpell; import mage.target.common.TargetControlledCreaturePermanent; @@ -24,10 +24,10 @@ */ public final class WisdomOfTheJedi extends CardImpl { - private static final FilterSpell filterSpell = new FilterSpell("spell with converted mana cost of 3 or less"); + private static final FilterSpell filterSpell = new FilterSpell("spell with mana value of 3 or less"); static { - filterSpell.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filterSpell.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public WisdomOfTheJedi(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/w/WishclawTalisman.java b/Mage.Sets/src/mage/cards/w/WishclawTalisman.java index fae511df2783..5a3264ba9cdd 100644 --- a/Mage.Sets/src/mage/cards/w/WishclawTalisman.java +++ b/Mage.Sets/src/mage/cards/w/WishclawTalisman.java @@ -70,7 +70,7 @@ class WishclawTalismanEffect extends OneShotEffect { WishclawTalismanEffect() { super(Outcome.Benefit); - staticText = "Search your library for a card, put it into your hand, then shuffle your library. " + + staticText = "Search your library for a card, put it into your hand, then shuffle. " + "An opponent gains control of {this}"; } diff --git a/Mage.Sets/src/mage/cards/w/WispweaverAngel.java b/Mage.Sets/src/mage/cards/w/WispweaverAngel.java index 0fc98102cd46..cf28a17d89f3 100644 --- a/Mage.Sets/src/mage/cards/w/WispweaverAngel.java +++ b/Mage.Sets/src/mage/cards/w/WispweaverAngel.java @@ -11,7 +11,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetControlledCreaturePermanent; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/w/WitchOfTheMoors.java b/Mage.Sets/src/mage/cards/w/WitchOfTheMoors.java index 4a3f80926277..7b2e00b095f2 100644 --- a/Mage.Sets/src/mage/cards/w/WitchOfTheMoors.java +++ b/Mage.Sets/src/mage/cards/w/WitchOfTheMoors.java @@ -6,6 +6,7 @@ import mage.abilities.condition.Condition; import mage.abilities.condition.common.YouGainedLifeCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.dynamicvalue.common.ControllerGotLifeCount; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.abilities.effects.common.SacrificeOpponentsEffect; import mage.abilities.keyword.DeathtouchAbility; @@ -53,7 +54,7 @@ public WitchOfTheMoors(UUID ownerId, CardSetInfo setInfo) { ability.addTarget(new TargetCardInYourGraveyard( 0, 1, StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD )); - this.addAbility(ability, new PlayerGainedLifeWatcher()); + this.addAbility(ability.addHint(ControllerGotLifeCount.getHint()), new PlayerGainedLifeWatcher()); } private WitchOfTheMoors(final WitchOfTheMoors card) { diff --git a/Mage.Sets/src/mage/cards/w/WitchsClinic.java b/Mage.Sets/src/mage/cards/w/WitchsClinic.java new file mode 100644 index 000000000000..0668aecc935c --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WitchsClinic.java @@ -0,0 +1,54 @@ +package mage.cards.w; + +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.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.mana.ColorlessManaAbility; +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.CommanderPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WitchsClinic extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("commander"); + + static { + filter.add(CommanderPredicate.instance); + } + + public WitchsClinic(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // {T}: Add {C}. + this.addAbility(new ColorlessManaAbility()); + + // {2}, {T}: Target commander gains lifelink until end of turn. + Ability ability = new SimpleActivatedAbility(new GainAbilityTargetEffect( + LifelinkAbility.getInstance(), Duration.EndOfTurn + ), new GenericManaCost(2)); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private WitchsClinic(final WitchsClinic card) { + super(card); + } + + @Override + public WitchsClinic copy() { + return new WitchsClinic(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WitchsCottage.java b/Mage.Sets/src/mage/cards/w/WitchsCottage.java index bf659a2d3b19..797d8eb27435 100644 --- a/Mage.Sets/src/mage/cards/w/WitchsCottage.java +++ b/Mage.Sets/src/mage/cards/w/WitchsCottage.java @@ -17,7 +17,7 @@ import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCardInYourGraveyard; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/w/Withdraw.java b/Mage.Sets/src/mage/cards/w/Withdraw.java index 1f09431e2063..322a228deabb 100644 --- a/Mage.Sets/src/mage/cards/w/Withdraw.java +++ b/Mage.Sets/src/mage/cards/w/Withdraw.java @@ -11,7 +11,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/w/WitherbloomApprentice.java b/Mage.Sets/src/mage/cards/w/WitherbloomApprentice.java new file mode 100644 index 000000000000..c6634baddb48 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WitherbloomApprentice.java @@ -0,0 +1,42 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; +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 WitherbloomApprentice extends CardImpl { + + public WitherbloomApprentice(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, each opponent loses 1 life and you gain 1 life. + Ability ability = new MagecraftAbility(new LoseLifeOpponentsEffect(1)); + ability.addEffect(new GainLifeEffect(1).concatBy("and")); + this.addAbility(ability); + } + + private WitherbloomApprentice(final WitherbloomApprentice card) { + super(card); + } + + @Override + public WitherbloomApprentice copy() { + return new WitherbloomApprentice(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WitherbloomCampus.java b/Mage.Sets/src/mage/cards/w/WitherbloomCampus.java new file mode 100644 index 000000000000..974751cc78d5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WitherbloomCampus.java @@ -0,0 +1,46 @@ +package mage.cards.w; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WitherbloomCampus extends CardImpl { + + public WitherbloomCampus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // Witherbloom Campus enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {B} or {G}. + this.addAbility(new BlackManaAbility()); + this.addAbility(new GreenManaAbility()); + + // {4}, {T}: Scry 1. + Ability ability = new SimpleActivatedAbility(new ScryEffect(1), new GenericManaCost(4)); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private WitherbloomCampus(final WitherbloomCampus card) { + super(card); + } + + @Override + public WitherbloomCampus copy() { + return new WitherbloomCampus(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WitherbloomCommand.java b/Mage.Sets/src/mage/cards/w/WitherbloomCommand.java new file mode 100644 index 000000000000..8ebc8f617684 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WitherbloomCommand.java @@ -0,0 +1,117 @@ +package mage.cards.w; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.abilities.effects.common.MillCardsTargetEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterNonlandPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.TargetPermanent; +import mage.target.TargetPlayer; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WitherbloomCommand extends CardImpl { + + private static final FilterPermanent filter + = new FilterNonlandPermanent("noncreature, nonland permanent with mana value 2 or less"); + + static { + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); + } + + public WitherbloomCommand(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{B}{G}"); + + // Choose two - + this.getSpellAbility().getModes().setMinModes(2); + this.getSpellAbility().getModes().setMaxModes(2); + + // • Target player mills three cards, then you return a land card from your graveyard to your hand. + this.getSpellAbility().addEffect(new MillCardsTargetEffect(3)); + this.getSpellAbility().addEffect(new WitherbloomCommandEffect()); + this.getSpellAbility().addTarget(new TargetPlayer()); + + // • Destroy target noncreature, nonland permanent with mana value 2 or less. + Mode mode = new Mode(new DestroyTargetEffect()); + mode.addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addMode(mode); + + // • Target creature gets -3/-1 until end of turn. + mode = new Mode(new BoostTargetEffect(-3, -1)); + mode.addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addMode(mode); + + // • Target opponent loses 2 life and you gain 2 life. + mode = new Mode(new LoseLifeTargetEffect(2)); + mode.addEffect(new GainLifeEffect(2).concatBy("and")); + mode.addTarget(new TargetOpponent()); + this.getSpellAbility().addMode(mode); + } + + private WitherbloomCommand(final WitherbloomCommand card) { + super(card); + } + + @Override + public WitherbloomCommand copy() { + return new WitherbloomCommand(this); + } +} + +class WitherbloomCommandEffect extends OneShotEffect { + + WitherbloomCommandEffect() { + super(Outcome.ReturnToHand); + staticText = ", then you return a land card from your graveyard to your hand"; + } + + private WitherbloomCommandEffect(final WitherbloomCommandEffect effect) { + super(effect); + } + + @Override + public WitherbloomCommandEffect copy() { + return new WitherbloomCommandEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + TargetCard target = new TargetCardInYourGraveyard(1, StaticFilters.FILTER_CARD_LAND); + target.setNotTarget(true); + if (!target.canChoose(source.getSourceId(), source.getControllerId(), game)) { + return false; + } + player.choose(outcome, target, source.getSourceId(), game); + Card card = game.getCard(target.getFirstTarget()); + return card != null && player.moveCards(card, Zone.HAND, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WitherbloomPledgemage.java b/Mage.Sets/src/mage/cards/w/WitherbloomPledgemage.java new file mode 100644 index 000000000000..71d74e1561e6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WitherbloomPledgemage.java @@ -0,0 +1,38 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.effects.common.GainLifeEffect; +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 WitherbloomPledgemage extends CardImpl { + + public WitherbloomPledgemage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B/G}{B/G}"); + + this.subtype.add(SubType.TREEFOLK); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, you gain 1 life. + this.addAbility(new MagecraftAbility(new GainLifeEffect(1))); + } + + private WitherbloomPledgemage(final WitherbloomPledgemage card) { + super(card); + } + + @Override + public WitherbloomPledgemage copy() { + return new WitherbloomPledgemage(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/Withercrown.java b/Mage.Sets/src/mage/cards/w/Withercrown.java index ce6d35ee2cac..a9f2b533e6b7 100644 --- a/Mage.Sets/src/mage/cards/w/Withercrown.java +++ b/Mage.Sets/src/mage/cards/w/Withercrown.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.cards.w; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/w/WitnessTheEnd.java b/Mage.Sets/src/mage/cards/w/WitnessTheEnd.java index 2071bee4a1ca..b95098c6aa47 100644 --- a/Mage.Sets/src/mage/cards/w/WitnessTheEnd.java +++ b/Mage.Sets/src/mage/cards/w/WitnessTheEnd.java @@ -1,8 +1,5 @@ - package mage.cards.w; -import java.util.UUID; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.ExileFromZoneTargetEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.keyword.DevoidAbility; @@ -10,26 +7,28 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class WitnessTheEnd extends CardImpl { public WitnessTheEnd(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}"); // Devoid this.addAbility(new DevoidAbility(this.color)); + // Target opponent exiles two cards from their hand and loses 2 life. - getSpellAbility().addEffect(new ExileFromZoneTargetEffect(Zone.HAND, null, "", new FilterCard("cards"), 2)); - Effect effect = new LoseLifeTargetEffect(2); - effect.setText("and loses 2 life"); - getSpellAbility().addTarget(new TargetOpponent()); - getSpellAbility().addEffect(effect); + this.getSpellAbility().addEffect(new ExileFromZoneTargetEffect( + Zone.HAND, StaticFilters.FILTER_CARD_CARDS, 2, false + )); + this.getSpellAbility().addEffect(new LoseLifeTargetEffect(2).setText("and loses 2 life")); + this.getSpellAbility().addTarget(new TargetOpponent()); } private WitnessTheEnd(final WitnessTheEnd card) { diff --git a/Mage.Sets/src/mage/cards/w/WizardMentor.java b/Mage.Sets/src/mage/cards/w/WizardMentor.java index 702482bd921e..78c15081ed93 100644 --- a/Mage.Sets/src/mage/cards/w/WizardMentor.java +++ b/Mage.Sets/src/mage/cards/w/WizardMentor.java @@ -1,50 +1,39 @@ - package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.ReturnToHandSourceEffect; -import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; +import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.game.Game; +import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class WizardMentor extends CardImpl { - - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("another target creature"); - static { - filter.add(AnotherPredicate.instance); - } public WizardMentor(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WIZARD); this.power = new MageInt(2); this.toughness = new MageInt(2); // {tap}: Return Wizard Mentor and target creature you control to their owner's hand. - Effect effect = new ReturnToHandSourceEffect(true); - effect.setText("Return Wizard Mentor"); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); - ability.addEffect(effect); - effect = new ReturnToHandTargetEffect(); - ability.addEffect(effect); - effect.setText("and another target creature you control to their owners' hands"); - ability.addTarget(new TargetControlledCreaturePermanent(filter)); + Ability ability = new SimpleActivatedAbility(new WizardMentorEffect(), new TapSourceCost()); + ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); } @@ -57,3 +46,31 @@ public WizardMentor copy() { return new WizardMentor(this); } } + +class WizardMentorEffect extends OneShotEffect { + + WizardMentorEffect() { + super(Outcome.Benefit); + staticText = "return {this} and target creature you control to their owner's hand"; + } + + private WizardMentorEffect(final WizardMentorEffect effect) { + super(effect); + } + + @Override + public WizardMentorEffect copy() { + return new WizardMentorEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getSourceId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(source.getSourcePermanentIfItStillExists(game)); + cards.add(source.getFirstTarget()); + return player.moveCards(cards, Zone.HAND, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WoeStrider.java b/Mage.Sets/src/mage/cards/w/WoeStrider.java index 88b5e5e173ab..25828d07f555 100644 --- a/Mage.Sets/src/mage/cards/w/WoeStrider.java +++ b/Mage.Sets/src/mage/cards/w/WoeStrider.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.GoatToken; import mage.target.common.TargetControlledPermanent; diff --git a/Mage.Sets/src/mage/cards/w/WolfOfDevilsBreach.java b/Mage.Sets/src/mage/cards/w/WolfOfDevilsBreach.java index 628b3eccb496..f09250fcc5db 100644 --- a/Mage.Sets/src/mage/cards/w/WolfOfDevilsBreach.java +++ b/Mage.Sets/src/mage/cards/w/WolfOfDevilsBreach.java @@ -41,9 +41,9 @@ public WolfOfDevilsBreach(UUID ownerId, CardSetInfo setInfo) { toPay.add(new ManaCostsImpl<>("{1}{R}")); toPay.add(new DiscardCardCost()); Ability ability = new AttacksTriggeredAbility(new DoIfCostPaid(new DamageTargetEffect(new WolfOfDevilsBreachDiscardCostCardConvertedMana()), toPay, - "Pay {1}{R} and discard a card to let {this} do damage to target creature or planeswalker equal to the discarded card's converted mana cost?", true), false, + "Pay {1}{R} and discard a card to let {this} do damage to target creature or planeswalker equal to the discarded card's mana value?", true), false, "Whenever {this} attacks you may pay {1}{R} and discard a card. If you do, {this} deals damage to target creature or planeswalker " - + "equal to the discarded card's converted mana cost."); + + "equal to the discarded card's mana value."); ability.addTarget(new TargetCreatureOrPlaneswalker()); this.addAbility(ability); } @@ -72,7 +72,7 @@ public int calculate(Game game, Ability sourceAbility, Effect effect) { DiscardCardCost discardCost = (DiscardCardCost) cost; int cmc = 0; for (Card card : discardCost.getCards()) { - cmc += card.getConvertedManaCost(); + cmc += card.getManaValue(); } return cmc; } @@ -95,6 +95,6 @@ public String toString() { @Override public String getMessage() { - return "the discarded card's converted mana cost"; + return "the discarded card's mana value"; } } diff --git a/Mage.Sets/src/mage/cards/w/WolfbittenCaptive.java b/Mage.Sets/src/mage/cards/w/WolfbittenCaptive.java index 8eca2d646d42..692ca2c899b4 100644 --- a/Mage.Sets/src/mage/cards/w/WolfbittenCaptive.java +++ b/Mage.Sets/src/mage/cards/w/WolfbittenCaptive.java @@ -1,21 +1,19 @@ - package mage.cards.w; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.LimitedTimesPerTurnActivatedAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; + +import java.util.UUID; /** * @author Loki @@ -38,8 +36,7 @@ public WolfbittenCaptive(UUID ownerId, CardSetInfo setInfo) { // At the beginning of each upkeep, if no spells were cast last turn, transform Wolfbitten Captive. this.addAbility(new TransformAbility()); - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.instance, TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + this.addAbility(new WerewolfFrontTriggeredAbility()); } private WolfbittenCaptive(final WolfbittenCaptive card) { diff --git a/Mage.Sets/src/mage/cards/w/WolfwillowHaven.java b/Mage.Sets/src/mage/cards/w/WolfwillowHaven.java index 29d02cd5115f..5d5abd19b6b5 100644 --- a/Mage.Sets/src/mage/cards/w/WolfwillowHaven.java +++ b/Mage.Sets/src/mage/cards/w/WolfwillowHaven.java @@ -41,7 +41,7 @@ public WolfwillowHaven(UUID ownerId, CardSetInfo setInfo) { // Whenever enchanted land is tapped for mana, its controller adds an additional {G}. this.addAbility(new EnchantedTappedTriggeredManaAbility(new AddManaToManaPoolTargetControllerEffect( new Mana(ColoredManaSymbol.G), "their" - ))); + ).setText("its controller adds an additional {G}"))); // {4}{G}, Sacrifice Wolfwillow Haven: Create a 2/2 green Wolf creature token. Activate this ability only during your turn. ability = new ActivateIfConditionActivatedAbility( diff --git a/Mage.Sets/src/mage/cards/w/WolverineRiders.java b/Mage.Sets/src/mage/cards/w/WolverineRiders.java index 8c1143ca0c13..bfe059cd22a7 100644 --- a/Mage.Sets/src/mage/cards/w/WolverineRiders.java +++ b/Mage.Sets/src/mage/cards/w/WolverineRiders.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.ElfWarriorToken; diff --git a/Mage.Sets/src/mage/cards/w/WoodedFoothills.java b/Mage.Sets/src/mage/cards/w/WoodedFoothills.java index a4e5f55ee86a..cfc580933ff2 100644 --- a/Mage.Sets/src/mage/cards/w/WoodedFoothills.java +++ b/Mage.Sets/src/mage/cards/w/WoodedFoothills.java @@ -21,7 +21,7 @@ public WoodedFoothills(UUID ownerId, CardSetInfo setInfo) { this.frameColor = new ObjectColor("RG"); // {tap}, Pay 1 life, Sacrifice Wooded Foothills: Search your library for a Mountain or Forest card and put it onto the battlefield. Then shuffle your library. - this.addAbility(new FetchLandActivatedAbility(EnumSet.of(SubType.MOUNTAIN, SubType.FOREST))); + this.addAbility(new FetchLandActivatedAbility(SubType.MOUNTAIN, SubType.FOREST)); } private WoodedFoothills(final WoodedFoothills card) { diff --git a/Mage.Sets/src/mage/cards/w/WoodlandBellower.java b/Mage.Sets/src/mage/cards/w/WoodlandBellower.java index f33e3e546c79..eeea27be6cb6 100644 --- a/Mage.Sets/src/mage/cards/w/WoodlandBellower.java +++ b/Mage.Sets/src/mage/cards/w/WoodlandBellower.java @@ -1,43 +1,47 @@ - package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.ComparisonType; -import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; -import mage.game.Game; -import mage.players.Player; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; +import java.util.UUID; + /** - * * @author fireshoes */ public final class WoodlandBellower extends CardImpl { + private static final FilterCard filter = new FilterCard("nonlegendary green creature card with mana value 3 or less"); + + static { + filter.add(new ColorPredicate(ObjectColor.GREEN)); + filter.add(CardType.CREATURE.getPredicate()); + filter.add(Predicates.not(SuperType.LEGENDARY.getPredicate())); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); + } + public WoodlandBellower(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}"); this.subtype.add(SubType.BEAST); this.power = new MageInt(6); this.toughness = new MageInt(5); // When Woodland Bellower enters the battlefield, you may search your library for a nonlegendary green creature card with converted mana cost 3 or less, put it onto the battlefield, then shuffle your library. - this.addAbility(new EntersBattlefieldTriggeredAbility(new WoodlandBellowerEffect(), true)); + this.addAbility(new EntersBattlefieldTriggeredAbility( + new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter)), true + )); } private WoodlandBellower(final WoodlandBellower card) { @@ -49,45 +53,3 @@ public WoodlandBellower copy() { return new WoodlandBellower(this); } } - -class WoodlandBellowerEffect extends OneShotEffect { - - WoodlandBellowerEffect() { - super(Outcome.PutCreatureInPlay); - staticText = "Search your library for a nonlegendary green creature card with converted mana cost 3 or less, put it onto the battlefield, then shuffle your library"; - } - - WoodlandBellowerEffect(final WoodlandBellowerEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { - return false; - } - FilterCard filter = new FilterCard("nonlegendary green creature card with converted mana cost 3 or less"); - filter.add(new ColorPredicate(ObjectColor.GREEN)); - filter.add(CardType.CREATURE.getPredicate()); - filter.add(Predicates.not(SuperType.LEGENDARY.getPredicate())); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); - TargetCardInLibrary target = new TargetCardInLibrary(filter); - if (controller.searchLibrary(target, source, game)) { - if (!target.getTargets().isEmpty()) { - Card card = controller.getLibrary().getCard(target.getFirstTarget(), game); - controller.moveCards(card, Zone.BATTLEFIELD, source, game); - } - controller.shuffleLibrary(source, game); - return true; - } - controller.shuffleLibrary(source, game); - return false; - } - - @Override - public WoodlandBellowerEffect copy() { - return new WoodlandBellowerEffect(this); - } - -} diff --git a/Mage.Sets/src/mage/cards/w/WoodlandGuidance.java b/Mage.Sets/src/mage/cards/w/WoodlandGuidance.java index 5b4d6879489c..10b34414b0af 100644 --- a/Mage.Sets/src/mage/cards/w/WoodlandGuidance.java +++ b/Mage.Sets/src/mage/cards/w/WoodlandGuidance.java @@ -30,7 +30,7 @@ public WoodlandGuidance(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new DoIfClashWonEffect(new UntapAllLandsControllerEffect(new FilterLandPermanent(SubType.FOREST, "Forests")))); // Remove WoodlandGuidance from the game - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect().concatBy("
")); } private WoodlandGuidance(final WoodlandGuidance card) { diff --git a/Mage.Sets/src/mage/cards/w/WoodlandSleuth.java b/Mage.Sets/src/mage/cards/w/WoodlandSleuth.java index add2b8e09438..f0bbd1665f05 100644 --- a/Mage.Sets/src/mage/cards/w/WoodlandSleuth.java +++ b/Mage.Sets/src/mage/cards/w/WoodlandSleuth.java @@ -75,9 +75,10 @@ public boolean apply(Game game, Ability source) { Card[] cards = player.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURE, game).toArray(new Card[0]); if (cards.length > 0) { Card card = cards[RandomUtil.nextInt(cards.length)]; - card.moveToZone(Zone.HAND, source, game, true); - game.informPlayers(card.getName() + " returned to the hand of " + player.getLogName()); - return true; + if (player.moveCards(card, Zone.HAND, source, game)) { + game.informPlayers(card.getName() + " returned to the hand of " + player.getLogName()); + return true; + } } } return false; diff --git a/Mage.Sets/src/mage/cards/w/WookieeRaidleader.java b/Mage.Sets/src/mage/cards/w/WookieeRaidleader.java index 81f435b65776..0681c9ad256a 100644 --- a/Mage.Sets/src/mage/cards/w/WookieeRaidleader.java +++ b/Mage.Sets/src/mage/cards/w/WookieeRaidleader.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.constants.Duration; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** diff --git a/Mage.Sets/src/mage/cards/w/WordOfBlasting.java b/Mage.Sets/src/mage/cards/w/WordOfBlasting.java index f8ebdb26dfcb..0ba5e875a09c 100644 --- a/Mage.Sets/src/mage/cards/w/WordOfBlasting.java +++ b/Mage.Sets/src/mage/cards/w/WordOfBlasting.java @@ -2,7 +2,7 @@ package mage.cards.w; import java.util.UUID; -import mage.abilities.dynamicvalue.common.TargetConvertedManaCost; +import mage.abilities.dynamicvalue.common.TargetManaValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageTargetControllerEffect; import mage.abilities.effects.common.DestroyTargetEffect; @@ -30,8 +30,8 @@ public WordOfBlasting(UUID ownerId, CardSetInfo setInfo) { // Destroy target Wall. It can't be regenerated. Word of Blasting deals damage equal to that Wall's converted mana cost to the Wall's controller. this.getSpellAbility().addEffect(new DestroyTargetEffect(true)); - Effect effect = new DamageTargetControllerEffect(TargetConvertedManaCost.instance); - effect.setText("{this} deals damage equal to that Wall's converted mana cost to the Wall's controller"); + Effect effect = new DamageTargetControllerEffect(TargetManaValue.instance); + effect.setText("{this} deals damage equal to that Wall's mana value to the Wall's controller"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetPermanent(filter)); } diff --git a/Mage.Sets/src/mage/cards/w/WordsOfWind.java b/Mage.Sets/src/mage/cards/w/WordsOfWind.java index 7965982edabf..4544a0e2d9ac 100644 --- a/Mage.Sets/src/mage/cards/w/WordsOfWind.java +++ b/Mage.Sets/src/mage/cards/w/WordsOfWind.java @@ -1,4 +1,3 @@ - package mage.cards.w; import java.util.List; @@ -28,8 +27,7 @@ public final class WordsOfWind extends CardImpl { public WordsOfWind(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); // {1}: The next time you would draw a card this turn, each player returns a permanent they control to its owner's hand instead. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new WordsOfWindEffect(), new ManaCostsImpl("{1}"))); @@ -62,40 +60,40 @@ public WordsOfWindEffect copy() { } @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { + public boolean replaceEvent(GameEvent event, Ability source, Game game) { game.informPlayers("Each player returns a permanent they control to its owner's hand instead"); for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { Player player = game.getPlayer(playerId); if (player != null) { TargetControlledPermanent target = new TargetControlledPermanent(); List liste = game.getBattlefield().getActivePermanents(new FilterControlledPermanent(), playerId, game); - if(!liste.isEmpty()){ - while (!player.choose(Outcome.ReturnToHand, target, source.getSourceId(), game)){ + if (!liste.isEmpty()) { + while (!player.choose(Outcome.ReturnToHand, target, source.getSourceId(), game)) { if (!player.canRespond()) { return false; } } Permanent permanent = game.getPermanent(target.getFirstTarget()); if (permanent != null) { - permanent.moveToZone(Zone.HAND, source, game, false); + player.moveCards(permanent, Zone.HAND, source, game); } } } } - this.used = true; + this.used = true; discard(); return true; } - + @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.DRAW_CARD; - } - + } + @Override public boolean applies(GameEvent event, Ability source, Game game) { if (!this.used) { - return source.isControlledBy(event.getPlayerId()); + return source.isControlledBy(event.getPlayerId()); } return false; } diff --git a/Mage.Sets/src/mage/cards/w/WorkshopElders.java b/Mage.Sets/src/mage/cards/w/WorkshopElders.java index ba1fe0a5c8c1..db4284c9a684 100644 --- a/Mage.Sets/src/mage/cards/w/WorkshopElders.java +++ b/Mage.Sets/src/mage/cards/w/WorkshopElders.java @@ -17,7 +17,7 @@ import mage.constants.TargetController; import mage.counters.CounterType; import mage.filter.FilterPermanent; -import mage.filter.common.FilterArtifactCreaturePermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledArtifactPermanent; import mage.filter.predicate.Predicates; import mage.target.TargetPermanent; @@ -29,13 +29,11 @@ */ public final class WorkshopElders extends CardImpl { - private static final FilterPermanent filter - = new FilterArtifactCreaturePermanent("artifact creatures"); private static final FilterPermanent filter2 = new FilterControlledArtifactPermanent("noncreature artifact you control"); static { - filter.add(Predicates.not(CardType.CREATURE.getPredicate())); + filter2.add(Predicates.not(CardType.CREATURE.getPredicate())); } public WorkshopElders(UUID ownerId, CardSetInfo setInfo) { @@ -48,7 +46,8 @@ public WorkshopElders(UUID ownerId, CardSetInfo setInfo) { // Artifact creatures you control have flying. this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( - FlyingAbility.getInstance(), Duration.WhileOnBattlefield, filter + FlyingAbility.getInstance(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENTS_ARTIFACT_CREATURE ))); // At the beginning of combat on your turn, you may have target noncreature artifact you control become a 0/0 artifact creature. If you do, put four +1/+1 counters on it. diff --git a/Mage.Sets/src/mage/cards/w/WorldgorgerDragon.java b/Mage.Sets/src/mage/cards/w/WorldgorgerDragon.java index c3e8b176ec06..b86c549b73df 100644 --- a/Mage.Sets/src/mage/cards/w/WorldgorgerDragon.java +++ b/Mage.Sets/src/mage/cards/w/WorldgorgerDragon.java @@ -21,7 +21,7 @@ import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.ExileZone; import mage.game.Game; import mage.game.permanent.PermanentToken; diff --git a/Mage.Sets/src/mage/cards/w/WormfangDrake.java b/Mage.Sets/src/mage/cards/w/WormfangDrake.java index 38f5943c8d51..3d839dddab03 100644 --- a/Mage.Sets/src/mage/cards/w/WormfangDrake.java +++ b/Mage.Sets/src/mage/cards/w/WormfangDrake.java @@ -19,7 +19,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/w/WormfangNewt.java b/Mage.Sets/src/mage/cards/w/WormfangNewt.java index e1db3ff284ad..cffc82621795 100644 --- a/Mage.Sets/src/mage/cards/w/WormfangNewt.java +++ b/Mage.Sets/src/mage/cards/w/WormfangNewt.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledLandPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.Target; import mage.target.TargetPermanent; diff --git a/Mage.Sets/src/mage/cards/w/WormfangTurtle.java b/Mage.Sets/src/mage/cards/w/WormfangTurtle.java index 43301bfabc7f..2b79abb22f71 100644 --- a/Mage.Sets/src/mage/cards/w/WormfangTurtle.java +++ b/Mage.Sets/src/mage/cards/w/WormfangTurtle.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledLandPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.Target; import mage.target.TargetPermanent; diff --git a/Mage.Sets/src/mage/cards/w/WormholeSerpent.java b/Mage.Sets/src/mage/cards/w/WormholeSerpent.java new file mode 100644 index 000000000000..ec3b6e6623d9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WormholeSerpent.java @@ -0,0 +1,45 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.combat.CantBeBlockedTargetEffect; +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 TheElk801 + */ +public final class WormholeSerpent extends CardImpl { + + public WormholeSerpent(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}"); + + this.subtype.add(SubType.SERPENT); + this.power = new MageInt(3); + this.toughness = new MageInt(5); + + // {3}{U}: Target creature can't be blocked this turn. + Ability ability = new SimpleActivatedAbility( + new CantBeBlockedTargetEffect(Duration.EndOfTurn), new ManaCostsImpl("{3}{U}") + ); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private WormholeSerpent(final WormholeSerpent card) { + super(card); + } + + @Override + public WormholeSerpent copy() { + return new WormholeSerpent(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WorstFears.java b/Mage.Sets/src/mage/cards/w/WorstFears.java index 6e940e35aea1..1403f76857e5 100644 --- a/Mage.Sets/src/mage/cards/w/WorstFears.java +++ b/Mage.Sets/src/mage/cards/w/WorstFears.java @@ -22,7 +22,7 @@ public WorstFears(UUID ownerId, CardSetInfo setInfo) { // You control target player during that player's next turn. Exile Worst Fears. (You see all cards that player could see and make all decisions for that player.) this.getSpellAbility().addEffect(new ControlTargetPlayerNextTurnEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private WorstFears(final WorstFears card) { diff --git a/Mage.Sets/src/mage/cards/w/WretchedAnurid.java b/Mage.Sets/src/mage/cards/w/WretchedAnurid.java index 42819f66cf25..618a35ee8fc4 100644 --- a/Mage.Sets/src/mage/cards/w/WretchedAnurid.java +++ b/Mage.Sets/src/mage/cards/w/WretchedAnurid.java @@ -11,7 +11,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/w/WrexialTheRisenDeep.java b/Mage.Sets/src/mage/cards/w/WrexialTheRisenDeep.java index 45b5279a1720..2acdaa76228f 100644 --- a/Mage.Sets/src/mage/cards/w/WrexialTheRisenDeep.java +++ b/Mage.Sets/src/mage/cards/w/WrexialTheRisenDeep.java @@ -15,11 +15,10 @@ import mage.constants.*; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.game.events.DamagedPlayerEvent; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; import mage.game.stack.StackObject; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/x/XathridDemon.java b/Mage.Sets/src/mage/cards/x/XathridDemon.java index 609a69d9aae8..503eae7aa9c1 100644 --- a/Mage.Sets/src/mage/cards/x/XathridDemon.java +++ b/Mage.Sets/src/mage/cards/x/XathridDemon.java @@ -17,7 +17,7 @@ import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/x/XenagosGodOfRevels.java b/Mage.Sets/src/mage/cards/x/XenagosGodOfRevels.java index e0e485e3bb01..d1b0519ab7c0 100644 --- a/Mage.Sets/src/mage/cards/x/XenagosGodOfRevels.java +++ b/Mage.Sets/src/mage/cards/x/XenagosGodOfRevels.java @@ -16,7 +16,7 @@ import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; diff --git a/Mage.Sets/src/mage/cards/x/XenicPoltergeist.java b/Mage.Sets/src/mage/cards/x/XenicPoltergeist.java index b9f12027b29c..fba8185af5b8 100644 --- a/Mage.Sets/src/mage/cards/x/XenicPoltergeist.java +++ b/Mage.Sets/src/mage/cards/x/XenicPoltergeist.java @@ -68,7 +68,7 @@ class XenicPoltergeistEffect extends ContinuousEffectImpl { public XenicPoltergeistEffect() { super(Duration.Custom, Outcome.BecomeCreature); - staticText = "Until your next upkeep, target noncreature artifact becomes an artifact creature with power and toughness each equal to its converted mana cost"; + staticText = "Until your next upkeep, target noncreature artifact becomes an artifact creature with power and toughness each equal to its mana value"; } public XenicPoltergeistEffect(final XenicPoltergeistEffect effect) { @@ -113,7 +113,7 @@ public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) UUID permanentId = targetPointer.getFirst(game, source); Permanent permanent = game.getPermanentOrLKIBattlefield(permanentId); if (permanent != null) { - int manaCost = permanent.getConvertedManaCost(); + int manaCost = permanent.getManaValue(); permanent.getPower().setValue(manaCost); permanent.getToughness().setValue(manaCost); } diff --git a/Mage.Sets/src/mage/cards/y/YannikScavengingSentinel.java b/Mage.Sets/src/mage/cards/y/YannikScavengingSentinel.java index a0f527cdec40..c0f1d664a7e6 100644 --- a/Mage.Sets/src/mage/cards/y/YannikScavengingSentinel.java +++ b/Mage.Sets/src/mage/cards/y/YannikScavengingSentinel.java @@ -16,7 +16,7 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/y/YasharnImplacableEarth.java b/Mage.Sets/src/mage/cards/y/YasharnImplacableEarth.java index fd42d52c6d9c..2cd5ae54e3cc 100644 --- a/Mage.Sets/src/mage/cards/y/YasharnImplacableEarth.java +++ b/Mage.Sets/src/mage/cards/y/YasharnImplacableEarth.java @@ -40,7 +40,7 @@ public YasharnImplacableEarth(UUID ownerId, CardSetInfo setInfo) { this.addAbility(new EntersBattlefieldTriggeredAbility( new SearchLibraryPutInHandEffect(new YasharnImplacableEarthTarget(), true) .setText("search your library for a basic Forest card and a basic Plains card, " + - "reveal those cards, put them into your hand, then shuffle your library") + "reveal those cards, put them into your hand, then shuffle") )); // Players can't pay life or sacrifice nonland permanents to cast spells or activate abilities. diff --git a/Mage.Sets/src/mage/cards/y/YavimayaDryad.java b/Mage.Sets/src/mage/cards/y/YavimayaDryad.java index d7f5f71ee710..e125c1975c7a 100644 --- a/Mage.Sets/src/mage/cards/y/YavimayaDryad.java +++ b/Mage.Sets/src/mage/cards/y/YavimayaDryad.java @@ -60,7 +60,7 @@ class YavimayaDryadEffect extends SearchEffect { public YavimayaDryadEffect(TargetCardInLibrary target) { super(target, Outcome.PutLandInPlay); - staticText = "you may search your library for a Forest card and put it onto the battlefield tapped under target player's control. If you do, shuffle your library"; + staticText = "you may search your library for a Forest card and put it onto the battlefield tapped under target player's control. If you do, shuffle"; } public YavimayaDryadEffect(final YavimayaDryadEffect effect) { diff --git a/Mage.Sets/src/mage/cards/y/YawgmothsVileOffering.java b/Mage.Sets/src/mage/cards/y/YawgmothsVileOffering.java index d811465f2186..2237c9038cac 100644 --- a/Mage.Sets/src/mage/cards/y/YawgmothsVileOffering.java +++ b/Mage.Sets/src/mage/cards/y/YawgmothsVileOffering.java @@ -48,7 +48,7 @@ public YawgmothsVileOffering(UUID ownerId, CardSetInfo setInfo) { this.getSpellAbility().addEffect(new YawgmothsVileOfferingEffect()); this.getSpellAbility().addTarget(new TargetCardInGraveyard(0, 1, cardFilter)); this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker(0, 1, new FilterCreatureOrPlaneswalkerPermanent(), false)); - this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); } private YawgmothsVileOffering(final YawgmothsVileOffering card) { diff --git a/Mage.Sets/src/mage/cards/y/YedoraGraveGardener.java b/Mage.Sets/src/mage/cards/y/YedoraGraveGardener.java new file mode 100644 index 000000000000..21980b0fa563 --- /dev/null +++ b/Mage.Sets/src/mage/cards/y/YedoraGraveGardener.java @@ -0,0 +1,129 @@ +package mage.cards.y; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.mana.GreenManaAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class YedoraGraveGardener extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent("another nontoken creature you control"); + + static { + filter.add(Predicates.not(TokenPredicate.instance)); + filter.add(AnotherPredicate.instance); + } + + public YedoraGraveGardener(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.TREEFOLK); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Whenever another nontoken creature you control dies, you may return it to the battlefield face down under its owner's control. It's a Forest land. + this.addAbility(new DiesCreatureTriggeredAbility( + new YedoraGraveGardenerEffect(), true, filter, true + )); + } + + private YedoraGraveGardener(final YedoraGraveGardener card) { + super(card); + } + + @Override + public YedoraGraveGardener copy() { + return new YedoraGraveGardener(this); + } +} + +class YedoraGraveGardenerEffect extends OneShotEffect { + + YedoraGraveGardenerEffect() { + super(Outcome.Benefit); + staticText = "you may return it to the battlefield face down under its owner's control. " + + "It's a Forest land. (It has no other types or abilities.)"; + } + + private YedoraGraveGardenerEffect(final YedoraGraveGardenerEffect effect) { + super(effect); + } + + @Override + public YedoraGraveGardenerEffect copy() { + return new YedoraGraveGardenerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(getTargetPointer().getFirst(game, source)); + if (player == null || card == null) { + return false; + } + game.addEffect(new YedoraGraveGardenerContinuousEffect().setTargetPointer( + new FixedTarget(new MageObjectReference(card, game, 1)) + ), source); + player.moveCards( + card, Zone.BATTLEFIELD, source, game, + false, true, true, null + ); + return true; + } +} + +class YedoraGraveGardenerContinuousEffect extends ContinuousEffectImpl { + + public YedoraGraveGardenerContinuousEffect() { + super(Duration.Custom, Layer.CopyEffects_1, SubLayer.FaceDownEffects_1b, Outcome.Neutral); + } + + public YedoraGraveGardenerContinuousEffect(final YedoraGraveGardenerContinuousEffect effect) { + super(effect); + } + + @Override + public YedoraGraveGardenerContinuousEffect copy() { + return new YedoraGraveGardenerContinuousEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent target = game.getPermanent(targetPointer.getFirst(game, source)); + if (target == null || !target.isFaceDown(game)) { + discard(); + return false; + } + target.getSuperType().clear(); + target.getCardType().clear(); + target.removeAllSubTypes(game); + target.addCardType(CardType.LAND); + target.addSubType(game, SubType.FOREST); + target.removeAllAbilities(source.getSourceId(), game); + target.addAbility(new GreenManaAbility()); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/y/YennettCrypticSovereign.java b/Mage.Sets/src/mage/cards/y/YennettCrypticSovereign.java index 723368e56cb0..0caca7660010 100644 --- a/Mage.Sets/src/mage/cards/y/YennettCrypticSovereign.java +++ b/Mage.Sets/src/mage/cards/y/YennettCrypticSovereign.java @@ -66,7 +66,7 @@ public YennettCrypticSovereignEffect() { super(Outcome.Benefit); this.staticText = "reveal the top card of your library. " + "You may cast it without paying its mana cost " + - "if its converted mana cost is odd. " + + "if its mana value is odd. " + "If you don't cast it, draw a card."; } @@ -90,7 +90,7 @@ public boolean apply(Game game, Ability source) { return false; } player.revealCards(source, new CardsImpl(card), game); - if (card.getConvertedManaCost() % 2 == 1) { + if (card.getManaValue() % 2 == 1) { if (player.chooseUse(outcome, "Cast " + card.getLogName() + " without paying its mana cost?", source, game)) { player.cast(card.getSpellAbility(), game, true, new ApprovingObject(source, game)); } else { diff --git a/Mage.Sets/src/mage/cards/y/YisanTheWandererBard.java b/Mage.Sets/src/mage/cards/y/YisanTheWandererBard.java index 36aa367d50ab..3b6755bff9a8 100644 --- a/Mage.Sets/src/mage/cards/y/YisanTheWandererBard.java +++ b/Mage.Sets/src/mage/cards/y/YisanTheWandererBard.java @@ -20,7 +20,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -62,7 +62,7 @@ class YisanTheWandererBardEffect extends OneShotEffect { public YisanTheWandererBardEffect() { super(Outcome.Benefit); - this.staticText = "Search your library for a creature card with converted mana cost equal to the number of verse counters on {this}, put it onto the battlefield, then shuffle your library"; + this.staticText = "Search your library for a creature card with mana value equal to the number of verse counters on {this}, put it onto the battlefield, then shuffle"; } public YisanTheWandererBardEffect(final YisanTheWandererBardEffect effect) { @@ -80,8 +80,8 @@ public boolean apply(Game game, Ability source) { Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (sourcePermanent != null && controller != null) { int newConvertedCost = sourcePermanent.getCounters(game).getCount(CounterType.VERSE); - FilterCard filter = new FilterCard("creature card with converted mana cost " + newConvertedCost); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, newConvertedCost)); + FilterCard filter = new FilterCard("creature card with mana value " + newConvertedCost); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, newConvertedCost)); filter.add(CardType.CREATURE.getPredicate()); TargetCardInLibrary target = new TargetCardInLibrary(filter); if (controller.searchLibrary(target, source, game)) { diff --git a/Mage.Sets/src/mage/cards/y/YodaJediMaster.java b/Mage.Sets/src/mage/cards/y/YodaJediMaster.java index 4b86ae3aaee1..f1d3a4b06da4 100644 --- a/Mage.Sets/src/mage/cards/y/YodaJediMaster.java +++ b/Mage.Sets/src/mage/cards/y/YodaJediMaster.java @@ -22,7 +22,7 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.command.emblems.YodaEmblem; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/y/YomijiWhoBarsTheWay.java b/Mage.Sets/src/mage/cards/y/YomijiWhoBarsTheWay.java index 64227c7db51f..f7655b7cab33 100644 --- a/Mage.Sets/src/mage/cards/y/YomijiWhoBarsTheWay.java +++ b/Mage.Sets/src/mage/cards/y/YomijiWhoBarsTheWay.java @@ -12,7 +12,7 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * diff --git a/Mage.Sets/src/mage/cards/y/YorionSkyNomad.java b/Mage.Sets/src/mage/cards/y/YorionSkyNomad.java index b808b8c61626..a25c2e61cbf0 100644 --- a/Mage.Sets/src/mage/cards/y/YorionSkyNomad.java +++ b/Mage.Sets/src/mage/cards/y/YorionSkyNomad.java @@ -16,7 +16,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.PermanentCard; import mage.game.permanent.PermanentMeld; diff --git a/Mage.Sets/src/mage/cards/y/YorvoLordOfGarenbrig.java b/Mage.Sets/src/mage/cards/y/YorvoLordOfGarenbrig.java index 00df41a828af..a7e73119e4c7 100644 --- a/Mage.Sets/src/mage/cards/y/YorvoLordOfGarenbrig.java +++ b/Mage.Sets/src/mage/cards/y/YorvoLordOfGarenbrig.java @@ -14,7 +14,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage.Sets/src/mage/cards/y/YouthfulValkyrie.java b/Mage.Sets/src/mage/cards/y/YouthfulValkyrie.java index 34367405b76a..86aa57f3429a 100644 --- a/Mage.Sets/src/mage/cards/y/YouthfulValkyrie.java +++ b/Mage.Sets/src/mage/cards/y/YouthfulValkyrie.java @@ -11,7 +11,7 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/y/YurikoTheTigersShadow.java b/Mage.Sets/src/mage/cards/y/YurikoTheTigersShadow.java index ab27d1a9f631..1fd2058ebf02 100644 --- a/Mage.Sets/src/mage/cards/y/YurikoTheTigersShadow.java +++ b/Mage.Sets/src/mage/cards/y/YurikoTheTigersShadow.java @@ -71,7 +71,7 @@ public YurikoTheTigersShadowEffect() { super(Outcome.Benefit); this.staticText = "reveal the top card of your library " + "and put that card into your hand. Each opponent loses life " - + "equal to that card's converted mana cost"; + + "equal to that card's mana value"; } public YurikoTheTigersShadowEffect(final YurikoTheTigersShadowEffect effect) { @@ -96,7 +96,7 @@ public boolean apply(Game game, Ability source) { player.revealCards(source, new CardsImpl(card), game); player.moveCards(card, Zone.HAND, source, game); return new LoseLifeOpponentsEffect( - card.getConvertedManaCost() + card.getManaValue() ).apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/z/ZadaHedronGrinder.java b/Mage.Sets/src/mage/cards/z/ZadaHedronGrinder.java index 610271044140..85bd7a3a4ba2 100644 --- a/Mage.Sets/src/mage/cards/z/ZadaHedronGrinder.java +++ b/Mage.Sets/src/mage/cards/z/ZadaHedronGrinder.java @@ -1,26 +1,33 @@ package mage.cards.z; -import java.util.UUID; import mage.MageInt; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.common.CopySpellForEachItCouldTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.FilterInPlay; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.filter.predicate.mageobject.MageObjectReferencePredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.game.stack.Spell; +import mage.game.stack.StackObject; import mage.players.Player; import mage.target.Target; import mage.util.TargetAddress; +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + /** - * * @author LevelX2 */ public final class ZadaHedronGrinder extends CardImpl { @@ -32,11 +39,10 @@ public ZadaHedronGrinder(UUID ownerId, CardSetInfo setInfo) { this.power = new MageInt(3); this.toughness = new MageInt(3); - // Whenever you cast an instant or sorcery spell that targets only Zada, Hedron Grinder, - // copy that spell for each other creature you control that the spell could target. + // Whenever you cast an instant or sorcery spell that targets only Zada, Hedron Grinder, + // copy that spell for each other creature you control that the spell could target. // Each copy targets a different one of those creatures. this.addAbility(new ZadaHedronGrinderTriggeredAbility()); - } private ZadaHedronGrinder(final ZadaHedronGrinder card) { @@ -55,7 +61,7 @@ class ZadaHedronGrinderTriggeredAbility extends TriggeredAbilityImpl { super(Zone.BATTLEFIELD, new ZadaHedronGrinderCopySpellEffect(), false); } - ZadaHedronGrinderTriggeredAbility(final ZadaHedronGrinderTriggeredAbility ability) { + private ZadaHedronGrinderTriggeredAbility(final ZadaHedronGrinderTriggeredAbility ability) { super(ability); } @@ -71,40 +77,38 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { - Spell spell = game.getStack().getSpell(event.getTargetId()); - return checkSpell(spell, game) - && event.getPlayerId().equals(controllerId); - } - - private boolean checkSpell(Spell spell, Game game) { - if (spell != null - && (spell.isInstant() - || spell.isSorcery())) { - boolean noTargets = true; - for (TargetAddress addr : TargetAddress.walk(spell)) { - if (addr != null) { - noTargets = false; - Target targetInstance = addr.getTarget(spell); - if (targetInstance != null) { - for (UUID target : targetInstance.getTargets()) { - if (target != null) { - Permanent permanent = game.getPermanent(target); - if (permanent == null - || !permanent.getId().equals(getSourceId())) { - return false; - } - } - } - } - } + if (!isControlledBy(event.getPlayerId())) { + return false; + } + Spell spell = game.getSpell(event.getTargetId()); + if (spell == null || !spell.isInstantOrSorcery()) { + return false; + } + boolean noTargets = true; + for (TargetAddress addr : TargetAddress.walk(spell)) { + if (addr == null) { + continue; + } + noTargets = false; + Target targetInstance = addr.getTarget(spell); + if (targetInstance == null) { + continue; } - if (noTargets) { - return false; + for (UUID target : targetInstance.getTargets()) { + if (target == null) { + continue; + } + Permanent permanent = game.getPermanent(target); + if (permanent == null || !permanent.getId().equals(getSourceId())) { + return false; + } } - getEffects().get(0).setValue("triggeringSpell", spell); - return true; } - return false; + if (noTargets) { + return false; + } + getEffects().setValue("triggeringSpell", spell); + return true; } @Override @@ -115,55 +119,40 @@ public String getRule() { } } -class ZadaHedronGrinderCopySpellEffect extends CopySpellForEachItCouldTargetEffect { +class ZadaHedronGrinderCopySpellEffect extends CopySpellForEachItCouldTargetEffect { - public ZadaHedronGrinderCopySpellEffect() { - this(new FilterControlledCreaturePermanent()); - this.staticText = "copy that spell for each other creature you control " - + "that the spell could target. Each copy targets a different one of those creatures."; + ZadaHedronGrinderCopySpellEffect() { + super(); } - public ZadaHedronGrinderCopySpellEffect(ZadaHedronGrinderCopySpellEffect effect) { + private ZadaHedronGrinderCopySpellEffect(final ZadaHedronGrinderCopySpellEffect effect) { super(effect); } - private ZadaHedronGrinderCopySpellEffect(FilterInPlay filter) { - super(filter); - } - @Override protected Player getPlayer(Game game, Ability source) { - Spell spell = getSpell(game, source); - if (spell != null) { - return game.getPlayer(spell.getControllerId()); - } - return null; - } - - @Override - protected Spell getSpell(Game game, Ability source) { - return (Spell) getValue("triggeringSpell"); + return game.getPlayer(source.getControllerId()); } @Override - protected boolean changeTarget(Target target, Game game, Ability source) { - return true; - } - - @Override - protected void modifyCopy(Spell copy, Game game, Ability source) { - Spell spell = getSpell(game, source); - copy.setControllerId(spell.getControllerId()); + protected List getPossibleTargets(StackObject stackObject, Player player, Ability source, Game game) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + return game.getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_CONTROLLED_CREATURE, + player.getId(), source.getSourceId(), game + ).stream() + .filter(Objects::nonNull) + .filter(p -> !p.equals(permanent)) + .filter(p -> stackObject.canTarget(game, p.getId())) + .map(p -> new MageObjectReference(p, game)) + .map(MageObjectReferencePredicate::new) + .collect(Collectors.toList()); } @Override - protected boolean okUUIDToCopyFor(UUID potentialTarget, Game game, Ability source, Spell spell) { - Permanent permanent = game.getPermanent(potentialTarget); - if (permanent == null - || !permanent.isControlledBy(spell.getControllerId())) { - return false; - } - return true; + protected Spell getStackObject(Game game, Ability source) { + return (Spell) getValue("triggeringSpell"); } @Override diff --git a/Mage.Sets/src/mage/cards/z/ZaffaiThunderConductor.java b/Mage.Sets/src/mage/cards/z/ZaffaiThunderConductor.java new file mode 100644 index 000000000000..3e1e797fbd74 --- /dev/null +++ b/Mage.Sets/src/mage/cards/z/ZaffaiThunderConductor.java @@ -0,0 +1,88 @@ +package mage.cards.z; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.game.Game; +import mage.game.permanent.token.PrismariToken; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ZaffaiThunderConductor extends CardImpl { + + public ZaffaiThunderConductor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, scry 1. If that spell's mana value is 5 or greater, create a 4/4 blue and red Elemental creature token. If that spell's mana value is 10 or greater, Zaffai, Thunder Conductor deals 10 damage to an opponent chosen at random. + this.addAbility(new MagecraftAbility(new ZaffaiThunderConductorEffect())); + } + + private ZaffaiThunderConductor(final ZaffaiThunderConductor card) { + super(card); + } + + @Override + public ZaffaiThunderConductor copy() { + return new ZaffaiThunderConductor(this); + } +} + +class ZaffaiThunderConductorEffect extends OneShotEffect { + + ZaffaiThunderConductorEffect() { + super(Outcome.Benefit); + staticText = "scry 1. If that spell's mana value is 5 or greater, " + + "create a 4/4 blue and red Elemental creature token. If that spell's mana value is 10 or greater, " + + "{this} deals 10 damage to an opponent chosen at random"; + } + + private ZaffaiThunderConductorEffect(final ZaffaiThunderConductorEffect effect) { + super(effect); + } + + @Override + public ZaffaiThunderConductorEffect copy() { + return new ZaffaiThunderConductorEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + player.scry(1, source, game); + Spell spell = (Spell) getValue(MagecraftAbility.SPELL_KEY); + if (spell == null || spell.getManaValue() < 5) { + return false; + } + new PrismariToken().putOntoBattlefield(1, game, source, source.getControllerId()); + if (spell.getManaValue() < 10) { + return true; + } + TargetOpponent target = new TargetOpponent(true); + player.chooseTarget(outcome, target, source, game); + Player opponent = game.getPlayer(target.getFirstTarget()); + opponent.damage(10, source.getSourceId(), source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/z/ZagrasThiefOfHeartbeats.java b/Mage.Sets/src/mage/cards/z/ZagrasThiefOfHeartbeats.java index 3dd08a71ed5a..f6ccdac87826 100644 --- a/Mage.Sets/src/mage/cards/z/ZagrasThiefOfHeartbeats.java +++ b/Mage.Sets/src/mage/cards/z/ZagrasThiefOfHeartbeats.java @@ -16,7 +16,7 @@ import mage.constants.*; import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.events.DamagedPlaneswalkerEvent; +import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; @@ -88,12 +88,12 @@ public ZagrasThiefOfHeartbeatsTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (!((DamagedPlaneswalkerEvent) event).isCombatDamage()) { + if (!((DamagedEvent) event).isCombatDamage()) { return false; } Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); @@ -102,6 +102,10 @@ public boolean checkTrigger(GameEvent event, Game game) { || !permanent.isControlledBy(getControllerId())) { return false; } + Permanent damaged = game.getPermanentOrLKIBattlefield(event.getTargetId()); + if (damaged == null || !permanent.isPlaneswalker()) { + return false; + } this.getEffects().clear(); this.addEffect(new DestroyTargetEffect().setTargetPointer(new FixedTarget(event.getTargetId(), game))); return true; diff --git a/Mage.Sets/src/mage/cards/z/ZarethSanTheTrickster.java b/Mage.Sets/src/mage/cards/z/ZarethSanTheTrickster.java index 6f9caa8dcfdc..e4e3b1a21999 100644 --- a/Mage.Sets/src/mage/cards/z/ZarethSanTheTrickster.java +++ b/Mage.Sets/src/mage/cards/z/ZarethSanTheTrickster.java @@ -16,7 +16,7 @@ import mage.filter.FilterCard; import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.filter.predicate.permanent.UnblockedPredicate; import mage.game.Game; import mage.game.events.DamagedEvent; diff --git a/Mage.Sets/src/mage/cards/z/ZeganaUtopianSpeaker.java b/Mage.Sets/src/mage/cards/z/ZeganaUtopianSpeaker.java index 44f82c559eeb..3056f65c7a6e 100644 --- a/Mage.Sets/src/mage/cards/z/ZeganaUtopianSpeaker.java +++ b/Mage.Sets/src/mage/cards/z/ZeganaUtopianSpeaker.java @@ -15,7 +15,7 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/z/ZephyrBoots.java b/Mage.Sets/src/mage/cards/z/ZephyrBoots.java new file mode 100644 index 000000000000..984af9fb599d --- /dev/null +++ b/Mage.Sets/src/mage/cards/z/ZephyrBoots.java @@ -0,0 +1,50 @@ +package mage.cards.z; + +import mage.abilities.common.DealsDamageToAPlayerAttachedTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.DrawDiscardControllerEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ZephyrBoots extends CardImpl { + + public ZephyrBoots(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Equipped creature has flying. + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( + FlyingAbility.getInstance(), AttachmentType.EQUIPMENT + ))); + + // Whenever equipped creature deals combat damage to a player, draw a card, then discard a card. + this.addAbility(new DealsDamageToAPlayerAttachedTriggeredAbility( + new DrawDiscardControllerEffect(1, 1), + "equipped creature", false + )); + + // Equip {2} + this.addAbility(new EquipAbility(2)); + } + + private ZephyrBoots(final ZephyrBoots card) { + super(card); + } + + @Override + public ZephyrBoots copy() { + return new ZephyrBoots(this); + } +} diff --git a/Mage.Sets/src/mage/cards/z/ZimoneQuandrixProdigy.java b/Mage.Sets/src/mage/cards/z/ZimoneQuandrixProdigy.java new file mode 100644 index 000000000000..a4173f1c1e5b --- /dev/null +++ b/Mage.Sets/src/mage/cards/z/ZimoneQuandrixProdigy.java @@ -0,0 +1,70 @@ +package mage.cards.z; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.PutCardFromHandOntoBattlefieldEffect; +import mage.abilities.hint.common.LandsYouControlHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ZimoneQuandrixProdigy extends CardImpl { + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, ComparisonType.MORE_THAN, 7 + ); + + public ZimoneQuandrixProdigy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // {1}, {T}: You may put a land card from your hand onto the battlefield tapped. + Ability ability = new SimpleActivatedAbility( + new PutCardFromHandOntoBattlefieldEffect( + StaticFilters.FILTER_CARD_LAND_A, false, true + ), new GenericManaCost(1) + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + + // {4}, {T}: Draw a card. If you control eight or more lands, draw two cards instead. + ability = new SimpleActivatedAbility(new ConditionalOneShotEffect( + new DrawCardSourceControllerEffect(2), + new DrawCardSourceControllerEffect(1), + condition, "draw a card. If you control " + + "eight or more lands, draw two cards instead" + ), new GenericManaCost(4)); + ability.addCost(new TapSourceCost()); + this.addAbility(ability.addHint(LandsYouControlHint.instance)); + } + + private ZimoneQuandrixProdigy(final ZimoneQuandrixProdigy card) { + super(card); + } + + @Override + public ZimoneQuandrixProdigy copy() { + return new ZimoneQuandrixProdigy(this); + } +} diff --git a/Mage.Sets/src/mage/cards/z/ZirdaTheDawnwaker.java b/Mage.Sets/src/mage/cards/z/ZirdaTheDawnwaker.java index 1efe933963a3..065c655da323 100644 --- a/Mage.Sets/src/mage/cards/z/ZirdaTheDawnwaker.java +++ b/Mage.Sets/src/mage/cards/z/ZirdaTheDawnwaker.java @@ -2,8 +2,8 @@ import mage.MageInt; import mage.MageObject; -import mage.Mana; import mage.abilities.Ability; +import mage.abilities.ActivatedAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.TapSourceCost; @@ -20,6 +20,7 @@ import mage.target.common.TargetCreaturePermanent; import mage.util.CardUtil; +import java.util.Collection; import java.util.Set; import java.util.UUID; @@ -40,7 +41,7 @@ public ZirdaTheDawnwaker(UUID ownerId, CardSetInfo setInfo) { // Companion — Each permanent card in your starting deck has an activated ability. this.addAbility(new CompanionAbility(ZirdaTheDawnwakerCompanionCondition.instance)); - // Abilities you activate that aren't mana abilities cost {2} less to activate. + // Abilities you activate that aren't mana abilities cost {2} less to activate. // This effect can't reduce the mana in that cost to less than one mana. this.addAbility(new SimpleStaticAbility(new ZirdaTheDawnwakerEffect())); @@ -76,20 +77,16 @@ public boolean isLegal(Set deck, int startingSize) { return deck .stream() .filter(MageObject::isPermanent) - .allMatch(card -> card - .getAbilities() - .stream() - .map(Ability::getAbilityType) - .anyMatch(abilityType -> abilityType == AbilityType.ACTIVATED - || abilityType == AbilityType.MANA) - ); + .map(MageObject::getAbilities) + .flatMap(Collection::stream) + .anyMatch(ActivatedAbility.class::isInstance); } } class ZirdaTheDawnwakerEffect extends CostModificationEffectImpl { ZirdaTheDawnwakerEffect() { - super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST); + super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST); staticText = "Abilities you activate that aren't mana abilities cost {2} less to activate. " + "This effect can't reduce the mana in that cost to less than one mana"; } @@ -100,8 +97,10 @@ private ZirdaTheDawnwakerEffect(final ZirdaTheDawnwakerEffect effect) { @Override public boolean apply(Game game, Ability source, Ability abilityToModify) { - int reduceMax = CardUtil.calculateActualPossibleGenericManaReduction(abilityToModify.getManaCostsToPay().getMana(), 2, 1); - if (reduceMax <= 0) { + int reduceMax = CardUtil.calculateActualPossibleGenericManaReduction( + abilityToModify.getManaCostsToPay().getMana(), 2, 1 + ); + if (reduceMax < 1) { return true; } CardUtil.reduceCost(abilityToModify, reduceMax); @@ -112,8 +111,7 @@ public boolean apply(Game game, Ability source, Ability abilityToModify) { @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { return abilityToModify.getAbilityType() == AbilityType.ACTIVATED - && abilityToModify.isControlledBy(source.getControllerId()) - && abilityToModify.getAbilityType() != AbilityType.MANA; // no mana abilities + && abilityToModify.isControlledBy(source.getControllerId()); } @Override diff --git a/Mage.Sets/src/mage/cards/z/ZirilanOfTheClaw.java b/Mage.Sets/src/mage/cards/z/ZirilanOfTheClaw.java index 76e3e695807f..41447b94678b 100644 --- a/Mage.Sets/src/mage/cards/z/ZirilanOfTheClaw.java +++ b/Mage.Sets/src/mage/cards/z/ZirilanOfTheClaw.java @@ -59,7 +59,7 @@ class ZirilanOfTheClawEffect extends OneShotEffect { public ZirilanOfTheClawEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "Search your library for a Dragon permanent card and put that card onto the battlefield. Then shuffle your library." + this.staticText = "Search your library for a Dragon permanent card and put that card onto the battlefield. Then shuffle." + " That Dragon gains haste until end of turn. Exile it at the beginning of the next end step"; } diff --git a/Mage.Sets/src/mage/cards/z/ZombieCannibal.java b/Mage.Sets/src/mage/cards/z/ZombieCannibal.java index cda76f02e037..d63ee87cd864 100644 --- a/Mage.Sets/src/mage/cards/z/ZombieCannibal.java +++ b/Mage.Sets/src/mage/cards/z/ZombieCannibal.java @@ -10,7 +10,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.game.events.DamagedPlayerEvent; import mage.game.events.GameEvent; diff --git a/Mage.Sets/src/mage/cards/z/ZurTheEnchanter.java b/Mage.Sets/src/mage/cards/z/ZurTheEnchanter.java index 73d9e05d2c1f..776e894fad8f 100644 --- a/Mage.Sets/src/mage/cards/z/ZurTheEnchanter.java +++ b/Mage.Sets/src/mage/cards/z/ZurTheEnchanter.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; /** @@ -22,11 +22,11 @@ */ public final class ZurTheEnchanter extends CardImpl { - private static final FilterCard filter = new FilterCard("enchantment card with converted mana cost 3 or less"); + private static final FilterCard filter = new FilterCard("enchantment card with mana value 3 or less"); static { filter.add(CardType.ENCHANTMENT.getPredicate()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } public ZurTheEnchanter(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/z/ZurzothChaosRider.java b/Mage.Sets/src/mage/cards/z/ZurzothChaosRider.java index 1afcb0d7a875..dde64936c3ba 100644 --- a/Mage.Sets/src/mage/cards/z/ZurzothChaosRider.java +++ b/Mage.Sets/src/mage/cards/z/ZurzothChaosRider.java @@ -121,6 +121,7 @@ public boolean checkTrigger(GameEvent event, Game game) { .stream() .map(game::getPermanent) .filter(Objects::nonNull) + .filter(permanent -> permanent.isControlledBy(this.getControllerId())) .filter(permanent -> permanent.hasSubtype(SubType.DEVIL, game)) .map(MageItem::getId) .map(game.getCombat()::getDefenderId) @@ -131,7 +132,7 @@ public boolean checkTrigger(GameEvent event, Game game) { if (playerIds.isEmpty()) { return false; } - playerIds.add(getControllerId()); + playerIds.add(this.getControllerId()); this.getEffects().clear(); this.addEffect(new ZurzothChaosRiderEffect(playerIds)); return true; diff --git a/Mage.Sets/src/mage/sets/Commander2021Edition.java b/Mage.Sets/src/mage/sets/Commander2021Edition.java new file mode 100644 index 000000000000..d5c3aaafd4d4 --- /dev/null +++ b/Mage.Sets/src/mage/sets/Commander2021Edition.java @@ -0,0 +1,352 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * @author TheElk801 + */ +public final class Commander2021Edition extends ExpansionSet { + + private static final Commander2021Edition instance = new Commander2021Edition(); + + public static Commander2021Edition getInstance() { + return instance; + } + + private Commander2021Edition() { + super("Commander 2021 Edition", "C21", ExpansionSet.buildDate(2021, 4, 23), SetType.SUPPLEMENTAL); + this.blockName = "Command Zone"; + this.hasBasicLands = false; + + cards.add(new SetCardInfo("Adrix and Nev, Twincasters", 9, Rarity.MYTHIC, mage.cards.a.AdrixAndNevTwincasters.class)); + cards.add(new SetCardInfo("Aether Gale", 113, Rarity.RARE, mage.cards.a.AetherGale.class)); + cards.add(new SetCardInfo("Aetherspouts", 114, Rarity.RARE, mage.cards.a.Aetherspouts.class)); + cards.add(new SetCardInfo("Ageless Entity", 184, Rarity.RARE, mage.cards.a.AgelessEntity.class)); + cards.add(new SetCardInfo("Alhammarret's Archive", 233, Rarity.MYTHIC, mage.cards.a.AlhammarretsArchive.class)); + cards.add(new SetCardInfo("Alibou, Ancient Witness", 7, Rarity.MYTHIC, mage.cards.a.AlibouAncientWitness.class)); + cards.add(new SetCardInfo("Ambition's Cost", 134, Rarity.UNCOMMON, mage.cards.a.AmbitionsCost.class)); + cards.add(new SetCardInfo("Ancient Craving", 135, Rarity.UNCOMMON, mage.cards.a.AncientCraving.class)); + cards.add(new SetCardInfo("Ancient Den", 276, Rarity.COMMON, mage.cards.a.AncientDen.class)); + cards.add(new SetCardInfo("Angel of Serenity", 83, Rarity.MYTHIC, mage.cards.a.AngelOfSerenity.class)); + cards.add(new SetCardInfo("Angel of the Ruins", 11, Rarity.RARE, mage.cards.a.AngelOfTheRuins.class)); + cards.add(new SetCardInfo("Apex of Power", 158, Rarity.MYTHIC, mage.cards.a.ApexOfPower.class)); + cards.add(new SetCardInfo("Arashi, the Sky Asunder", 185, Rarity.RARE, mage.cards.a.ArashiTheSkyAsunder.class)); + cards.add(new SetCardInfo("Arcane Signet", 234, Rarity.COMMON, mage.cards.a.ArcaneSignet.class)); + cards.add(new SetCardInfo("Archaeomancer's Map", 12, Rarity.RARE, mage.cards.a.ArchaeomancersMap.class)); + cards.add(new SetCardInfo("Audacious Reshapers", 47, Rarity.RARE, mage.cards.a.AudaciousReshapers.class)); + cards.add(new SetCardInfo("Author of Shadows", 35, Rarity.RARE, mage.cards.a.AuthorOfShadows.class)); + cards.add(new SetCardInfo("Barren Moor", 277, Rarity.UNCOMMON, mage.cards.b.BarrenMoor.class)); + cards.add(new SetCardInfo("Battlefield Forge", 278, Rarity.RARE, mage.cards.b.BattlefieldForge.class)); + cards.add(new SetCardInfo("Battlemage's Bracers", 48, Rarity.RARE, mage.cards.b.BattlemagesBracers.class)); + cards.add(new SetCardInfo("Beast Within", 186, Rarity.UNCOMMON, mage.cards.b.BeastWithin.class)); + cards.add(new SetCardInfo("Biomass Mutation", 209, Rarity.RARE, mage.cards.b.BiomassMutation.class)); + cards.add(new SetCardInfo("Blasphemous Act", 159, Rarity.RARE, mage.cards.b.BlasphemousAct.class)); + cards.add(new SetCardInfo("Blight Mound", 36, Rarity.RARE, mage.cards.b.BlightMound.class)); + cards.add(new SetCardInfo("Blighted Cataract", 279, Rarity.UNCOMMON, mage.cards.b.BlightedCataract.class)); + cards.add(new SetCardInfo("Blighted Woodland", 280, Rarity.UNCOMMON, mage.cards.b.BlightedWoodland.class)); + cards.add(new SetCardInfo("Bloodthirsty Aerialist", 136, Rarity.UNCOMMON, mage.cards.b.BloodthirstyAerialist.class)); + cards.add(new SetCardInfo("Bloodthirsty Blade", 235, Rarity.UNCOMMON, mage.cards.b.BloodthirstyBlade.class)); + cards.add(new SetCardInfo("Bloodtracker", 137, Rarity.RARE, mage.cards.b.Bloodtracker.class)); + cards.add(new SetCardInfo("Blossoming Bogbeast", 59, Rarity.RARE, mage.cards.b.BlossomingBogbeast.class)); + cards.add(new SetCardInfo("Bojuka Bog", 281, Rarity.COMMON, mage.cards.b.BojukaBog.class)); + cards.add(new SetCardInfo("Bold Plagiarist", 37, Rarity.RARE, mage.cards.b.BoldPlagiarist.class)); + cards.add(new SetCardInfo("Boreas Charger", 84, Rarity.RARE, mage.cards.b.BoreasCharger.class)); + cards.add(new SetCardInfo("Boros Charm", 210, Rarity.UNCOMMON, mage.cards.b.BorosCharm.class)); + cards.add(new SetCardInfo("Boros Garrison", 282, Rarity.UNCOMMON, mage.cards.b.BorosGarrison.class)); + cards.add(new SetCardInfo("Boros Locket", 236, Rarity.COMMON, mage.cards.b.BorosLocket.class)); + cards.add(new SetCardInfo("Bosh, Iron Golem", 237, Rarity.RARE, mage.cards.b.BoshIronGolem.class)); + cards.add(new SetCardInfo("Brainstorm", 115, Rarity.COMMON, mage.cards.b.Brainstorm.class)); + cards.add(new SetCardInfo("Brass's Bounty", 160, Rarity.RARE, mage.cards.b.BrasssBounty.class)); + cards.add(new SetCardInfo("Breena, the Demagogue", 1, Rarity.MYTHIC, mage.cards.b.BreenaTheDemagogue.class)); + cards.add(new SetCardInfo("Bronze Guardian", 13, Rarity.RARE, mage.cards.b.BronzeGuardian.class)); + cards.add(new SetCardInfo("Burnished Hart", 238, Rarity.UNCOMMON, mage.cards.b.BurnishedHart.class)); + cards.add(new SetCardInfo("Call the Skybreaker", 211, Rarity.RARE, mage.cards.c.CallTheSkybreaker.class)); + cards.add(new SetCardInfo("Caves of Koilos", 283, Rarity.RARE, mage.cards.c.CavesOfKoilos.class)); + cards.add(new SetCardInfo("Chain Reaction", 161, Rarity.RARE, mage.cards.c.ChainReaction.class)); + cards.add(new SetCardInfo("Champion of Wits", 116, Rarity.RARE, mage.cards.c.ChampionOfWits.class)); + cards.add(new SetCardInfo("Charmbreaker Devils", 162, Rarity.RARE, mage.cards.c.CharmbreakerDevils.class)); + cards.add(new SetCardInfo("Citadel Siege", 85, Rarity.RARE, mage.cards.c.CitadelSiege.class)); + cards.add(new SetCardInfo("Cleansing Nova", 86, Rarity.RARE, mage.cards.c.CleansingNova.class)); + cards.add(new SetCardInfo("Coiling Oracle", 212, Rarity.COMMON, mage.cards.c.CoilingOracle.class)); + cards.add(new SetCardInfo("Combat Calligrapher", 14, Rarity.RARE, mage.cards.c.CombatCalligrapher.class)); + cards.add(new SetCardInfo("Combustible Gearhulk", 163, Rarity.MYTHIC, mage.cards.c.CombustibleGearhulk.class)); + cards.add(new SetCardInfo("Command Tower", 284, Rarity.COMMON, mage.cards.c.CommandTower.class)); + cards.add(new SetCardInfo("Commander's Insight", 23, Rarity.RARE, mage.cards.c.CommandersInsight.class)); + cards.add(new SetCardInfo("Commander's Sphere", 239, Rarity.COMMON, mage.cards.c.CommandersSphere.class)); + cards.add(new SetCardInfo("Coveted Jewel", 240, Rarity.RARE, mage.cards.c.CovetedJewel.class)); + cards.add(new SetCardInfo("Crackling Drake", 213, Rarity.UNCOMMON, mage.cards.c.CracklingDrake.class)); + cards.add(new SetCardInfo("Crafty Cutpurse", 117, Rarity.RARE, mage.cards.c.CraftyCutpurse.class)); + cards.add(new SetCardInfo("Creative Technique", 49, Rarity.RARE, mage.cards.c.CreativeTechnique.class)); + cards.add(new SetCardInfo("Cultivate", 187, Rarity.UNCOMMON, mage.cards.c.Cultivate.class)); + cards.add(new SetCardInfo("Cunning Rhetoric", 38, Rarity.RARE, mage.cards.c.CunningRhetoric.class)); + cards.add(new SetCardInfo("Curiosity Crafter", 24, Rarity.RARE, mage.cards.c.CuriosityCrafter.class)); + cards.add(new SetCardInfo("Curse of Disturbance", 138, Rarity.UNCOMMON, mage.cards.c.CurseOfDisturbance.class)); + cards.add(new SetCardInfo("Curse of the Swine", 118, Rarity.RARE, mage.cards.c.CurseOfTheSwine.class)); + cards.add(new SetCardInfo("Cursed Mirror", 50, Rarity.RARE, mage.cards.c.CursedMirror.class)); + cards.add(new SetCardInfo("Damnable Pact", 139, Rarity.RARE, mage.cards.d.DamnablePact.class)); + cards.add(new SetCardInfo("Daretti, Scrap Savant", 164, Rarity.MYTHIC, mage.cards.d.DarettiScrapSavant.class)); + cards.add(new SetCardInfo("Darksteel Citadel", 285, Rarity.UNCOMMON, mage.cards.d.DarksteelCitadel.class)); + cards.add(new SetCardInfo("Darksteel Mutation", 87, Rarity.UNCOMMON, mage.cards.d.DarksteelMutation.class)); + cards.add(new SetCardInfo("Dazzling Sphinx", 25, Rarity.RARE, mage.cards.d.DazzlingSphinx.class)); + cards.add(new SetCardInfo("Deadly Tempest", 140, Rarity.RARE, mage.cards.d.DeadlyTempest.class)); + cards.add(new SetCardInfo("Deathbringer Liege", 214, Rarity.RARE, mage.cards.d.DeathbringerLiege.class)); + cards.add(new SetCardInfo("Deathbringer Regent", 141, Rarity.RARE, mage.cards.d.DeathbringerRegent.class)); + cards.add(new SetCardInfo("Debtors' Knell", 215, Rarity.RARE, mage.cards.d.DebtorsKnell.class)); + cards.add(new SetCardInfo("Deekah, Fractal Theorist", 26, Rarity.RARE, mage.cards.d.DeekahFractalTheorist.class)); + cards.add(new SetCardInfo("Defiant Bloodlord", 142, Rarity.RARE, mage.cards.d.DefiantBloodlord.class)); + cards.add(new SetCardInfo("Desert of the Fervent", 286, Rarity.COMMON, mage.cards.d.DesertOfTheFervent.class)); + cards.add(new SetCardInfo("Desert of the Mindful", 287, Rarity.COMMON, mage.cards.d.DesertOfTheMindful.class)); + cards.add(new SetCardInfo("Desolation Twin", 82, Rarity.RARE, mage.cards.d.DesolationTwin.class)); + cards.add(new SetCardInfo("Dig Through Time", 119, Rarity.RARE, mage.cards.d.DigThroughTime.class)); + cards.add(new SetCardInfo("Digsite Engineer", 15, Rarity.RARE, mage.cards.d.DigsiteEngineer.class)); + cards.add(new SetCardInfo("Diluvian Primordial", 120, Rarity.RARE, mage.cards.d.DiluvianPrimordial.class)); + cards.add(new SetCardInfo("Dispatch", 88, Rarity.UNCOMMON, mage.cards.d.Dispatch.class)); + cards.add(new SetCardInfo("Dispeller's Capsule", 89, Rarity.COMMON, mage.cards.d.DispellersCapsule.class)); + cards.add(new SetCardInfo("Druidic Satchel", 241, Rarity.RARE, mage.cards.d.DruidicSatchel.class)); + cards.add(new SetCardInfo("Dualcaster Mage", 165, Rarity.RARE, mage.cards.d.DualcasterMage.class)); + cards.add(new SetCardInfo("Duelist's Heritage", 90, Rarity.RARE, mage.cards.d.DuelistsHeritage.class)); + cards.add(new SetCardInfo("Duplicant", 242, Rarity.RARE, mage.cards.d.Duplicant.class)); + cards.add(new SetCardInfo("Elementalist's Palette", 76, Rarity.RARE, mage.cards.e.ElementalistsPalette.class)); + cards.add(new SetCardInfo("Elite Scaleguard", 91, Rarity.UNCOMMON, mage.cards.e.EliteScaleguard.class)); + cards.add(new SetCardInfo("Elixir of Immortality", 243, Rarity.UNCOMMON, mage.cards.e.ElixirOfImmortality.class)); + cards.add(new SetCardInfo("Epic Experiment", 216, Rarity.MYTHIC, mage.cards.e.EpicExperiment.class)); + cards.add(new SetCardInfo("Epicure of Blood", 143, Rarity.COMMON, mage.cards.e.EpicureOfBlood.class)); + cards.add(new SetCardInfo("Erratic Cyclops", 166, Rarity.RARE, mage.cards.e.ErraticCyclops.class)); + cards.add(new SetCardInfo("Esix, Fractal Bloom", 10, Rarity.MYTHIC, mage.cards.e.EsixFractalBloom.class)); + cards.add(new SetCardInfo("Essence Pulse", 39, Rarity.RARE, mage.cards.e.EssencePulse.class)); + cards.add(new SetCardInfo("Etali, Primal Storm", 167, Rarity.RARE, mage.cards.e.EtaliPrimalStorm.class)); + cards.add(new SetCardInfo("Excavation Technique", 16, Rarity.RARE, mage.cards.e.ExcavationTechnique.class)); + cards.add(new SetCardInfo("Exotic Orchard", 288, Rarity.RARE, mage.cards.e.ExoticOrchard.class)); + cards.add(new SetCardInfo("Ezuri's Predation", 188, Rarity.RARE, mage.cards.e.EzurisPredation.class)); + cards.add(new SetCardInfo("Ezzaroot Channeler", 60, Rarity.RARE, mage.cards.e.EzzarootChanneler.class)); + cards.add(new SetCardInfo("Fain, the Broker", 40, Rarity.RARE, mage.cards.f.FainTheBroker.class)); + cards.add(new SetCardInfo("Faithless Looting", 168, Rarity.COMMON, mage.cards.f.FaithlessLooting.class)); + cards.add(new SetCardInfo("Feed the Swarm", 144, Rarity.COMMON, mage.cards.f.FeedTheSwarm.class)); + cards.add(new SetCardInfo("Feldon of the Third Path", 169, Rarity.MYTHIC, mage.cards.f.FeldonOfTheThirdPath.class)); + cards.add(new SetCardInfo("Felisa, Fang of Silverquill", 2, Rarity.MYTHIC, mage.cards.f.FelisaFangOfSilverquill.class)); + cards.add(new SetCardInfo("Fiery Encore", 51, Rarity.RARE, mage.cards.f.FieryEncore.class)); + cards.add(new SetCardInfo("Fiery Fall", 170, Rarity.COMMON, mage.cards.f.FieryFall.class)); + cards.add(new SetCardInfo("Forgotten Ancient", 189, Rarity.RARE, mage.cards.f.ForgottenAncient.class)); + cards.add(new SetCardInfo("Forgotten Cave", 289, Rarity.COMMON, mage.cards.f.ForgottenCave.class)); + cards.add(new SetCardInfo("Fractal Harness", 61, Rarity.RARE, mage.cards.f.FractalHarness.class)); + cards.add(new SetCardInfo("Garruk, Primal Hunter", 190, Rarity.MYTHIC, mage.cards.g.GarrukPrimalHunter.class)); + cards.add(new SetCardInfo("Gaze of Granite", 217, Rarity.RARE, mage.cards.g.GazeOfGranite.class)); + cards.add(new SetCardInfo("Geometric Nexus", 77, Rarity.RARE, mage.cards.g.GeometricNexus.class)); + cards.add(new SetCardInfo("Ghostly Prison", 92, Rarity.UNCOMMON, mage.cards.g.GhostlyPrison.class)); + cards.add(new SetCardInfo("Gideon, Champion of Justice", 93, Rarity.MYTHIC, mage.cards.g.GideonChampionOfJustice.class)); + cards.add(new SetCardInfo("Gift of Paradise", 191, Rarity.COMMON, mage.cards.g.GiftOfParadise.class)); + cards.add(new SetCardInfo("Gingerbread Cabin", 290, Rarity.COMMON, mage.cards.g.GingerbreadCabin.class)); + cards.add(new SetCardInfo("Gluttonous Troll", 218, Rarity.RARE, mage.cards.g.GluttonousTroll.class)); + cards.add(new SetCardInfo("Golgari Rot Farm", 291, Rarity.UNCOMMON, mage.cards.g.GolgariRotFarm.class)); + cards.add(new SetCardInfo("Great Furnace", 292, Rarity.COMMON, mage.cards.g.GreatFurnace.class)); + cards.add(new SetCardInfo("Greed", 145, Rarity.RARE, mage.cards.g.Greed.class)); + cards.add(new SetCardInfo("Guardian Archon", 17, Rarity.RARE, mage.cards.g.GuardianArchon.class)); + cards.add(new SetCardInfo("Guardian Augmenter", 62, Rarity.RARE, mage.cards.g.GuardianAugmenter.class)); + cards.add(new SetCardInfo("Gyome, Master Chef", 5, Rarity.MYTHIC, mage.cards.g.GyomeMasterChef.class)); + cards.add(new SetCardInfo("Healing Technique", 63, Rarity.RARE, mage.cards.h.HealingTechnique.class)); + cards.add(new SetCardInfo("Hedron Archive", 244, Rarity.UNCOMMON, mage.cards.h.HedronArchive.class)); + cards.add(new SetCardInfo("Hellkite Igniter", 171, Rarity.RARE, mage.cards.h.HellkiteIgniter.class)); + cards.add(new SetCardInfo("Hellkite Tyrant", 172, Rarity.MYTHIC, mage.cards.h.HellkiteTyrant.class)); + cards.add(new SetCardInfo("High Market", 293, Rarity.RARE, mage.cards.h.HighMarket.class)); + cards.add(new SetCardInfo("Hoard-Smelter Dragon", 173, Rarity.RARE, mage.cards.h.HoardSmelterDragon.class)); + cards.add(new SetCardInfo("Hornet Nest", 192, Rarity.RARE, mage.cards.h.HornetNest.class)); + cards.add(new SetCardInfo("Hornet Queen", 193, Rarity.RARE, mage.cards.h.HornetQueen.class)); + cards.add(new SetCardInfo("Humble Defector", 174, Rarity.UNCOMMON, mage.cards.h.HumbleDefector.class)); + cards.add(new SetCardInfo("Hunted Lammasu", 94, Rarity.RARE, mage.cards.h.HuntedLammasu.class)); + cards.add(new SetCardInfo("Hydra Broodmaster", 194, Rarity.RARE, mage.cards.h.HydraBroodmaster.class)); + cards.add(new SetCardInfo("Ichor Wellspring", 245, Rarity.COMMON, mage.cards.i.IchorWellspring.class)); + cards.add(new SetCardInfo("Idol of Oblivion", 246, Rarity.RARE, mage.cards.i.IdolOfOblivion.class)); + cards.add(new SetCardInfo("Incarnation Technique", 41, Rarity.RARE, mage.cards.i.IncarnationTechnique.class)); + cards.add(new SetCardInfo("Incubation // Incongruity", 219, Rarity.UNCOMMON, mage.cards.i.IncubationIncongruity.class)); + cards.add(new SetCardInfo("Incubation Druid", 195, Rarity.RARE, mage.cards.i.IncubationDruid.class)); + cards.add(new SetCardInfo("Infernal Offering", 146, Rarity.RARE, mage.cards.i.InfernalOffering.class)); + cards.add(new SetCardInfo("Inferno Project", 52, Rarity.RARE, mage.cards.i.InfernoProject.class)); + cards.add(new SetCardInfo("Inkshield", 71, Rarity.RARE, mage.cards.i.Inkshield.class)); + cards.add(new SetCardInfo("Inspiring Refrain", 27, Rarity.RARE, mage.cards.i.InspiringRefrain.class)); + cards.add(new SetCardInfo("Izzet Boilerworks", 294, Rarity.UNCOMMON, mage.cards.i.IzzetBoilerworks.class)); + cards.add(new SetCardInfo("Izzet Signet", 247, Rarity.COMMON, mage.cards.i.IzzetSignet.class)); + cards.add(new SetCardInfo("Jaya Ballard", 175, Rarity.MYTHIC, mage.cards.j.JayaBallard.class)); + cards.add(new SetCardInfo("Jor Kadeen, the Prevailer", 220, Rarity.RARE, mage.cards.j.JorKadeenThePrevailer.class)); + cards.add(new SetCardInfo("Jungle Hollow", 295, Rarity.COMMON, mage.cards.j.JungleHollow.class)); + cards.add(new SetCardInfo("Kaseto, Orochi Archmage", 221, Rarity.MYTHIC, mage.cards.k.KasetoOrochiArchmage.class)); + cards.add(new SetCardInfo("Kazandu Tuskcaller", 196, Rarity.RARE, mage.cards.k.KazanduTuskcaller.class)); + cards.add(new SetCardInfo("Keen Duelist", 42, Rarity.RARE, mage.cards.k.KeenDuelist.class)); + cards.add(new SetCardInfo("Key to the City", 248, Rarity.RARE, mage.cards.k.KeyToTheCity.class)); + cards.add(new SetCardInfo("Knight of the White Orchid", 95, Rarity.RARE, mage.cards.k.KnightOfTheWhiteOrchid.class)); + cards.add(new SetCardInfo("Kodama's Reach", 197, Rarity.COMMON, mage.cards.k.KodamasReach.class)); + cards.add(new SetCardInfo("Krosan Grip", 198, Rarity.UNCOMMON, mage.cards.k.KrosanGrip.class)); + cards.add(new SetCardInfo("Leyline Prowler", 222, Rarity.UNCOMMON, mage.cards.l.LeylineProwler.class)); + cards.add(new SetCardInfo("Living Lore", 121, Rarity.UNCOMMON, mage.cards.l.LivingLore.class)); + cards.add(new SetCardInfo("Llanowar Reborn", 296, Rarity.UNCOMMON, mage.cards.l.LlanowarReborn.class)); + cards.add(new SetCardInfo("Llanowar Wastes", 297, Rarity.RARE, mage.cards.l.LlanowarWastes.class)); + cards.add(new SetCardInfo("Lonely Sandbar", 298, Rarity.COMMON, mage.cards.l.LonelySandbar.class)); + cards.add(new SetCardInfo("Losheel, Clockwork Scholar", 18, Rarity.RARE, mage.cards.l.LosheelClockworkScholar.class)); + cards.add(new SetCardInfo("Loxodon Warhammer", 249, Rarity.RARE, mage.cards.l.LoxodonWarhammer.class)); + cards.add(new SetCardInfo("Lumbering Falls", 299, Rarity.RARE, mage.cards.l.LumberingFalls.class)); + cards.add(new SetCardInfo("Mage-Ring Network", 300, Rarity.UNCOMMON, mage.cards.m.MageRingNetwork.class)); + cards.add(new SetCardInfo("Magister of Worth", 223, Rarity.RARE, mage.cards.m.MagisterOfWorth.class)); + cards.add(new SetCardInfo("Mana Geyser", 176, Rarity.COMMON, mage.cards.m.ManaGeyser.class)); + cards.add(new SetCardInfo("Managorger Hydra", 199, Rarity.RARE, mage.cards.m.ManagorgerHydra.class)); + cards.add(new SetCardInfo("Marshland Bloodcaster", 43, Rarity.RARE, mage.cards.m.MarshlandBloodcaster.class)); + cards.add(new SetCardInfo("Martial Impetus", 96, Rarity.UNCOMMON, mage.cards.m.MartialImpetus.class)); + cards.add(new SetCardInfo("Master Biomancer", 224, Rarity.MYTHIC, mage.cards.m.MasterBiomancer.class)); + cards.add(new SetCardInfo("Memorial to Genius", 301, Rarity.UNCOMMON, mage.cards.m.MemorialToGenius.class)); + cards.add(new SetCardInfo("Metallurgic Summonings", 122, Rarity.MYTHIC, mage.cards.m.MetallurgicSummonings.class)); + cards.add(new SetCardInfo("Meteor Golem", 250, Rarity.UNCOMMON, mage.cards.m.MeteorGolem.class)); + cards.add(new SetCardInfo("Mikokoro, Center of the Sea", 302, Rarity.RARE, mage.cards.m.MikokoroCenterOfTheSea.class)); + cards.add(new SetCardInfo("Mind Stone", 251, Rarity.UNCOMMON, mage.cards.m.MindStone.class)); + cards.add(new SetCardInfo("Mind's Desire", 123, Rarity.RARE, mage.cards.m.MindsDesire.class)); + cards.add(new SetCardInfo("Moldervine Reclamation", 225, Rarity.UNCOMMON, mage.cards.m.MoldervineReclamation.class)); + cards.add(new SetCardInfo("Monologue Tax", 19, Rarity.RARE, mage.cards.m.MonologueTax.class)); + cards.add(new SetCardInfo("Mosswort Bridge", 303, Rarity.RARE, mage.cards.m.MosswortBridge.class)); + cards.add(new SetCardInfo("Muse Vortex", 28, Rarity.RARE, mage.cards.m.MuseVortex.class)); + cards.add(new SetCardInfo("Mycosynth Wellspring", 252, Rarity.COMMON, mage.cards.m.MycosynthWellspring.class)); + cards.add(new SetCardInfo("Myr Battlesphere", 253, Rarity.RARE, mage.cards.m.MyrBattlesphere.class)); + cards.add(new SetCardInfo("Myriad Landscape", 304, Rarity.UNCOMMON, mage.cards.m.MyriadLandscape.class)); + cards.add(new SetCardInfo("Naru Meha, Master Wizard", 124, Rarity.MYTHIC, mage.cards.n.NaruMehaMasterWizard.class)); + cards.add(new SetCardInfo("Necropolis Regent", 147, Rarity.MYTHIC, mage.cards.n.NecropolisRegent.class)); + cards.add(new SetCardInfo("Nils, Discipline Enforcer", 20, Rarity.RARE, mage.cards.n.NilsDisciplineEnforcer.class)); + cards.add(new SetCardInfo("Nissa's Expedition", 200, Rarity.UNCOMMON, mage.cards.n.NissasExpedition.class)); + cards.add(new SetCardInfo("Nissa's Renewal", 201, Rarity.RARE, mage.cards.n.NissasRenewal.class)); + cards.add(new SetCardInfo("Novijen, Heart of Progress", 305, Rarity.UNCOMMON, mage.cards.n.NovijenHeartOfProgress.class)); + cards.add(new SetCardInfo("Noxious Gearhulk", 148, Rarity.MYTHIC, mage.cards.n.NoxiousGearhulk.class)); + cards.add(new SetCardInfo("Ob Nixilis Reignited", 149, Rarity.MYTHIC, mage.cards.o.ObNixilisReignited.class)); + cards.add(new SetCardInfo("Oblation", 97, Rarity.RARE, mage.cards.o.Oblation.class)); + cards.add(new SetCardInfo("Octavia, Living Thesis", 29, Rarity.RARE, mage.cards.o.OctaviaLivingThesis.class)); + cards.add(new SetCardInfo("Opal Palace", 306, Rarity.COMMON, mage.cards.o.OpalPalace.class)); + cards.add(new SetCardInfo("Oran-Rief, the Vastwood", 307, Rarity.RARE, mage.cards.o.OranRiefTheVastwood.class)); + cards.add(new SetCardInfo("Oreskos Explorer", 98, Rarity.UNCOMMON, mage.cards.o.OreskosExplorer.class)); + cards.add(new SetCardInfo("Orzhov Advokist", 99, Rarity.UNCOMMON, mage.cards.o.OrzhovAdvokist.class)); + cards.add(new SetCardInfo("Orzhov Basilica", 308, Rarity.UNCOMMON, mage.cards.o.OrzhovBasilica.class)); + cards.add(new SetCardInfo("Orzhov Signet", 254, Rarity.UNCOMMON, mage.cards.o.OrzhovSignet.class)); + cards.add(new SetCardInfo("Osgir, the Reconstructor", 8, Rarity.MYTHIC, mage.cards.o.OsgirTheReconstructor.class)); + cards.add(new SetCardInfo("Oversimplify", 72, Rarity.RARE, mage.cards.o.Oversimplify.class)); + cards.add(new SetCardInfo("Paradise Plume", 255, Rarity.UNCOMMON, mage.cards.p.ParadisePlume.class)); + cards.add(new SetCardInfo("Paradox Zone", 64, Rarity.RARE, mage.cards.p.ParadoxZone.class)); + cards.add(new SetCardInfo("Parasitic Impetus", 150, Rarity.UNCOMMON, mage.cards.p.ParasiticImpetus.class)); + cards.add(new SetCardInfo("Pendant of Prosperity", 256, Rarity.RARE, mage.cards.p.PendantOfProsperity.class)); + cards.add(new SetCardInfo("Perplexing Test", 30, Rarity.RARE, mage.cards.p.PerplexingTest.class)); + cards.add(new SetCardInfo("Pest Infestation", 65, Rarity.RARE, mage.cards.p.PestInfestation.class)); + cards.add(new SetCardInfo("Phyrexia's Core", 309, Rarity.UNCOMMON, mage.cards.p.PhyrexiasCore.class)); + cards.add(new SetCardInfo("Pia Nalaar", 177, Rarity.RARE, mage.cards.p.PiaNalaar.class)); + cards.add(new SetCardInfo("Pilgrim's Eye", 257, Rarity.COMMON, mage.cards.p.PilgrimsEye.class)); + cards.add(new SetCardInfo("Plaxcaster Frogling", 226, Rarity.UNCOMMON, mage.cards.p.PlaxcasterFrogling.class)); + cards.add(new SetCardInfo("Ponder", 125, Rarity.COMMON, mage.cards.p.Ponder.class)); + cards.add(new SetCardInfo("Primal Empathy", 227, Rarity.UNCOMMON, mage.cards.p.PrimalEmpathy.class)); + cards.add(new SetCardInfo("Pristine Talisman", 258, Rarity.COMMON, mage.cards.p.PristineTalisman.class)); + cards.add(new SetCardInfo("Promise of Loyalty", 21, Rarity.RARE, mage.cards.p.PromiseOfLoyalty.class)); + cards.add(new SetCardInfo("Pulse of Murasa", 202, Rarity.UNCOMMON, mage.cards.p.PulseOfMurasa.class)); + cards.add(new SetCardInfo("Pyromancer's Goggles", 259, Rarity.MYTHIC, mage.cards.p.PyromancersGoggles.class)); + cards.add(new SetCardInfo("Quicksmith Genius", 178, Rarity.UNCOMMON, mage.cards.q.QuicksmithGenius.class)); + cards.add(new SetCardInfo("Radiant Fountain", 310, Rarity.COMMON, mage.cards.r.RadiantFountain.class)); + cards.add(new SetCardInfo("Radiant Performer", 54, Rarity.RARE, mage.cards.r.RadiantPerformer.class)); + cards.add(new SetCardInfo("Rampaging Baloths", 203, Rarity.RARE, mage.cards.r.RampagingBaloths.class)); + cards.add(new SetCardInfo("Rampant Growth", 204, Rarity.COMMON, mage.cards.r.RampantGrowth.class)); + cards.add(new SetCardInfo("Rapid Hybridization", 126, Rarity.UNCOMMON, mage.cards.r.RapidHybridization.class)); + cards.add(new SetCardInfo("Reckless Spite", 151, Rarity.UNCOMMON, mage.cards.r.RecklessSpite.class)); + cards.add(new SetCardInfo("Reef Worm", 127, Rarity.RARE, mage.cards.r.ReefWorm.class)); + cards.add(new SetCardInfo("Reinterpret", 73, Rarity.RARE, mage.cards.r.Reinterpret.class)); + cards.add(new SetCardInfo("Reliquary Tower", 311, Rarity.UNCOMMON, mage.cards.r.ReliquaryTower.class)); + cards.add(new SetCardInfo("Replication Technique", 31, Rarity.RARE, mage.cards.r.ReplicationTechnique.class)); + cards.add(new SetCardInfo("Return of the Wildspeaker", 205, Rarity.RARE, mage.cards.r.ReturnOfTheWildspeaker.class)); + cards.add(new SetCardInfo("Return to Dust", 100, Rarity.UNCOMMON, mage.cards.r.ReturnToDust.class)); + cards.add(new SetCardInfo("Revival Experiment", 74, Rarity.RARE, mage.cards.r.RevivalExperiment.class)); + cards.add(new SetCardInfo("Rionya, Fire Dancer", 55, Rarity.RARE, mage.cards.r.RionyaFireDancer.class)); + cards.add(new SetCardInfo("Rite of Replication", 128, Rarity.RARE, mage.cards.r.RiteOfReplication.class)); + cards.add(new SetCardInfo("Rogue's Passage", 312, Rarity.UNCOMMON, mage.cards.r.RoguesPassage.class)); + cards.add(new SetCardInfo("Rousing Refrain", 56, Rarity.RARE, mage.cards.r.RousingRefrain.class)); + cards.add(new SetCardInfo("Rout", 101, Rarity.RARE, mage.cards.r.Rout.class)); + cards.add(new SetCardInfo("Ruin Grinder", 57, Rarity.RARE, mage.cards.r.RuinGrinder.class)); + cards.add(new SetCardInfo("Ruxa, Patient Professor", 66, Rarity.RARE, mage.cards.r.RuxaPatientProfessor.class)); + cards.add(new SetCardInfo("Sanctum Gargoyle", 102, Rarity.COMMON, mage.cards.s.SanctumGargoyle.class)); + cards.add(new SetCardInfo("Sangromancer", 152, Rarity.RARE, mage.cards.s.Sangromancer.class)); + cards.add(new SetCardInfo("Sanguine Bond", 153, Rarity.RARE, mage.cards.s.SanguineBond.class)); + cards.add(new SetCardInfo("Sapling of Colfenor", 228, Rarity.RARE, mage.cards.s.SaplingOfColfenor.class)); + cards.add(new SetCardInfo("Sapseep Forest", 313, Rarity.UNCOMMON, mage.cards.s.SapseepForest.class)); + cards.add(new SetCardInfo("Scavenger Grounds", 314, Rarity.RARE, mage.cards.s.ScavengerGrounds.class)); + cards.add(new SetCardInfo("Scholarship Sponsor", 22, Rarity.RARE, mage.cards.s.ScholarshipSponsor.class)); + cards.add(new SetCardInfo("Scrap Trawler", 260, Rarity.RARE, mage.cards.s.ScrapTrawler.class)); + cards.add(new SetCardInfo("Sculpting Steel", 261, Rarity.RARE, mage.cards.s.SculptingSteel.class)); + cards.add(new SetCardInfo("Secluded Steppe", 315, Rarity.COMMON, mage.cards.s.SecludedSteppe.class)); + cards.add(new SetCardInfo("Seething Song", 179, Rarity.COMMON, mage.cards.s.SeethingSong.class)); + cards.add(new SetCardInfo("Selfless Squire", 103, Rarity.RARE, mage.cards.s.SelflessSquire.class)); + cards.add(new SetCardInfo("Sequence Engine", 67, Rarity.RARE, mage.cards.s.SequenceEngine.class)); + cards.add(new SetCardInfo("Serum Visions", 129, Rarity.UNCOMMON, mage.cards.s.SerumVisions.class)); + cards.add(new SetCardInfo("Shamanic Revelation", 206, Rarity.RARE, mage.cards.s.ShamanicRevelation.class)); + cards.add(new SetCardInfo("Shivan Reef", 316, Rarity.RARE, mage.cards.s.ShivanReef.class)); + cards.add(new SetCardInfo("Silversmote Ghoul", 154, Rarity.UNCOMMON, mage.cards.s.SilversmoteGhoul.class)); + cards.add(new SetCardInfo("Simic Growth Chamber", 317, Rarity.UNCOMMON, mage.cards.s.SimicGrowthChamber.class)); + cards.add(new SetCardInfo("Simic Signet", 262, Rarity.COMMON, mage.cards.s.SimicSignet.class)); + cards.add(new SetCardInfo("Slayers' Stronghold", 318, Rarity.RARE, mage.cards.s.SlayersStronghold.class)); + cards.add(new SetCardInfo("Sly Instigator", 32, Rarity.RARE, mage.cards.s.SlyInstigator.class)); + cards.add(new SetCardInfo("Sol Ring", 263, Rarity.UNCOMMON, mage.cards.s.SolRing.class)); + cards.add(new SetCardInfo("Solemn Simulacrum", 264, Rarity.RARE, mage.cards.s.SolemnSimulacrum.class)); + cards.add(new SetCardInfo("Soul Snare", 104, Rarity.UNCOMMON, mage.cards.s.SoulSnare.class)); + cards.add(new SetCardInfo("Spawning Kraken", 33, Rarity.RARE, mage.cards.s.SpawningKraken.class)); + cards.add(new SetCardInfo("Spectral Searchlight", 265, Rarity.UNCOMMON, mage.cards.s.SpectralSearchlight.class)); + cards.add(new SetCardInfo("Spitting Image", 229, Rarity.RARE, mage.cards.s.SpittingImage.class)); + cards.add(new SetCardInfo("Sproutback Trudge", 68, Rarity.RARE, mage.cards.s.SproutbackTrudge.class)); + cards.add(new SetCardInfo("Stalking Leonin", 105, Rarity.RARE, mage.cards.s.StalkingLeonin.class)); + cards.add(new SetCardInfo("Steel Hellkite", 266, Rarity.RARE, mage.cards.s.SteelHellkite.class)); + cards.add(new SetCardInfo("Steel Overseer", 267, Rarity.RARE, mage.cards.s.SteelOverseer.class)); + cards.add(new SetCardInfo("Stinging Study", 44, Rarity.RARE, mage.cards.s.StingingStudy.class)); + cards.add(new SetCardInfo("Study Hall", 80, Rarity.COMMON, mage.cards.s.StudyHall.class)); + cards.add(new SetCardInfo("Suffer the Past", 155, Rarity.UNCOMMON, mage.cards.s.SufferThePast.class)); + cards.add(new SetCardInfo("Sun Droplet", 268, Rarity.UNCOMMON, mage.cards.s.SunDroplet.class)); + cards.add(new SetCardInfo("Sun Titan", 106, Rarity.MYTHIC, mage.cards.s.SunTitan.class)); + cards.add(new SetCardInfo("Sunbird's Invocation", 180, Rarity.RARE, mage.cards.s.SunbirdsInvocation.class)); + cards.add(new SetCardInfo("Sunhome, Fortress of the Legion", 319, Rarity.UNCOMMON, mage.cards.s.SunhomeFortressOfTheLegion.class)); + cards.add(new SetCardInfo("Sunscorch Regent", 107, Rarity.RARE, mage.cards.s.SunscorchRegent.class)); + cards.add(new SetCardInfo("Surge to Victory", 58, Rarity.RARE, mage.cards.s.SurgeToVictory.class)); + cards.add(new SetCardInfo("Swarm Intelligence", 130, Rarity.RARE, mage.cards.s.SwarmIntelligence.class)); + cards.add(new SetCardInfo("Tainted Field", 320, Rarity.UNCOMMON, mage.cards.t.TaintedField.class)); + cards.add(new SetCardInfo("Tainted Wood", 321, Rarity.UNCOMMON, mage.cards.t.TaintedWood.class)); + cards.add(new SetCardInfo("Talisman of Creativity", 269, Rarity.UNCOMMON, mage.cards.t.TalismanOfCreativity.class)); + cards.add(new SetCardInfo("Talisman of Resilience", 270, Rarity.UNCOMMON, mage.cards.t.TalismanOfResilience.class)); + cards.add(new SetCardInfo("Talrand, Sky Summoner", 131, Rarity.RARE, mage.cards.t.TalrandSkySummoner.class)); + cards.add(new SetCardInfo("Taste of Death", 156, Rarity.RARE, mage.cards.t.TasteOfDeath.class)); + cards.add(new SetCardInfo("Temple of Epiphany", 322, Rarity.RARE, mage.cards.t.TempleOfEpiphany.class)); + cards.add(new SetCardInfo("Temple of Malady", 323, Rarity.RARE, mage.cards.t.TempleOfMalady.class)); + cards.add(new SetCardInfo("Temple of Mystery", 324, Rarity.RARE, mage.cards.t.TempleOfMystery.class)); + cards.add(new SetCardInfo("Temple of Silence", 325, Rarity.RARE, mage.cards.t.TempleOfSilence.class)); + cards.add(new SetCardInfo("Temple of Triumph", 327, Rarity.RARE, mage.cards.t.TempleOfTriumph.class)); + cards.add(new SetCardInfo("Temple of the False God", 326, Rarity.UNCOMMON, mage.cards.t.TempleOfTheFalseGod.class)); + cards.add(new SetCardInfo("Tempting Contract", 78, Rarity.RARE, mage.cards.t.TemptingContract.class)); + cards.add(new SetCardInfo("Terastodon", 207, Rarity.RARE, mage.cards.t.Terastodon.class)); + cards.add(new SetCardInfo("Teysa, Envoy of Ghosts", 230, Rarity.RARE, mage.cards.t.TeysaEnvoyOfGhosts.class)); + cards.add(new SetCardInfo("Theoretical Duplication", 34, Rarity.RARE, mage.cards.t.TheoreticalDuplication.class)); + cards.add(new SetCardInfo("Thopter Engineer", 181, Rarity.UNCOMMON, mage.cards.t.ThopterEngineer.class)); + cards.add(new SetCardInfo("Thousand-Year Elixir", 271, Rarity.RARE, mage.cards.t.ThousandYearElixir.class)); + cards.add(new SetCardInfo("Tivash, Gloom Summoner", 45, Rarity.RARE, mage.cards.t.TivashGloomSummoner.class)); + cards.add(new SetCardInfo("Together Forever", 108, Rarity.RARE, mage.cards.t.TogetherForever.class)); + cards.add(new SetCardInfo("Tragic Arrogance", 109, Rarity.RARE, mage.cards.t.TragicArrogance.class)); + cards.add(new SetCardInfo("Tranquil Thicket", 408, Rarity.COMMON, mage.cards.t.TranquilThicket.class)); + cards.add(new SetCardInfo("Traumatic Visions", 132, Rarity.COMMON, mage.cards.t.TraumaticVisions.class)); + cards.add(new SetCardInfo("Treasure Cruise", 133, Rarity.COMMON, mage.cards.t.TreasureCruise.class)); + cards.add(new SetCardInfo("Triplicate Titan", 79, Rarity.RARE, mage.cards.t.TriplicateTitan.class)); + cards.add(new SetCardInfo("Trudge Garden", 69, Rarity.RARE, mage.cards.t.TrudgeGarden.class)); + cards.add(new SetCardInfo("Trygon Predator", 231, Rarity.UNCOMMON, mage.cards.t.TrygonPredator.class)); + cards.add(new SetCardInfo("Unstable Obelisk", 272, Rarity.UNCOMMON, mage.cards.u.UnstableObelisk.class)); + cards.add(new SetCardInfo("Utter End", 232, Rarity.RARE, mage.cards.u.UtterEnd.class)); + cards.add(new SetCardInfo("Vampire Nighthawk", 157, Rarity.UNCOMMON, mage.cards.v.VampireNighthawk.class)); + cards.add(new SetCardInfo("Veinwitch Coven", 46, Rarity.RARE, mage.cards.v.VeinwitchCoven.class)); + cards.add(new SetCardInfo("Venser's Journal", 273, Rarity.RARE, mage.cards.v.VensersJournal.class)); + cards.add(new SetCardInfo("Verdant Sun's Avatar", 208, Rarity.RARE, mage.cards.v.VerdantSunsAvatar.class)); + cards.add(new SetCardInfo("Veyran, Voice of Duality", 3, Rarity.MYTHIC, mage.cards.v.VeyranVoiceOfDuality.class)); + cards.add(new SetCardInfo("Victory Chimes", 274, Rarity.RARE, mage.cards.v.VictoryChimes.class)); + cards.add(new SetCardInfo("Volcanic Vision", 182, Rarity.RARE, mage.cards.v.VolcanicVision.class)); + cards.add(new SetCardInfo("Vow of Duty", 110, Rarity.UNCOMMON, mage.cards.v.VowOfDuty.class)); + cards.add(new SetCardInfo("Wake the Past", 75, Rarity.RARE, mage.cards.w.WakeThePast.class)); + cards.add(new SetCardInfo("Well of Lost Dreams", 275, Rarity.RARE, mage.cards.w.WellOfLostDreams.class)); + cards.add(new SetCardInfo("Wildfire Devils", 183, Rarity.RARE, mage.cards.w.WildfireDevils.class)); + cards.add(new SetCardInfo("Willowdusk, Essence Seer", 6, Rarity.MYTHIC, mage.cards.w.WillowduskEssenceSeer.class)); + cards.add(new SetCardInfo("Windborn Muse", 111, Rarity.RARE, mage.cards.w.WindbornMuse.class)); + cards.add(new SetCardInfo("Witch's Clinic", 81, Rarity.RARE, mage.cards.w.WitchsClinic.class)); + cards.add(new SetCardInfo("Yavimaya Coast", 409, Rarity.RARE, mage.cards.y.YavimayaCoast.class)); + cards.add(new SetCardInfo("Yedora, Grave Gardener", 70, Rarity.RARE, mage.cards.y.YedoraGraveGardener.class)); + cards.add(new SetCardInfo("Zaffai, Thunder Conductor", 4, Rarity.MYTHIC, mage.cards.z.ZaffaiThunderConductor.class)); + cards.add(new SetCardInfo("Zetalpa, Primal Dawn", 112, Rarity.RARE, mage.cards.z.ZetalpaPrimalDawn.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/Conspiracy.java b/Mage.Sets/src/mage/sets/Conspiracy.java index bc992d213619..1de0a2588647 100644 --- a/Mage.Sets/src/mage/sets/Conspiracy.java +++ b/Mage.Sets/src/mage/sets/Conspiracy.java @@ -39,6 +39,7 @@ private Conspiracy() { cards.add(new SetCardInfo("Basandra, Battle Seraph", 184, Rarity.RARE, mage.cards.b.BasandraBattleSeraph.class)); cards.add(new SetCardInfo("Bite of the Black Rose", 26, Rarity.UNCOMMON, mage.cards.b.BiteOfTheBlackRose.class)); cards.add(new SetCardInfo("Boldwyr Intimidator", 137, Rarity.UNCOMMON, mage.cards.b.BoldwyrIntimidator.class)); + cards.add(new SetCardInfo("Brago's Representative", 14, Rarity.COMMON, mage.cards.b.BragosRepresentative.class)); cards.add(new SetCardInfo("Brago, King Eternal", 41, Rarity.RARE, mage.cards.b.BragoKingEternal.class)); cards.add(new SetCardInfo("Brainstorm", 91, Rarity.COMMON, mage.cards.b.Brainstorm.class)); cards.add(new SetCardInfo("Breakthrough", 92, Rarity.UNCOMMON, mage.cards.b.Breakthrough.class)); @@ -90,6 +91,7 @@ private Conspiracy() { cards.add(new SetCardInfo("Grenzo, Dungeon Warden", 47, Rarity.RARE, mage.cards.g.GrenzoDungeonWarden.class)); cards.add(new SetCardInfo("Grenzo's Cutthroat", 32, Rarity.COMMON, mage.cards.g.GrenzosCutthroat.class)); cards.add(new SetCardInfo("Grixis Illusionist", 99, Rarity.COMMON, mage.cards.g.GrixisIllusionist.class)); + cards.add(new SetCardInfo("Grudge Keeper", 28, Rarity.COMMON, mage.cards.g.GrudgeKeeper.class)); cards.add(new SetCardInfo("Guardian Zendikon", 71, Rarity.COMMON, mage.cards.g.GuardianZendikon.class)); cards.add(new SetCardInfo("Heartless Hidetsugu", 144, Rarity.RARE, mage.cards.h.HeartlessHidetsugu.class)); cards.add(new SetCardInfo("Heckling Fiends", 145, Rarity.UNCOMMON, mage.cards.h.HecklingFiends.class)); diff --git a/Mage.Sets/src/mage/sets/ConspiracyTakeTheCrown.java b/Mage.Sets/src/mage/sets/ConspiracyTakeTheCrown.java index 10c116f56a65..603c9bb55aa8 100644 --- a/Mage.Sets/src/mage/sets/ConspiracyTakeTheCrown.java +++ b/Mage.Sets/src/mage/sets/ConspiracyTakeTheCrown.java @@ -33,6 +33,7 @@ private ConspiracyTakeTheCrown() { cards.add(new SetCardInfo("Altar's Reap", 127, Rarity.COMMON, mage.cards.a.AltarsReap.class)); cards.add(new SetCardInfo("Ascended Lawmage", 198, Rarity.UNCOMMON, mage.cards.a.AscendedLawmage.class)); cards.add(new SetCardInfo("Avatar of Woe", 128, Rarity.MYTHIC, mage.cards.a.AvatarOfWoe.class)); + cards.add(new SetCardInfo("Ballot Broker", 13, Rarity.COMMON, mage.cards.b.BallotBroker.class)); cards.add(new SetCardInfo("Beast Within", 174, Rarity.UNCOMMON, mage.cards.b.BeastWithin.class)); cards.add(new SetCardInfo("Berserk", 175, Rarity.MYTHIC, mage.cards.b.Berserk.class)); cards.add(new SetCardInfo("Besmirch", 49, Rarity.UNCOMMON, mage.cards.b.Besmirch.class)); @@ -121,6 +122,7 @@ private ConspiracyTakeTheCrown() { cards.add(new SetCardInfo("Hundred-Handed One", 93, Rarity.RARE, mage.cards.h.HundredHandedOne.class)); cards.add(new SetCardInfo("Hurly-Burly", 165, Rarity.COMMON, mage.cards.h.HurlyBurly.class)); cards.add(new SetCardInfo("Ill-Tempered Cyclops", 166, Rarity.COMMON, mage.cards.i.IllTemperedCyclops.class)); + cards.add(new SetCardInfo("Illusion of Choice", 31, Rarity.UNCOMMON, mage.cards.i.IllusionOfChoice.class)); cards.add(new SetCardInfo("Infest", 139, Rarity.UNCOMMON, mage.cards.i.Infest.class)); cards.add(new SetCardInfo("Inquisition of Kozilek", 140, Rarity.RARE, mage.cards.i.InquisitionOfKozilek.class)); cards.add(new SetCardInfo("Into the Void", 112, Rarity.UNCOMMON, mage.cards.i.IntoTheVoid.class)); diff --git a/Mage.Sets/src/mage/sets/DoubleMasters.java b/Mage.Sets/src/mage/sets/DoubleMasters.java index bad9a832ddeb..21ab75bf72d0 100644 --- a/Mage.Sets/src/mage/sets/DoubleMasters.java +++ b/Mage.Sets/src/mage/sets/DoubleMasters.java @@ -1,9 +1,16 @@ package mage.sets; import mage.cards.ExpansionSet; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; +import java.util.ArrayList; +import java.util.List; + /** * @author TheElk801 */ @@ -16,7 +23,7 @@ public static DoubleMasters getInstance() { } private DoubleMasters() { - super("Double Masters", "2XM", ExpansionSet.buildDate(2020, 8, 7), SetType.SUPPLEMENTAL); + super("Double Masters", "2XM", ExpansionSet.buildDate(2020, 8, 7), SetType.SUPPLEMENTAL, new DoubleMastersCollator()); this.blockName = "Reprint"; this.hasBasicLands = true; this.hasBoosters = true; @@ -412,3 +419,368 @@ private DoubleMasters() { cards.add(new SetCardInfo("Yavimaya's Embrace", 229, Rarity.UNCOMMON, mage.cards.y.YavimayasEmbrace.class)); } } + +// Booster collation info from https://www.lethe.xyz/mtg/collation/2xm.html +// Using USA collation for all rarities +// Foil slot partially inferred to match standard booster rarity as best as possible +// Foil odds (approximate): 10/14 to be common, 3/14 to be uncommon, 0.875/14 to be rare, 0.125/14 to be mythic +// Regular common sheets used for foil commons as foil common sheet is currently incomplete +// TODO: write a test, not sure how right now +class DoubleMastersCollator implements BoosterCollator { + + private static class DoubleMastersRun extends CardRun { + private static final DoubleMastersRun commonA = new DoubleMastersRun(true, "160", "108", "146", "79", "247", "165", "114", "111", "163", "29", "143", "105", "162", "135", "154", "78", "144", "151", "140", "84", "187", "304", "87", "133", "173", "95", "126", "28", "176", "90", "137", "165", "83", "159", "116", "168", "92", "121", "154", "79", "150", "181", "247", "146", "111", "160", "143", "96", "114", "108", "151", "78", "135", "95", "162", "144", "87", "140", "105", "163", "304", "84", "126", "173", "111", "133", "29", "187", "83", "137", "176", "90", "159", "150", "96", "247", "146", "165", "92", "116", "28", "160", "108", "121", "79", "168", "144", "162", "87", "163", "114", "84", "154", "304", "105", "135", "173", "95", "126", "29", "151", "83", "140", "181", "133", "78", "143", "187", "28", "150", "96", "159", "176", "90", "137", "168", "116", "92", "181", "121"); + private static final DoubleMastersRun commonB = new DoubleMastersRun(true, "250", "70", "259", "305", "45", "80", "261", "60", "288", "331", "294", "63", "255", "263", "46", "230", "262", "50", "257", "256", "44", "283", "237", "74", "157", "277", "59", "330", "280", "52", "254", "329", "250", "69", "239", "331", "45", "287", "288", "42", "115", "273", "40", "305", "269", "63", "294", "257", "50", "80", "230", "60", "256", "259", "46", "261", "283", "44", "237", "330", "70", "263", "255", "52", "262", "254", "40", "157", "287", "115", "69", "277", "273", "59", "329", "280", "74", "250", "257", "60", "331", "261", "45", "239", "269", "42", "288", "305", "63", "80", "283", "50", "294", "259", "70", "263", "237", "44", "255", "262", "46", "230", "157", "59", "256", "330", "52", "254", "277", "74", "280", "329", "69", "287", "115", "42", "239", "269", "40", "273"); + private static final DoubleMastersRun commonC = new DoubleMastersRun(true, "18", "35", "4", "17", "2", "30", "12", "27", "3", "13", "33", "30", "18", "12", "35", "27", "4", "3", "17", "13", "2", "33", "12", "4", "13", "18", "27", "17", "33", "30", "35", "3", "2", "18", "35", "4", "17", "2", "30", "12", "27", "3", "13", "33", "30", "18", "12", "35", "27", "4", "3", "17", "13", "2", "33", "12", "4", "13", "18", "27", "17", "33", "30", "35", "3", "2", "18", "35", "4", "17", "2", "30", "12", "27", "3", "13", "33", "30", "18", "12", "35", "27", "4", "3", "17", "13", "2", "33", "12", "4", "13", "18", "27", "17", "33", "30", "35", "3", "2", "18", "35", "4", "17", "2", "30", "12", "27", "3", "13", "33", "30", "18", "12", "35", "27", "4", "3", "17", "13", "2", "33"); + private static final DoubleMastersRun uncommonA = new DoubleMastersRun(true, "315", "244", "73", "274", "208", "147", "202", "169", "290", "102", "65", "285", "220", "67", "186", "246", "112", "222", "22", "301", "86", "62", "228", "161", "101", "302", "54", "184", "220", "307", "73", "93", "15", "119", "202", "291", "169", "323", "102", "244", "201", "172", "312", "290", "37", "208", "246", "6", "65", "307", "86", "186", "67", "222", "141", "15", "315", "22", "274", "161", "37", "101", "285", "228", "184", "112", "147", "302", "172", "54", "220", "141", "301", "6", "93", "119", "169", "323", "291", "312", "201", "62", "244", "184", "102", "15", "222", "37", "290", "112", "147", "65", "285", "101", "315", "67", "202", "186", "274", "208", "323", "73", "307", "228", "86", "161", "119", "246", "312", "6", "22", "172", "301", "62", "302", "141", "93", "291", "54", "201"); + private static final DoubleMastersRun uncommonB = new DoubleMastersRun(true, "217", "23", "49", "245", "91", "194", "148", "71", "16", "125", "238", "198", "180", "36", "278", "99", "224", "38", "232", "123", "68", "258", "229", "310", "120", "242", "188", "25", "66", "267", "138", "178", "281", "199", "89", "194", "241", "23", "49", "91", "245", "166", "134", "238", "217", "148", "36", "265", "16", "125", "198", "232", "71", "100", "267", "229", "180", "68", "278", "123", "25", "99", "241", "38", "120", "258", "199", "188", "224", "281", "310", "49", "23", "66", "138", "178", "245", "217", "166", "134", "242", "89", "36", "265", "148", "100", "242", "198", "180", "25", "238", "16", "194", "38", "91", "71", "125", "278", "229", "310", "68", "232", "123", "178", "99", "258", "188", "120", "267", "199", "89", "224", "241", "66", "134", "166", "281", "138", "100", "265"); + private static final DoubleMastersRun rareA = new DoubleMastersRun(false, "76", "231", "153", "77", "117", "118", "10", "43", "313", "158", "48", "85", "124", "252", "14", "167", "316", "318", "196", "127", "320", "130", "321", "97", "175", "271", "210", "272", "282", "26", "139", "103", "64", "325", "104", "179", "289", "32", "293", "326", "299", "327", "72", "109", "223", "225", "226", "75", "332", "113", "76", "231", "153", "77", "117", "118", "10", "43", "313", "158", "48", "85", "124", "252", "14", "167", "316", "318", "196", "127", "320", "130", "321", "97", "175", "271", "210", "272", "282", "26", "139", "103", "64", "325", "104", "179", "289", "32", "293", "326", "299", "327", "72", "109", "223", "225", "226", "75", "332", "113", "190", "8", "192", "240", "81", "314", "248", "164", "253", "51", "131", "204", "205", "20", "206", "136", "275", "214", "218", "303"); + private static final DoubleMastersRun rareB = new DoubleMastersRun(false, "122", "82", "317", "55", "264", "174", "324", "24", "284", "215", "328"); + private static final DoubleMastersRun rareC = new DoubleMastersRun(false, "189", "7", "311", "155", "236", "193", "11", "47", "249", "195", "128", "170", "260", "132", "203", "21", "268", "98", "57", "322", "209", "177", "279", "61", "212", "219", "292", "34", "183", "110", "149", "227", "306"); + private static final DoubleMastersRun rareD = new DoubleMastersRun(false, "309", "191", "233", "9", "156", "243", "88", "251", "319", "53", "129", "200", "171", "19", "266", "207", "58", "211", "276", "213", "142", "286", "106", "31", "221", "182", "39"); + private static final DoubleMastersRun rareE = new DoubleMastersRun(false, "5", "41", "152", "234", "235", "197", "94", "56", "1", "270", "107", "145", "295", "296", "297", "298", "300", "216", "185", "308"); + private static final DoubleMastersRun foilUncommonA = new DoubleMastersRun(false, "6", "119", "312", "244", "161", "246", "315", "86", "93", "15", "169", "201", "54", "172", "202", "208", "22", "274", "323", "101", "102", "62", "141", "65", "285", "67", "220", "290", "291", "147", "222", "301", "302", "73", "184", "37", "112", "186", "228", "307"); + private static final DoubleMastersRun foilUncommonB = new DoubleMastersRun(false, "310", "232", "120", "238", "241", "242", "245", "194", "123", "89", "91", "166", "49", "16", "125", "198", "199", "258", "265", "134", "267", "99", "23", "278", "100", "25", "281", "138", "178", "66", "217", "68", "180", "71", "36", "148", "224", "38", "188", "229"); + private static final DoubleMastersRun foilRareA = new DoubleMastersRun(false, "309", "231", "76", "189", "7", "153", "191", "233", "77", "9", "117", "311", "118", "155", "10", "236", "43", "193", "313", "156", "158", "243", "11", "122", "47", "82", "48", "85", "88", "124", "249", "251", "252", "14", "167", "195", "316", "317", "318", "196", "319", "127", "128", "53", "320", "170", "129", "260", "200", "171", "130", "321", "55", "132", "264", "203", "19", "266", "21", "174", "268", "207", "97", "98", "175", "57", "58", "271", "322", "209", "210", "211", "272", "276", "324", "177", "279", "24", "61", "282", "212", "26", "139", "284", "103", "64", "213", "142", "325", "104", "215", "286", "179", "219", "106", "289", "31", "32", "292", "293", "326", "221", "299", "34", "182", "327", "72", "109", "183", "223", "110", "149", "328", "225", "226", "227", "306", "75", "332", "113", "39"); + private static final DoubleMastersRun foilRareB = new DoubleMastersRun(false, "5", "41", "190", "8", "152", "234", "235", "192", "240", "81", "314", "248", "164", "253", "51", "197", "94", "131", "56", "204", "1", "205", "20", "206", "270", "136", "275", "214", "218", "107", "145", "295", "296", "297", "298", "300", "216", "303", "185", "308"); + + private DoubleMastersRun(boolean keepOrder, String... numbers) { + super(keepOrder, numbers); + } + } + + private static class DoubleMastersStructure extends BoosterStructure { + private static final DoubleMastersStructure C1 = new DoubleMastersStructure( + DoubleMastersRun.commonA, + DoubleMastersRun.commonA, + DoubleMastersRun.commonA, + DoubleMastersRun.commonA, + DoubleMastersRun.commonB, + DoubleMastersRun.commonB, + DoubleMastersRun.commonB, + DoubleMastersRun.commonC + ); + private static final DoubleMastersStructure C2 = new DoubleMastersStructure( + DoubleMastersRun.commonA, + DoubleMastersRun.commonA, + DoubleMastersRun.commonA, + DoubleMastersRun.commonB, + DoubleMastersRun.commonB, + DoubleMastersRun.commonB, + DoubleMastersRun.commonB, + DoubleMastersRun.commonC + ); + private static final DoubleMastersStructure C3 = new DoubleMastersStructure( + DoubleMastersRun.commonA, + DoubleMastersRun.commonA, + DoubleMastersRun.commonA, + DoubleMastersRun.commonA, + DoubleMastersRun.commonB, + DoubleMastersRun.commonB, + DoubleMastersRun.commonB, + DoubleMastersRun.commonB + ); + private static final DoubleMastersStructure U1 = new DoubleMastersStructure( + DoubleMastersRun.uncommonA, + DoubleMastersRun.uncommonB, + DoubleMastersRun.uncommonB + ); + private static final DoubleMastersStructure U2 = new DoubleMastersStructure( + DoubleMastersRun.uncommonA, + DoubleMastersRun.uncommonA, + DoubleMastersRun.uncommonB + ); + private static final DoubleMastersStructure R1 = new DoubleMastersStructure( + DoubleMastersRun.rareA, + DoubleMastersRun.rareC + ); + private static final DoubleMastersStructure R2 = new DoubleMastersStructure( + DoubleMastersRun.rareA, + DoubleMastersRun.rareD + ); + private static final DoubleMastersStructure R3 = new DoubleMastersStructure( + DoubleMastersRun.rareA, + DoubleMastersRun.rareE + ); + private static final DoubleMastersStructure R4 = new DoubleMastersStructure( + DoubleMastersRun.rareB, + DoubleMastersRun.rareC + ); + private static final DoubleMastersStructure R5 = new DoubleMastersStructure( + DoubleMastersRun.rareB, + DoubleMastersRun.rareD + ); + private static final DoubleMastersStructure R6 = new DoubleMastersStructure( + DoubleMastersRun.rareB, + DoubleMastersRun.rareE + ); + private static final DoubleMastersStructure F01 = new DoubleMastersStructure( + DoubleMastersRun.commonA, + DoubleMastersRun.commonB + ); + private static final DoubleMastersStructure F02 = new DoubleMastersStructure( + DoubleMastersRun.commonA, + DoubleMastersRun.commonC + ); + private static final DoubleMastersStructure F03 = new DoubleMastersStructure( + DoubleMastersRun.commonA, + DoubleMastersRun.foilUncommonA + ); + private static final DoubleMastersStructure F04 = new DoubleMastersStructure( + DoubleMastersRun.commonA, + DoubleMastersRun.foilUncommonB + ); + private static final DoubleMastersStructure F05 = new DoubleMastersStructure( + DoubleMastersRun.commonA, + DoubleMastersRun.foilRareA + ); + private static final DoubleMastersStructure F06 = new DoubleMastersStructure( + DoubleMastersRun.commonA, + DoubleMastersRun.foilRareB + ); + private static final DoubleMastersStructure F07 = new DoubleMastersStructure( + DoubleMastersRun.commonB, + DoubleMastersRun.commonC + ); + private static final DoubleMastersStructure F08 = new DoubleMastersStructure( + DoubleMastersRun.commonB, + DoubleMastersRun.foilUncommonA + ); + private static final DoubleMastersStructure F09 = new DoubleMastersStructure( + DoubleMastersRun.commonB, + DoubleMastersRun.foilUncommonB + ); + private static final DoubleMastersStructure F10 = new DoubleMastersStructure( + DoubleMastersRun.commonB, + DoubleMastersRun.foilRareA + ); + private static final DoubleMastersStructure F11 = new DoubleMastersStructure( + DoubleMastersRun.commonB, + DoubleMastersRun.foilRareB + ); + private static final DoubleMastersStructure F12 = new DoubleMastersStructure( + DoubleMastersRun.commonC, + DoubleMastersRun.foilUncommonA + ); + private static final DoubleMastersStructure F13 = new DoubleMastersStructure( + DoubleMastersRun.commonC, + DoubleMastersRun.foilUncommonB + ); + private static final DoubleMastersStructure F14 = new DoubleMastersStructure( + DoubleMastersRun.commonC, + DoubleMastersRun.foilRareA + ); + private static final DoubleMastersStructure F15 = new DoubleMastersStructure( + DoubleMastersRun.commonC, + DoubleMastersRun.foilRareB + ); + private static final DoubleMastersStructure F16 = new DoubleMastersStructure( + DoubleMastersRun.foilUncommonA, + DoubleMastersRun.foilUncommonB + ); + private static final DoubleMastersStructure F17 = new DoubleMastersStructure( + DoubleMastersRun.foilUncommonA, + DoubleMastersRun.foilRareA + ); + private static final DoubleMastersStructure F18 = new DoubleMastersStructure( + DoubleMastersRun.foilUncommonA, + DoubleMastersRun.foilRareB + ); + private static final DoubleMastersStructure F19 = new DoubleMastersStructure( + DoubleMastersRun.foilUncommonB, + DoubleMastersRun.foilRareA + ); + private static final DoubleMastersStructure F20 = new DoubleMastersStructure( + DoubleMastersRun.foilUncommonB, + DoubleMastersRun.foilRareB + ); + private static final DoubleMastersStructure F21 = new DoubleMastersStructure( + DoubleMastersRun.foilRareA, + DoubleMastersRun.foilRareB + ); + + + private DoubleMastersStructure(CardRun... runs) { + super(runs); + } + } + + private final RarityConfiguration commonRuns = new RarityConfiguration( + false, + DoubleMastersStructure.C1, DoubleMastersStructure.C2, DoubleMastersStructure.C1, DoubleMastersStructure.C2, + DoubleMastersStructure.C1, DoubleMastersStructure.C2, DoubleMastersStructure.C1, DoubleMastersStructure.C2, + DoubleMastersStructure.C1, DoubleMastersStructure.C2, DoubleMastersStructure.C1, DoubleMastersStructure.C2, + DoubleMastersStructure.C1, DoubleMastersStructure.C2, DoubleMastersStructure.C1, DoubleMastersStructure.C2, + DoubleMastersStructure.C1, DoubleMastersStructure.C2, DoubleMastersStructure.C1, DoubleMastersStructure.C2, + DoubleMastersStructure.C1, DoubleMastersStructure.C2, DoubleMastersStructure.C1, DoubleMastersStructure.C2, + DoubleMastersStructure.C1, DoubleMastersStructure.C2, DoubleMastersStructure.C1, DoubleMastersStructure.C2, + DoubleMastersStructure.C1, DoubleMastersStructure.C2, DoubleMastersStructure.C1, DoubleMastersStructure.C2, + DoubleMastersStructure.C1, DoubleMastersStructure.C2, DoubleMastersStructure.C1, DoubleMastersStructure.C2, + DoubleMastersStructure.C1, DoubleMastersStructure.C2, DoubleMastersStructure.C1, DoubleMastersStructure.C2, + DoubleMastersStructure.C1, DoubleMastersStructure.C2, DoubleMastersStructure.C1, DoubleMastersStructure.C2, + DoubleMastersStructure.C3, DoubleMastersStructure.C3, DoubleMastersStructure.C3 + ); + private final RarityConfiguration uncommonRuns = new RarityConfiguration( + DoubleMastersStructure.U1, DoubleMastersStructure.U2 + ); + private final RarityConfiguration rareRuns = new RarityConfiguration( + false, + DoubleMastersStructure.R1, DoubleMastersStructure.R2, DoubleMastersStructure.R3, + DoubleMastersStructure.R4, DoubleMastersStructure.R5, DoubleMastersStructure.R6 + ); + private final RarityConfiguration foilRuns = new RarityConfiguration( + false, + DoubleMastersStructure.F01, DoubleMastersStructure.F01, DoubleMastersStructure.F01, + DoubleMastersStructure.F01, DoubleMastersStructure.F01, DoubleMastersStructure.F01, + DoubleMastersStructure.F01, DoubleMastersStructure.F01, DoubleMastersStructure.F01, + DoubleMastersStructure.F01, DoubleMastersStructure.F01, DoubleMastersStructure.F01, + DoubleMastersStructure.F01, DoubleMastersStructure.F01, DoubleMastersStructure.F01, + DoubleMastersStructure.F01, DoubleMastersStructure.F01, DoubleMastersStructure.F01, + DoubleMastersStructure.F01, DoubleMastersStructure.F01, DoubleMastersStructure.F01, + DoubleMastersStructure.F01, DoubleMastersStructure.F01, DoubleMastersStructure.F01, + DoubleMastersStructure.F01, DoubleMastersStructure.F01, DoubleMastersStructure.F01, + DoubleMastersStructure.F01, DoubleMastersStructure.F01, DoubleMastersStructure.F01, + DoubleMastersStructure.F01, DoubleMastersStructure.F01, DoubleMastersStructure.F01, + DoubleMastersStructure.F01, DoubleMastersStructure.F01, DoubleMastersStructure.F01, + DoubleMastersStructure.F01, DoubleMastersStructure.F01, DoubleMastersStructure.F01, + DoubleMastersStructure.F01, DoubleMastersStructure.F01, DoubleMastersStructure.F01, + DoubleMastersStructure.F01, DoubleMastersStructure.F01, DoubleMastersStructure.F01, + DoubleMastersStructure.F01, DoubleMastersStructure.F01, DoubleMastersStructure.F01, + DoubleMastersStructure.F01, DoubleMastersStructure.F01, DoubleMastersStructure.F01, + DoubleMastersStructure.F01, DoubleMastersStructure.F01, DoubleMastersStructure.F01, + DoubleMastersStructure.F01, DoubleMastersStructure.F01, DoubleMastersStructure.F01, + DoubleMastersStructure.F01, DoubleMastersStructure.F01, DoubleMastersStructure.F01, + + DoubleMastersStructure.F02, DoubleMastersStructure.F02, DoubleMastersStructure.F02, + DoubleMastersStructure.F02, DoubleMastersStructure.F02, DoubleMastersStructure.F02, + DoubleMastersStructure.F02, DoubleMastersStructure.F02, DoubleMastersStructure.F02, + DoubleMastersStructure.F02, DoubleMastersStructure.F02, DoubleMastersStructure.F02, + DoubleMastersStructure.F02, DoubleMastersStructure.F02, DoubleMastersStructure.F02, + DoubleMastersStructure.F02, DoubleMastersStructure.F02, DoubleMastersStructure.F02, + DoubleMastersStructure.F02, DoubleMastersStructure.F02, DoubleMastersStructure.F02, + DoubleMastersStructure.F02, DoubleMastersStructure.F02, DoubleMastersStructure.F02, + DoubleMastersStructure.F02, DoubleMastersStructure.F02, DoubleMastersStructure.F02, + DoubleMastersStructure.F02, DoubleMastersStructure.F02, DoubleMastersStructure.F02, + DoubleMastersStructure.F02, DoubleMastersStructure.F02, DoubleMastersStructure.F02, + DoubleMastersStructure.F02, DoubleMastersStructure.F02, DoubleMastersStructure.F02, + DoubleMastersStructure.F02, DoubleMastersStructure.F02, DoubleMastersStructure.F02, + DoubleMastersStructure.F02, DoubleMastersStructure.F02, DoubleMastersStructure.F02, + DoubleMastersStructure.F02, DoubleMastersStructure.F02, DoubleMastersStructure.F02, + DoubleMastersStructure.F02, DoubleMastersStructure.F02, DoubleMastersStructure.F02, + DoubleMastersStructure.F02, DoubleMastersStructure.F02, DoubleMastersStructure.F02, + DoubleMastersStructure.F02, DoubleMastersStructure.F02, DoubleMastersStructure.F02, + DoubleMastersStructure.F02, DoubleMastersStructure.F02, DoubleMastersStructure.F02, + DoubleMastersStructure.F02, DoubleMastersStructure.F02, DoubleMastersStructure.F02, + + DoubleMastersStructure.F03, DoubleMastersStructure.F03, DoubleMastersStructure.F03, + DoubleMastersStructure.F03, DoubleMastersStructure.F03, DoubleMastersStructure.F03, + DoubleMastersStructure.F03, DoubleMastersStructure.F03, DoubleMastersStructure.F03, + DoubleMastersStructure.F03, DoubleMastersStructure.F03, DoubleMastersStructure.F03, + DoubleMastersStructure.F03, DoubleMastersStructure.F03, DoubleMastersStructure.F03, + DoubleMastersStructure.F03, + + DoubleMastersStructure.F04, DoubleMastersStructure.F04, DoubleMastersStructure.F04, + DoubleMastersStructure.F04, DoubleMastersStructure.F04, DoubleMastersStructure.F04, + DoubleMastersStructure.F04, DoubleMastersStructure.F04, DoubleMastersStructure.F04, + DoubleMastersStructure.F04, DoubleMastersStructure.F04, DoubleMastersStructure.F04, + DoubleMastersStructure.F04, DoubleMastersStructure.F04, DoubleMastersStructure.F04, + DoubleMastersStructure.F04, + + DoubleMastersStructure.F05, DoubleMastersStructure.F05, DoubleMastersStructure.F05, + DoubleMastersStructure.F05, DoubleMastersStructure.F05, DoubleMastersStructure.F05, + DoubleMastersStructure.F05, DoubleMastersStructure.F05, + + DoubleMastersStructure.F07, DoubleMastersStructure.F07, DoubleMastersStructure.F07, + DoubleMastersStructure.F07, DoubleMastersStructure.F07, DoubleMastersStructure.F07, + DoubleMastersStructure.F07, DoubleMastersStructure.F07, DoubleMastersStructure.F07, + DoubleMastersStructure.F07, DoubleMastersStructure.F07, DoubleMastersStructure.F07, + DoubleMastersStructure.F07, DoubleMastersStructure.F07, DoubleMastersStructure.F07, + DoubleMastersStructure.F07, DoubleMastersStructure.F07, DoubleMastersStructure.F07, + DoubleMastersStructure.F07, DoubleMastersStructure.F07, DoubleMastersStructure.F07, + DoubleMastersStructure.F07, DoubleMastersStructure.F07, DoubleMastersStructure.F07, + DoubleMastersStructure.F07, DoubleMastersStructure.F07, DoubleMastersStructure.F07, + DoubleMastersStructure.F07, DoubleMastersStructure.F07, DoubleMastersStructure.F07, + DoubleMastersStructure.F07, DoubleMastersStructure.F07, DoubleMastersStructure.F07, + DoubleMastersStructure.F07, DoubleMastersStructure.F07, DoubleMastersStructure.F07, + DoubleMastersStructure.F07, DoubleMastersStructure.F07, DoubleMastersStructure.F07, + DoubleMastersStructure.F07, DoubleMastersStructure.F07, DoubleMastersStructure.F07, + DoubleMastersStructure.F07, DoubleMastersStructure.F07, DoubleMastersStructure.F07, + DoubleMastersStructure.F07, DoubleMastersStructure.F07, DoubleMastersStructure.F07, + DoubleMastersStructure.F07, DoubleMastersStructure.F07, DoubleMastersStructure.F07, + DoubleMastersStructure.F07, DoubleMastersStructure.F07, DoubleMastersStructure.F07, + DoubleMastersStructure.F07, DoubleMastersStructure.F07, DoubleMastersStructure.F07, + DoubleMastersStructure.F07, DoubleMastersStructure.F07, DoubleMastersStructure.F07, + + DoubleMastersStructure.F08, DoubleMastersStructure.F08, DoubleMastersStructure.F08, + DoubleMastersStructure.F08, DoubleMastersStructure.F08, DoubleMastersStructure.F08, + DoubleMastersStructure.F08, DoubleMastersStructure.F08, DoubleMastersStructure.F08, + DoubleMastersStructure.F08, DoubleMastersStructure.F08, DoubleMastersStructure.F08, + DoubleMastersStructure.F08, DoubleMastersStructure.F08, DoubleMastersStructure.F08, + DoubleMastersStructure.F08, + + DoubleMastersStructure.F09, DoubleMastersStructure.F09, DoubleMastersStructure.F09, + DoubleMastersStructure.F09, DoubleMastersStructure.F09, DoubleMastersStructure.F09, + DoubleMastersStructure.F09, DoubleMastersStructure.F09, DoubleMastersStructure.F09, + DoubleMastersStructure.F09, DoubleMastersStructure.F09, DoubleMastersStructure.F09, + DoubleMastersStructure.F09, DoubleMastersStructure.F09, DoubleMastersStructure.F09, + DoubleMastersStructure.F09, + + DoubleMastersStructure.F10, DoubleMastersStructure.F10, DoubleMastersStructure.F10, + DoubleMastersStructure.F10, DoubleMastersStructure.F10, DoubleMastersStructure.F10, + DoubleMastersStructure.F10, DoubleMastersStructure.F10, + + DoubleMastersStructure.F12, DoubleMastersStructure.F12, DoubleMastersStructure.F12, + DoubleMastersStructure.F12, DoubleMastersStructure.F12, DoubleMastersStructure.F12, + DoubleMastersStructure.F12, DoubleMastersStructure.F12, DoubleMastersStructure.F12, + DoubleMastersStructure.F12, DoubleMastersStructure.F12, DoubleMastersStructure.F12, + DoubleMastersStructure.F12, DoubleMastersStructure.F12, DoubleMastersStructure.F12, + DoubleMastersStructure.F12, + + DoubleMastersStructure.F13, DoubleMastersStructure.F13, DoubleMastersStructure.F13, + DoubleMastersStructure.F13, DoubleMastersStructure.F13, DoubleMastersStructure.F13, + DoubleMastersStructure.F13, DoubleMastersStructure.F13, DoubleMastersStructure.F13, + DoubleMastersStructure.F13, DoubleMastersStructure.F13, DoubleMastersStructure.F13, + DoubleMastersStructure.F13, DoubleMastersStructure.F13, DoubleMastersStructure.F13, + DoubleMastersStructure.F13, + + DoubleMastersStructure.F14, DoubleMastersStructure.F14, DoubleMastersStructure.F14, + DoubleMastersStructure.F14, DoubleMastersStructure.F14, DoubleMastersStructure.F14, + DoubleMastersStructure.F14, DoubleMastersStructure.F14, + + DoubleMastersStructure.F16, DoubleMastersStructure.F16, DoubleMastersStructure.F16, + DoubleMastersStructure.F16, DoubleMastersStructure.F16, DoubleMastersStructure.F16, + DoubleMastersStructure.F16, DoubleMastersStructure.F16, DoubleMastersStructure.F16, + DoubleMastersStructure.F16, DoubleMastersStructure.F16, DoubleMastersStructure.F16, + DoubleMastersStructure.F16, DoubleMastersStructure.F16, DoubleMastersStructure.F16, + DoubleMastersStructure.F16, + + DoubleMastersStructure.F17, DoubleMastersStructure.F17, DoubleMastersStructure.F17, + DoubleMastersStructure.F17, DoubleMastersStructure.F17, DoubleMastersStructure.F17, + DoubleMastersStructure.F17, DoubleMastersStructure.F17, + + DoubleMastersStructure.F19, DoubleMastersStructure.F19, DoubleMastersStructure.F19, + DoubleMastersStructure.F19, DoubleMastersStructure.F19, DoubleMastersStructure.F19, + DoubleMastersStructure.F19, DoubleMastersStructure.F19, + + DoubleMastersStructure.F06, DoubleMastersStructure.F11, DoubleMastersStructure.F15, + DoubleMastersStructure.F18, DoubleMastersStructure.F20, DoubleMastersStructure.F21 + ); + + @Override + public void shuffle() { + commonRuns.shuffle(); + uncommonRuns.shuffle(); + rareRuns.shuffle(); + foilRuns.shuffle(); + } + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(foilRuns.getNext().makeRun()); + return booster; + } +} diff --git a/Mage.Sets/src/mage/sets/DuelDecksGarrukVsLiliana.java b/Mage.Sets/src/mage/sets/DuelDecksGarrukVsLiliana.java index ec381e29506c..a3254290807d 100644 --- a/Mage.Sets/src/mage/sets/DuelDecksGarrukVsLiliana.java +++ b/Mage.Sets/src/mage/sets/DuelDecksGarrukVsLiliana.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.sets; import mage.cards.ExpansionSet; diff --git a/Mage.Sets/src/mage/sets/DuelDecksJaceVsChandra.java b/Mage.Sets/src/mage/sets/DuelDecksJaceVsChandra.java index 1592d0482b4f..2cfa376181a1 100644 --- a/Mage.Sets/src/mage/sets/DuelDecksJaceVsChandra.java +++ b/Mage.Sets/src/mage/sets/DuelDecksJaceVsChandra.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.sets; import mage.cards.ExpansionSet; diff --git a/Mage.Sets/src/mage/sets/HistoricAnthology2.java b/Mage.Sets/src/mage/sets/HistoricAnthology2.java index 8bd30fee529d..9a1099f89d9d 100644 --- a/Mage.Sets/src/mage/sets/HistoricAnthology2.java +++ b/Mage.Sets/src/mage/sets/HistoricAnthology2.java @@ -6,6 +6,7 @@ /** * https://scryfall.com/sets/ha2 + * * @author mikalinn777 */ public final class HistoricAnthology2 extends ExpansionSet { @@ -20,6 +21,7 @@ private HistoricAnthology2() { super("Historic Anthology 2", "HA2", ExpansionSet.buildDate(2020, 3, 12), SetType.MAGIC_ARENA); this.hasBoosters = false; this.hasBasicLands = false; + cards.add(new SetCardInfo("Ancestral Mask", 13, Rarity.COMMON, mage.cards.a.AncestralMask.class)); cards.add(new SetCardInfo("Barren Moor", 19, Rarity.COMMON, mage.cards.b.BarrenMoor.class)); cards.add(new SetCardInfo("Bojuka Bog", 20, Rarity.COMMON, mage.cards.b.BojukaBog.class)); @@ -45,5 +47,5 @@ private HistoricAnthology2() { cards.add(new SetCardInfo("Tranquil Thicket", 25, Rarity.COMMON, mage.cards.t.TranquilThicket.class)); cards.add(new SetCardInfo("Virulent Plague", 9, Rarity.UNCOMMON, mage.cards.v.VirulentPlague.class)); cards.add(new SetCardInfo("Waste Not", 10, Rarity.RARE, mage.cards.w.WasteNot.class)); - } + } } diff --git a/Mage.Sets/src/mage/sets/HistoricAnthology3.java b/Mage.Sets/src/mage/sets/HistoricAnthology3.java index 736df3448adc..d7d6f7ea3c83 100644 --- a/Mage.Sets/src/mage/sets/HistoricAnthology3.java +++ b/Mage.Sets/src/mage/sets/HistoricAnthology3.java @@ -1,4 +1,3 @@ - package mage.sets; import mage.cards.ExpansionSet; @@ -21,6 +20,7 @@ private HistoricAnthology3() { this.blockName = "Reprint"; this.hasBoosters = false; this.hasBasicLands = false; + cards.add(new SetCardInfo("Ancient Ziggurat", 26, Rarity.UNCOMMON, mage.cards.a.AncientZiggurat.class)); cards.add(new SetCardInfo("Akroma's Memorial", 24, Rarity.MYTHIC, mage.cards.a.AkromasMemorial.class)); cards.add(new SetCardInfo("Body Double", 6, Rarity.RARE, mage.cards.b.BodyDouble.class)); @@ -48,8 +48,5 @@ private HistoricAnthology3() { cards.add(new SetCardInfo("Timely Reinforcements", 5, Rarity.UNCOMMON, mage.cards.t.TimelyReinforcements.class)); cards.add(new SetCardInfo("Ulamog, the Ceaseless Hunger", 1, Rarity.MYTHIC, mage.cards.u.UlamogTheCeaselessHunger.class)); cards.add(new SetCardInfo("Unburial Rites", 14, Rarity.UNCOMMON, mage.cards.u.UnburialRites.class)); - - } - } diff --git a/Mage.Sets/src/mage/sets/HistoricAnthology4.java b/Mage.Sets/src/mage/sets/HistoricAnthology4.java new file mode 100644 index 000000000000..dd7e3e73c4af --- /dev/null +++ b/Mage.Sets/src/mage/sets/HistoricAnthology4.java @@ -0,0 +1,50 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * @author TheElk801 + */ +public final class HistoricAnthology4 extends ExpansionSet { + + private static final HistoricAnthology4 instance = new HistoricAnthology4(); + + public static HistoricAnthology4 getInstance() { + return instance; + } + + private HistoricAnthology4() { + super("Historic Anthology 4", "HA4", ExpansionSet.buildDate(2021, 3, 11), SetType.MAGIC_ARENA); + this.blockName = "Reprint"; + this.hasBoosters = false; + this.hasBasicLands = false; + + cards.add(new SetCardInfo("Abomination of Llanowar", 20, Rarity.UNCOMMON, mage.cards.a.AbominationOfLlanowar.class)); + cards.add(new SetCardInfo("Adorned Pouncer", 1, Rarity.RARE, mage.cards.a.AdornedPouncer.class)); + cards.add(new SetCardInfo("Ammit Eternal", 8, Rarity.RARE, mage.cards.a.AmmitEternal.class)); + cards.add(new SetCardInfo("Blinkmoth Nexus", 25, Rarity.RARE, mage.cards.b.BlinkmothNexus.class)); + cards.add(new SetCardInfo("Bonesplitter", 21, Rarity.COMMON, mage.cards.b.Bonesplitter.class)); + cards.add(new SetCardInfo("Coldsteel Heart", 22, Rarity.UNCOMMON, mage.cards.c.ColdsteelHeart.class)); + cards.add(new SetCardInfo("Collected Conjuring", 19, Rarity.RARE, mage.cards.c.CollectedConjuring.class)); + cards.add(new SetCardInfo("Death's Shadow", 9, Rarity.RARE, mage.cards.d.DeathsShadow.class)); + cards.add(new SetCardInfo("Declaration in Stone", 2, Rarity.RARE, mage.cards.d.DeclarationInStone.class)); + cards.add(new SetCardInfo("Faith of the Devoted", 10, Rarity.UNCOMMON, mage.cards.f.FaithOfTheDevoted.class)); + cards.add(new SetCardInfo("Flameblade Adept", 12, Rarity.UNCOMMON, mage.cards.f.FlamebladeAdept.class)); + cards.add(new SetCardInfo("Goblin Gaveleer", 13, Rarity.COMMON, mage.cards.g.GoblinGaveleer.class)); + cards.add(new SetCardInfo("Hamza, Guardian of Arashin", 18, Rarity.UNCOMMON, mage.cards.h.HamzaGuardianOfArashin.class)); + cards.add(new SetCardInfo("Harmless Offering", 14, Rarity.RARE, mage.cards.h.HarmlessOffering.class)); + cards.add(new SetCardInfo("Iceberg Cancrix", 5, Rarity.COMMON, mage.cards.i.IcebergCancrix.class)); + cards.add(new SetCardInfo("Inspiring Statuary", 23, Rarity.RARE, mage.cards.i.InspiringStatuary.class)); + cards.add(new SetCardInfo("Lys Alana Huntmaster", 15, Rarity.COMMON, mage.cards.l.LysAlanaHuntmaster.class)); + cards.add(new SetCardInfo("Marit Lage's Slumber", 6, Rarity.RARE, mage.cards.m.MaritLagesSlumber.class)); + cards.add(new SetCardInfo("Sawtusk Demolisher", 16, Rarity.RARE, mage.cards.s.SawtuskDemolisher.class)); + cards.add(new SetCardInfo("Spider Spawning", 17, Rarity.UNCOMMON, mage.cards.s.SpiderSpawning.class)); + cards.add(new SetCardInfo("Sword of Body and Mind", 24, Rarity.MYTHIC, mage.cards.s.SwordOfBodyAndMind.class)); + cards.add(new SetCardInfo("Think Twice", 7, Rarity.COMMON, mage.cards.t.ThinkTwice.class)); + cards.add(new SetCardInfo("Thraben Inspector", 3, Rarity.COMMON, mage.cards.t.ThrabenInspector.class)); + cards.add(new SetCardInfo("Torment of Scarabs", 11, Rarity.UNCOMMON, mage.cards.t.TormentOfScarabs.class)); + cards.add(new SetCardInfo("Triumphant Reckoning", 4, Rarity.MYTHIC, mage.cards.t.TriumphantReckoning.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/Ixalan.java b/Mage.Sets/src/mage/sets/Ixalan.java index 9f9cdc945a76..6846697d7db3 100644 --- a/Mage.Sets/src/mage/sets/Ixalan.java +++ b/Mage.Sets/src/mage/sets/Ixalan.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.sets; import mage.cards.ExpansionSet; diff --git a/Mage.Sets/src/mage/sets/KaladeshRemastered.java b/Mage.Sets/src/mage/sets/KaladeshRemastered.java new file mode 100644 index 000000000000..89844ee7387c --- /dev/null +++ b/Mage.Sets/src/mage/sets/KaladeshRemastered.java @@ -0,0 +1,336 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.cards.repository.CardInfo; +import mage.constants.Rarity; +import mage.constants.SetType; + +import java.util.ArrayList; +import java.util.List; + +/** + * https://scryfall.com/sets/klr + */ +public class KaladeshRemastered extends ExpansionSet { + + private static final KaladeshRemastered instance = new KaladeshRemastered(); + + public static KaladeshRemastered getInstance() { + return instance; + } + + private KaladeshRemastered() { + super("Kaladesh Remastered", "KLR", ExpansionSet.buildDate(2020, 11, 12), SetType.MAGIC_ARENA); + this.hasBoosters = true; + this.hasBasicLands = true; + this.maxCardNumberInBooster = 301; + this.numBoosterLands = 1; + this.numBoosterCommon = 10; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 8; + + cards.add(new SetCardInfo("Aerial Responder", 1, Rarity.UNCOMMON, mage.cards.a.AerialResponder.class)); + cards.add(new SetCardInfo("Aeronaut Admiral", 2, Rarity.UNCOMMON, mage.cards.a.AeronautAdmiral.class)); + cards.add(new SetCardInfo("Aether Chaser", 113, Rarity.COMMON, mage.cards.a.AetherChaser.class)); + cards.add(new SetCardInfo("Aether Hub", 279, Rarity.UNCOMMON, mage.cards.a.AetherHub.class)); + cards.add(new SetCardInfo("Aether Inspector", 3, Rarity.COMMON, mage.cards.a.AetherInspector.class)); + cards.add(new SetCardInfo("Aether Meltdown", 38, Rarity.UNCOMMON, mage.cards.a.AetherMeltdown.class)); + cards.add(new SetCardInfo("Aether Poisoner", 75, Rarity.COMMON, mage.cards.a.AetherPoisoner.class)); + cards.add(new SetCardInfo("Aether Swooper", 39, Rarity.COMMON, mage.cards.a.AetherSwooper.class)); + cards.add(new SetCardInfo("Aether Theorist", 40, Rarity.COMMON, mage.cards.a.AetherTheorist.class)); + cards.add(new SetCardInfo("Aether Tradewinds", 41, Rarity.COMMON, mage.cards.a.AetherTradewinds.class)); + cards.add(new SetCardInfo("Aetherborn Marauder", 76, Rarity.UNCOMMON, mage.cards.a.AetherbornMarauder.class)); + cards.add(new SetCardInfo("Aetherflux Reservoir", 217, Rarity.RARE, mage.cards.a.AetherfluxReservoir.class)); + cards.add(new SetCardInfo("Aethersphere Harvester", 218, Rarity.RARE, mage.cards.a.AethersphereHarvester.class)); + cards.add(new SetCardInfo("Aetherstorm Roc", 4, Rarity.RARE, mage.cards.a.AetherstormRoc.class)); + cards.add(new SetCardInfo("Aethertorch Renegade", 114, Rarity.UNCOMMON, mage.cards.a.AethertorchRenegade.class)); + cards.add(new SetCardInfo("Aetherworks Marvel", 219, Rarity.MYTHIC, mage.cards.a.AetherworksMarvel.class)); + cards.add(new SetCardInfo("Airdrop Aeronauts", 5, Rarity.UNCOMMON, mage.cards.a.AirdropAeronauts.class)); + cards.add(new SetCardInfo("Ajani Unyielding", 188, Rarity.MYTHIC, mage.cards.a.AjaniUnyielding.class)); + cards.add(new SetCardInfo("Alley Evasion", 6, Rarity.COMMON, mage.cards.a.AlleyEvasion.class)); + cards.add(new SetCardInfo("Alley Strangler", 77, Rarity.COMMON, mage.cards.a.AlleyStrangler.class)); + cards.add(new SetCardInfo("Angel of Invention", 7, Rarity.MYTHIC, mage.cards.a.AngelOfInvention.class)); + cards.add(new SetCardInfo("Animation Module", 220, Rarity.RARE, mage.cards.a.AnimationModule.class)); + cards.add(new SetCardInfo("Appetite for the Unnatural", 151, Rarity.COMMON, mage.cards.a.AppetiteForTheUnnatural.class)); + cards.add(new SetCardInfo("Arborback Stomper", 152, Rarity.UNCOMMON, mage.cards.a.ArborbackStomper.class)); + cards.add(new SetCardInfo("Armorcraft Judge", 153, Rarity.UNCOMMON, mage.cards.a.ArmorcraftJudge.class)); + cards.add(new SetCardInfo("Attune with Aether", 154, Rarity.COMMON, mage.cards.a.AttuneWithAether.class)); + cards.add(new SetCardInfo("Audacious Infiltrator", 8, Rarity.COMMON, mage.cards.a.AudaciousInfiltrator.class)); + cards.add(new SetCardInfo("Authority of the Consuls", 9, Rarity.RARE, mage.cards.a.AuthorityOfTheConsuls.class)); + cards.add(new SetCardInfo("Aviary Mechanic", 10, Rarity.COMMON, mage.cards.a.AviaryMechanic.class)); + cards.add(new SetCardInfo("Ballista Charger", 221, Rarity.UNCOMMON, mage.cards.b.BallistaCharger.class)); + cards.add(new SetCardInfo("Baral's Expertise", 43, Rarity.RARE, mage.cards.b.BaralsExpertise.class)); + cards.add(new SetCardInfo("Baral, Chief of Compliance", 42, Rarity.RARE, mage.cards.b.BaralChiefOfCompliance.class)); + cards.add(new SetCardInfo("Barricade Breaker", 222, Rarity.UNCOMMON, mage.cards.b.BarricadeBreaker.class)); + cards.add(new SetCardInfo("Bastion Mastodon", 223, Rarity.COMMON, mage.cards.b.BastionMastodon.class)); + cards.add(new SetCardInfo("Blooming Marsh", 280, Rarity.RARE, mage.cards.b.BloomingMarsh.class)); + cards.add(new SetCardInfo("Blossoming Defense", 155, Rarity.UNCOMMON, mage.cards.b.BlossomingDefense.class)); + cards.add(new SetCardInfo("Bomat Bazaar Barge", 224, Rarity.UNCOMMON, mage.cards.b.BomatBazaarBarge.class)); + cards.add(new SetCardInfo("Bomat Courier", 225, Rarity.RARE, mage.cards.b.BomatCourier.class)); + cards.add(new SetCardInfo("Botanical Sanctum", 281, Rarity.RARE, mage.cards.b.BotanicalSanctum.class)); + cards.add(new SetCardInfo("Bristling Hydra", 156, Rarity.RARE, mage.cards.b.BristlingHydra.class)); + cards.add(new SetCardInfo("Built to Last", 11, Rarity.COMMON, mage.cards.b.BuiltToLast.class)); + cards.add(new SetCardInfo("Built to Smash", 115, Rarity.COMMON, mage.cards.b.BuiltToSmash.class)); + cards.add(new SetCardInfo("Cataclysmic Gearhulk", 12, Rarity.MYTHIC, mage.cards.c.CataclysmicGearhulk.class)); + cards.add(new SetCardInfo("Cathartic Reunion", 116, Rarity.COMMON, mage.cards.c.CatharticReunion.class)); + cards.add(new SetCardInfo("Ceremonious Rejection", 44, Rarity.UNCOMMON, mage.cards.c.CeremoniousRejection.class)); + cards.add(new SetCardInfo("Chandra's Pyrohelix", 118, Rarity.COMMON, mage.cards.c.ChandrasPyrohelix.class)); + cards.add(new SetCardInfo("Chandra's Revolution", 119, Rarity.COMMON, mage.cards.c.ChandrasRevolution.class)); + cards.add(new SetCardInfo("Chandra, Torch of Defiance", 117, Rarity.MYTHIC, mage.cards.c.ChandraTorchOfDefiance.class)); + cards.add(new SetCardInfo("Chief of the Foundry", 226, Rarity.UNCOMMON, mage.cards.c.ChiefOfTheFoundry.class)); + cards.add(new SetCardInfo("Cloudblazer", 189, Rarity.UNCOMMON, mage.cards.c.Cloudblazer.class)); + cards.add(new SetCardInfo("Cogworker's Puzzleknot", 227, Rarity.COMMON, mage.cards.c.CogworkersPuzzleknot.class)); + cards.add(new SetCardInfo("Combustible Gearhulk", 120, Rarity.MYTHIC, mage.cards.c.CombustibleGearhulk.class)); + cards.add(new SetCardInfo("Commencement of Festivities", 157, Rarity.COMMON, mage.cards.c.CommencementOfFestivities.class)); + cards.add(new SetCardInfo("Concealed Courtyard", 282, Rarity.RARE, mage.cards.c.ConcealedCourtyard.class)); + cards.add(new SetCardInfo("Confiscation Coup", 45, Rarity.RARE, mage.cards.c.ConfiscationCoup.class)); + cards.add(new SetCardInfo("Consulate Skygate", 228, Rarity.COMMON, mage.cards.c.ConsulateSkygate.class)); + cards.add(new SetCardInfo("Consulate Turret", 229, Rarity.COMMON, mage.cards.c.ConsulateTurret.class)); + cards.add(new SetCardInfo("Contraband Kingpin", 190, Rarity.UNCOMMON, mage.cards.c.ContrabandKingpin.class)); + cards.add(new SetCardInfo("Conviction", 13, Rarity.COMMON, mage.cards.c.Conviction.class)); + cards.add(new SetCardInfo("Countless Gears Renegade", 14, Rarity.COMMON, mage.cards.c.CountlessGearsRenegade.class)); + cards.add(new SetCardInfo("Creeping Mold", 158, Rarity.UNCOMMON, mage.cards.c.CreepingMold.class)); + cards.add(new SetCardInfo("Cultivator's Caravan", 230, Rarity.RARE, mage.cards.c.CultivatorsCaravan.class)); + cards.add(new SetCardInfo("Daredevil Dragster", 231, Rarity.UNCOMMON, mage.cards.d.DaredevilDragster.class)); + cards.add(new SetCardInfo("Daring Demolition", 78, Rarity.COMMON, mage.cards.d.DaringDemolition.class)); + cards.add(new SetCardInfo("Dark Intimations", 191, Rarity.RARE, mage.cards.d.DarkIntimations.class)); + cards.add(new SetCardInfo("Dawnfeather Eagle", 15, Rarity.COMMON, mage.cards.d.DawnfeatherEagle.class)); + cards.add(new SetCardInfo("Decoction Module", 232, Rarity.UNCOMMON, mage.cards.d.DecoctionModule.class)); + cards.add(new SetCardInfo("Defiant Salvager", 79, Rarity.COMMON, mage.cards.d.DefiantSalvager.class)); + cards.add(new SetCardInfo("Demolition Stomper", 233, Rarity.UNCOMMON, mage.cards.d.DemolitionStomper.class)); + cards.add(new SetCardInfo("Demon of Dark Schemes", 80, Rarity.MYTHIC, mage.cards.d.DemonOfDarkSchemes.class)); + cards.add(new SetCardInfo("Depala, Pilot Exemplar", 192, Rarity.RARE, mage.cards.d.DepalaPilotExemplar.class)); + cards.add(new SetCardInfo("Destructive Tampering", 121, Rarity.COMMON, mage.cards.d.DestructiveTampering.class)); + cards.add(new SetCardInfo("Die Young", 81, Rarity.COMMON, mage.cards.d.DieYoung.class)); + cards.add(new SetCardInfo("Disallow", 46, Rarity.RARE, mage.cards.d.Disallow.class)); + cards.add(new SetCardInfo("Dovin Baan", 193, Rarity.MYTHIC, mage.cards.d.DovinBaan.class)); + cards.add(new SetCardInfo("Druid of the Cowl", 159, Rarity.COMMON, mage.cards.d.DruidOfTheCowl.class)); + cards.add(new SetCardInfo("Dukhara Peafowl", 234, Rarity.COMMON, mage.cards.d.DukharaPeafowl.class)); + cards.add(new SetCardInfo("Dynavolt Tower", 235, Rarity.RARE, mage.cards.d.DynavoltTower.class)); + cards.add(new SetCardInfo("Eager Construct", 236, Rarity.COMMON, mage.cards.e.EagerConstruct.class)); + cards.add(new SetCardInfo("Eddytrail Hawk", 16, Rarity.COMMON, mage.cards.e.EddytrailHawk.class)); + cards.add(new SetCardInfo("Electrostatic Pummeler", 237, Rarity.RARE, mage.cards.e.ElectrostaticPummeler.class)); + cards.add(new SetCardInfo("Embraal Bruiser", 82, Rarity.UNCOMMON, mage.cards.e.EmbraalBruiser.class)); + cards.add(new SetCardInfo("Empyreal Voyager", 194, Rarity.UNCOMMON, mage.cards.e.EmpyrealVoyager.class)); + cards.add(new SetCardInfo("Engineered Might", 195, Rarity.UNCOMMON, mage.cards.e.EngineeredMight.class)); + cards.add(new SetCardInfo("Enraged Giant", 122, Rarity.UNCOMMON, mage.cards.e.EnragedGiant.class)); + cards.add(new SetCardInfo("Era of Innovation", 47, Rarity.UNCOMMON, mage.cards.e.EraOfInnovation.class)); + cards.add(new SetCardInfo("Essence Extraction", 83, Rarity.UNCOMMON, mage.cards.e.EssenceExtraction.class)); + cards.add(new SetCardInfo("Fabrication Module", 238, Rarity.UNCOMMON, mage.cards.f.FabricationModule.class)); + cards.add(new SetCardInfo("Fairgrounds Warden", 17, Rarity.UNCOMMON, mage.cards.f.FairgroundsWarden.class)); + cards.add(new SetCardInfo("Fatal Push", 84, Rarity.UNCOMMON, mage.cards.f.FatalPush.class)); + cards.add(new SetCardInfo("Fateful Showdown", 123, Rarity.RARE, mage.cards.f.FatefulShowdown.class)); + cards.add(new SetCardInfo("Fen Hauler", 85, Rarity.COMMON, mage.cards.f.FenHauler.class)); + cards.add(new SetCardInfo("Filigree Familiar", 239, Rarity.UNCOMMON, mage.cards.f.FiligreeFamiliar.class)); + cards.add(new SetCardInfo("Fireforger's Puzzleknot", 240, Rarity.COMMON, mage.cards.f.FireforgersPuzzleknot.class)); + cards.add(new SetCardInfo("Forest", 299, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 300, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 301, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fortuitous Find", 86, Rarity.COMMON, mage.cards.f.FortuitousFind.class)); + cards.add(new SetCardInfo("Foundry Hornet", 87, Rarity.UNCOMMON, mage.cards.f.FoundryHornet.class)); + cards.add(new SetCardInfo("Foundry Inspector", 241, Rarity.UNCOMMON, mage.cards.f.FoundryInspector.class)); + cards.add(new SetCardInfo("Foundry Screecher", 88, Rarity.COMMON, mage.cards.f.FoundryScreecher.class)); + cards.add(new SetCardInfo("Fourth Bridge Prowler", 89, Rarity.COMMON, mage.cards.f.FourthBridgeProwler.class)); + cards.add(new SetCardInfo("Fragmentize", 18, Rarity.COMMON, mage.cards.f.Fragmentize.class)); + cards.add(new SetCardInfo("Freejam Regent", 124, Rarity.RARE, mage.cards.f.FreejamRegent.class)); + cards.add(new SetCardInfo("Fretwork Colony", 90, Rarity.UNCOMMON, mage.cards.f.FretworkColony.class)); + cards.add(new SetCardInfo("Frontline Rebel", 125, Rarity.COMMON, mage.cards.f.FrontlineRebel.class)); + cards.add(new SetCardInfo("Fumigate", 19, Rarity.RARE, mage.cards.f.Fumigate.class)); + cards.add(new SetCardInfo("Furious Reprisal", 126, Rarity.UNCOMMON, mage.cards.f.FuriousReprisal.class)); + cards.add(new SetCardInfo("Gearseeker Serpent", 48, Rarity.COMMON, mage.cards.g.GearseekerSerpent.class)); + cards.add(new SetCardInfo("Gearshift Ace", 20, Rarity.UNCOMMON, mage.cards.g.GearshiftAce.class)); + cards.add(new SetCardInfo("Gifted Aetherborn", 91, Rarity.UNCOMMON, mage.cards.g.GiftedAetherborn.class)); + cards.add(new SetCardInfo("Glimmer of Genius", 49, Rarity.UNCOMMON, mage.cards.g.GlimmerOfGenius.class)); + cards.add(new SetCardInfo("Glint-Nest Crane", 50, Rarity.UNCOMMON, mage.cards.g.GlintNestCrane.class)); + cards.add(new SetCardInfo("Glint-Sleeve Artisan", 21, Rarity.COMMON, mage.cards.g.GlintSleeveArtisan.class)); + cards.add(new SetCardInfo("Glint-Sleeve Siphoner", 92, Rarity.RARE, mage.cards.g.GlintSleeveSiphoner.class)); + cards.add(new SetCardInfo("Gonti, Lord of Luxury", 93, Rarity.RARE, mage.cards.g.GontiLordOfLuxury.class)); + cards.add(new SetCardInfo("Greenbelt Rampager", 160, Rarity.RARE, mage.cards.g.GreenbeltRampager.class)); + cards.add(new SetCardInfo("Harnessed Lightning", 127, Rarity.UNCOMMON, mage.cards.h.HarnessedLightning.class)); + cards.add(new SetCardInfo("Hazardous Conditions", 196, Rarity.UNCOMMON, mage.cards.h.HazardousConditions.class)); + cards.add(new SetCardInfo("Heart of Kiran", 242, Rarity.MYTHIC, mage.cards.h.HeartOfKiran.class)); + cards.add(new SetCardInfo("Herald of Anguish", 94, Rarity.MYTHIC, mage.cards.h.HeraldOfAnguish.class)); + cards.add(new SetCardInfo("Herald of the Fair", 22, Rarity.COMMON, mage.cards.h.HeraldOfTheFair.class)); + cards.add(new SetCardInfo("Heroic Intervention", 161, Rarity.RARE, mage.cards.h.HeroicIntervention.class)); + cards.add(new SetCardInfo("Hidden Stockpile", 197, Rarity.UNCOMMON, mage.cards.h.HiddenStockpile.class)); + cards.add(new SetCardInfo("Highspire Artisan", 162, Rarity.COMMON, mage.cards.h.HighspireArtisan.class)); + cards.add(new SetCardInfo("Highspire Infusion", 163, Rarity.COMMON, mage.cards.h.HighspireInfusion.class)); + cards.add(new SetCardInfo("Hijack", 128, Rarity.COMMON, mage.cards.h.Hijack.class)); + cards.add(new SetCardInfo("Hinterland Drake", 51, Rarity.COMMON, mage.cards.h.HinterlandDrake.class)); + cards.add(new SetCardInfo("Hope of Ghirapur", 243, Rarity.RARE, mage.cards.h.HopeOfGhirapur.class)); + cards.add(new SetCardInfo("Hungry Flames", 129, Rarity.UNCOMMON, mage.cards.h.HungryFlames.class)); + cards.add(new SetCardInfo("Hunt the Weak", 164, Rarity.COMMON, mage.cards.h.HuntTheWeak.class)); + cards.add(new SetCardInfo("Ice Over", 52, Rarity.COMMON, mage.cards.i.IceOver.class)); + cards.add(new SetCardInfo("Illusionist's Stratagem", 53, Rarity.UNCOMMON, mage.cards.i.IllusionistsStratagem.class)); + cards.add(new SetCardInfo("Impeccable Timing", 23, Rarity.COMMON, mage.cards.i.ImpeccableTiming.class)); + cards.add(new SetCardInfo("Implement of Examination", 244, Rarity.COMMON, mage.cards.i.ImplementOfExamination.class)); + cards.add(new SetCardInfo("Implement of Malice", 245, Rarity.COMMON, mage.cards.i.ImplementOfMalice.class)); + cards.add(new SetCardInfo("Indomitable Creativity", 130, Rarity.MYTHIC, mage.cards.i.IndomitableCreativity.class)); + cards.add(new SetCardInfo("Inspired Charge", 24, Rarity.COMMON, mage.cards.i.InspiredCharge.class)); + cards.add(new SetCardInfo("Inspiring Vantage", 283, Rarity.RARE, mage.cards.i.InspiringVantage.class)); + cards.add(new SetCardInfo("Inventor's Apprentice", 131, Rarity.UNCOMMON, mage.cards.i.InventorsApprentice.class)); + cards.add(new SetCardInfo("Inventor's Goggles", 246, Rarity.COMMON, mage.cards.i.InventorsGoggles.class)); + cards.add(new SetCardInfo("Inventors' Fair", 284, Rarity.RARE, mage.cards.i.InventorsFair.class)); + cards.add(new SetCardInfo("Invigorated Rampage", 132, Rarity.UNCOMMON, mage.cards.i.InvigoratedRampage.class)); + cards.add(new SetCardInfo("Irontread Crusher", 247, Rarity.COMMON, mage.cards.i.IrontreadCrusher.class)); + cards.add(new SetCardInfo("Island", 290, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 291, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 292, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kambal, Consul of Allocation", 198, Rarity.RARE, mage.cards.k.KambalConsulOfAllocation.class)); + cards.add(new SetCardInfo("Kari Zev's Expertise", 134, Rarity.RARE, mage.cards.k.KariZevsExpertise.class)); + cards.add(new SetCardInfo("Kari Zev, Skyship Raider", 133, Rarity.RARE, mage.cards.k.KariZevSkyshipRaider.class)); + cards.add(new SetCardInfo("Key to the City", 248, Rarity.RARE, mage.cards.k.KeyToTheCity.class)); + cards.add(new SetCardInfo("Kujar Seedsculptor", 165, Rarity.COMMON, mage.cards.k.KujarSeedsculptor.class)); + cards.add(new SetCardInfo("Lathnu Sailback", 135, Rarity.COMMON, mage.cards.l.LathnuSailback.class)); + cards.add(new SetCardInfo("Leave in the Dust", 54, Rarity.COMMON, mage.cards.l.LeaveInTheDust.class)); + cards.add(new SetCardInfo("Lifecraft Cavalry", 166, Rarity.COMMON, mage.cards.l.LifecraftCavalry.class)); + cards.add(new SetCardInfo("Lifecrafter's Bestiary", 249, Rarity.RARE, mage.cards.l.LifecraftersBestiary.class)); + cards.add(new SetCardInfo("Live Fast", 95, Rarity.COMMON, mage.cards.l.LiveFast.class)); + cards.add(new SetCardInfo("Longtusk Cub", 167, Rarity.UNCOMMON, mage.cards.l.LongtuskCub.class)); + cards.add(new SetCardInfo("Lost Legacy", 96, Rarity.RARE, mage.cards.l.LostLegacy.class)); + cards.add(new SetCardInfo("Make Obsolete", 97, Rarity.UNCOMMON, mage.cards.m.MakeObsolete.class)); + cards.add(new SetCardInfo("Malfunction", 55, Rarity.COMMON, mage.cards.m.Malfunction.class)); + cards.add(new SetCardInfo("Marionette Master", 98, Rarity.RARE, mage.cards.m.MarionetteMaster.class)); + cards.add(new SetCardInfo("Master Trinketeer", 25, Rarity.RARE, mage.cards.m.MasterTrinketeer.class)); + cards.add(new SetCardInfo("Maulfist Revolutionary", 168, Rarity.UNCOMMON, mage.cards.m.MaulfistRevolutionary.class)); + cards.add(new SetCardInfo("Maulfist Squad", 99, Rarity.COMMON, mage.cards.m.MaulfistSquad.class)); + cards.add(new SetCardInfo("Maverick Thopterist", 199, Rarity.UNCOMMON, mage.cards.m.MaverickThopterist.class)); + cards.add(new SetCardInfo("Merchant's Dockhand", 250, Rarity.RARE, mage.cards.m.MerchantsDockhand.class)); + cards.add(new SetCardInfo("Metallic Mimic", 251, Rarity.RARE, mage.cards.m.MetallicMimic.class)); + cards.add(new SetCardInfo("Metallic Rebuke", 56, Rarity.COMMON, mage.cards.m.MetallicRebuke.class)); + cards.add(new SetCardInfo("Metallurgic Summonings", 57, Rarity.MYTHIC, mage.cards.m.MetallurgicSummonings.class)); + cards.add(new SetCardInfo("Metalwork Colossus", 252, Rarity.RARE, mage.cards.m.MetalworkColossus.class)); + cards.add(new SetCardInfo("Midnight Oil", 100, Rarity.RARE, mage.cards.m.MidnightOil.class)); + cards.add(new SetCardInfo("Mind Rot", 101, Rarity.COMMON, mage.cards.m.MindRot.class)); + cards.add(new SetCardInfo("Minister of Inquiries", 58, Rarity.UNCOMMON, mage.cards.m.MinisterOfInquiries.class)); + cards.add(new SetCardInfo("Mobile Garrison", 253, Rarity.COMMON, mage.cards.m.MobileGarrison.class)); + cards.add(new SetCardInfo("Monstrous Onslaught", 169, Rarity.UNCOMMON, mage.cards.m.MonstrousOnslaught.class)); + cards.add(new SetCardInfo("Mountain", 296, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 297, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 298, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Narnam Cobra", 254, Rarity.COMMON, mage.cards.n.NarnamCobra.class)); + cards.add(new SetCardInfo("Narnam Renegade", 170, Rarity.UNCOMMON, mage.cards.n.NarnamRenegade.class)); + cards.add(new SetCardInfo("Nature's Way", 171, Rarity.UNCOMMON, mage.cards.n.NaturesWay.class)); + cards.add(new SetCardInfo("Night Market Aeronaut", 102, Rarity.COMMON, mage.cards.n.NightMarketAeronaut.class)); + cards.add(new SetCardInfo("Night Market Lookout", 103, Rarity.COMMON, mage.cards.n.NightMarketLookout.class)); + cards.add(new SetCardInfo("Nimble Innovator", 59, Rarity.COMMON, mage.cards.n.NimbleInnovator.class)); + cards.add(new SetCardInfo("Nissa, Vital Force", 172, Rarity.MYTHIC, mage.cards.n.NissaVitalForce.class)); + cards.add(new SetCardInfo("Noxious Gearhulk", 104, Rarity.MYTHIC, mage.cards.n.NoxiousGearhulk.class)); + cards.add(new SetCardInfo("Oath of Ajani", 200, Rarity.RARE, mage.cards.o.OathOfAjani.class)); + cards.add(new SetCardInfo("Ornamental Courage", 173, Rarity.COMMON, mage.cards.o.OrnamentalCourage.class)); + cards.add(new SetCardInfo("Ornithopter", 255, Rarity.UNCOMMON, mage.cards.o.Ornithopter.class)); + cards.add(new SetCardInfo("Outland Boar", 201, Rarity.UNCOMMON, mage.cards.o.OutlandBoar.class)); + cards.add(new SetCardInfo("Ovalchase Dragster", 256, Rarity.UNCOMMON, mage.cards.o.OvalchaseDragster.class)); + cards.add(new SetCardInfo("Oviya Pashiri, Sage Lifecrafter", 174, Rarity.RARE, mage.cards.o.OviyaPashiriSageLifecrafter.class)); + cards.add(new SetCardInfo("Pacification Array", 257, Rarity.UNCOMMON, mage.cards.p.PacificationArray.class)); + cards.add(new SetCardInfo("Padeem, Consul of Innovation", 60, Rarity.RARE, mage.cards.p.PadeemConsulOfInnovation.class)); + cards.add(new SetCardInfo("Panharmonicon", 258, Rarity.RARE, mage.cards.p.Panharmonicon.class)); + cards.add(new SetCardInfo("Paradox Engine", 259, Rarity.MYTHIC, mage.cards.p.ParadoxEngine.class)); + cards.add(new SetCardInfo("Paradoxical Outcome", 61, Rarity.RARE, mage.cards.p.ParadoxicalOutcome.class)); + cards.add(new SetCardInfo("Peacewalker Colossus", 260, Rarity.RARE, mage.cards.p.PeacewalkerColossus.class)); + cards.add(new SetCardInfo("Peema Aether-Seer", 175, Rarity.UNCOMMON, mage.cards.p.PeemaAetherSeer.class)); + cards.add(new SetCardInfo("Peema Outrider", 176, Rarity.COMMON, mage.cards.p.PeemaOutrider.class)); + cards.add(new SetCardInfo("Pendulum of Patterns", 261, Rarity.COMMON, mage.cards.p.PendulumOfPatterns.class)); + cards.add(new SetCardInfo("Pia Nalaar", 136, Rarity.RARE, mage.cards.p.PiaNalaar.class)); + cards.add(new SetCardInfo("Plains", 287, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 288, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 289, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Planar Bridge", 262, Rarity.MYTHIC, mage.cards.p.PlanarBridge.class)); + cards.add(new SetCardInfo("Prakhata Pillar-Bug", 263, Rarity.COMMON, mage.cards.p.PrakhataPillarBug.class)); + cards.add(new SetCardInfo("Precise Strike", 137, Rarity.COMMON, mage.cards.p.PreciseStrike.class)); + cards.add(new SetCardInfo("Propeller Pioneer", 26, Rarity.COMMON, mage.cards.p.PropellerPioneer.class)); + cards.add(new SetCardInfo("Prophetic Prism", 264, Rarity.COMMON, mage.cards.p.PropheticPrism.class)); + cards.add(new SetCardInfo("Quicksmith Genius", 138, Rarity.UNCOMMON, mage.cards.q.QuicksmithGenius.class)); + cards.add(new SetCardInfo("Quicksmith Rebel", 139, Rarity.RARE, mage.cards.q.QuicksmithRebel.class)); + cards.add(new SetCardInfo("Rashmi, Eternities Crafter", 202, Rarity.MYTHIC, mage.cards.r.RashmiEternitiesCrafter.class)); + cards.add(new SetCardInfo("Ravenous Intruder", 140, Rarity.UNCOMMON, mage.cards.r.RavenousIntruder.class)); + cards.add(new SetCardInfo("Reckless Fireweaver", 141, Rarity.COMMON, mage.cards.r.RecklessFireweaver.class)); + cards.add(new SetCardInfo("Refurbish", 27, Rarity.UNCOMMON, mage.cards.r.Refurbish.class)); + cards.add(new SetCardInfo("Renegade Map", 265, Rarity.COMMON, mage.cards.r.RenegadeMap.class)); + cards.add(new SetCardInfo("Renegade Rallier", 203, Rarity.UNCOMMON, mage.cards.r.RenegadeRallier.class)); + cards.add(new SetCardInfo("Renegade Wheelsmith", 204, Rarity.UNCOMMON, mage.cards.r.RenegadeWheelsmith.class)); + cards.add(new SetCardInfo("Reservoir Walker", 266, Rarity.COMMON, mage.cards.r.ReservoirWalker.class)); + cards.add(new SetCardInfo("Restoration Gearsmith", 205, Rarity.UNCOMMON, mage.cards.r.RestorationGearsmith.class)); + cards.add(new SetCardInfo("Restoration Specialist", 28, Rarity.UNCOMMON, mage.cards.r.RestorationSpecialist.class)); + cards.add(new SetCardInfo("Revoke Privileges", 29, Rarity.COMMON, mage.cards.r.RevokePrivileges.class)); + cards.add(new SetCardInfo("Revolutionary Rebuff", 62, Rarity.COMMON, mage.cards.r.RevolutionaryRebuff.class)); + cards.add(new SetCardInfo("Ridgescale Tusker", 177, Rarity.UNCOMMON, mage.cards.r.RidgescaleTusker.class)); + cards.add(new SetCardInfo("Riparian Tiger", 178, Rarity.COMMON, mage.cards.r.RiparianTiger.class)); + cards.add(new SetCardInfo("Rishkar's Expertise", 180, Rarity.RARE, mage.cards.r.RishkarsExpertise.class)); + cards.add(new SetCardInfo("Rishkar, Peema Renegade", 179, Rarity.RARE, mage.cards.r.RishkarPeemaRenegade.class)); + cards.add(new SetCardInfo("Rogue Refiner", 206, Rarity.UNCOMMON, mage.cards.r.RogueRefiner.class)); + cards.add(new SetCardInfo("Ruinous Gremlin", 142, Rarity.COMMON, mage.cards.r.RuinousGremlin.class)); + cards.add(new SetCardInfo("Rush of Vitality", 105, Rarity.COMMON, mage.cards.r.RushOfVitality.class)); + cards.add(new SetCardInfo("Sage of Shaila's Claim", 181, Rarity.COMMON, mage.cards.s.SageOfShailasClaim.class)); + cards.add(new SetCardInfo("Saheeli Rai", 207, Rarity.MYTHIC, mage.cards.s.SaheeliRai.class)); + cards.add(new SetCardInfo("Salivating Gremlins", 143, Rarity.COMMON, mage.cards.s.SalivatingGremlins.class)); + cards.add(new SetCardInfo("Scrap Trawler", 267, Rarity.RARE, mage.cards.s.ScrapTrawler.class)); + cards.add(new SetCardInfo("Scrapheap Scrounger", 268, Rarity.RARE, mage.cards.s.ScrapheapScrounger.class)); + cards.add(new SetCardInfo("Scrapper Champion", 144, Rarity.UNCOMMON, mage.cards.s.ScrapperChampion.class)); + cards.add(new SetCardInfo("Sculpting Steel", 302, Rarity.MYTHIC, mage.cards.s.SculptingSteel.class)); + cards.add(new SetCardInfo("Select for Inspection", 63, Rarity.COMMON, mage.cards.s.SelectForInspection.class)); + cards.add(new SetCardInfo("Self-Assembler", 269, Rarity.COMMON, mage.cards.s.SelfAssembler.class)); + cards.add(new SetCardInfo("Servant of the Conduit", 182, Rarity.UNCOMMON, mage.cards.s.ServantOfTheConduit.class)); + cards.add(new SetCardInfo("Servo Exhibition", 30, Rarity.UNCOMMON, mage.cards.s.ServoExhibition.class)); + cards.add(new SetCardInfo("Servo Schematic", 270, Rarity.UNCOMMON, mage.cards.s.ServoSchematic.class)); + cards.add(new SetCardInfo("Shielded Aether Thief", 64, Rarity.UNCOMMON, mage.cards.s.ShieldedAetherThief.class)); + cards.add(new SetCardInfo("Shipwreck Moray", 65, Rarity.COMMON, mage.cards.s.ShipwreckMoray.class)); + cards.add(new SetCardInfo("Shrewd Negotiation", 66, Rarity.UNCOMMON, mage.cards.s.ShrewdNegotiation.class)); + cards.add(new SetCardInfo("Siege Modification", 145, Rarity.UNCOMMON, mage.cards.s.SiegeModification.class)); + cards.add(new SetCardInfo("Sky Skiff", 271, Rarity.COMMON, mage.cards.s.SkySkiff.class)); + cards.add(new SetCardInfo("Skyship Plunderer", 67, Rarity.UNCOMMON, mage.cards.s.SkyshipPlunderer.class)); + cards.add(new SetCardInfo("Skyship Stalker", 146, Rarity.RARE, mage.cards.s.SkyshipStalker.class)); + cards.add(new SetCardInfo("Skysovereign, Consul Flagship", 272, Rarity.MYTHIC, mage.cards.s.SkysovereignConsulFlagship.class)); + cards.add(new SetCardInfo("Skywhaler's Shot", 31, Rarity.UNCOMMON, mage.cards.s.SkywhalersShot.class)); + cards.add(new SetCardInfo("Sly Requisitioner", 106, Rarity.UNCOMMON, mage.cards.s.SlyRequisitioner.class)); + cards.add(new SetCardInfo("Speedway Fanatic", 147, Rarity.UNCOMMON, mage.cards.s.SpeedwayFanatic.class)); + cards.add(new SetCardInfo("Spire Patrol", 208, Rarity.UNCOMMON, mage.cards.s.SpirePatrol.class)); + cards.add(new SetCardInfo("Spire of Industry", 285, Rarity.RARE, mage.cards.s.SpireOfIndustry.class)); + cards.add(new SetCardInfo("Spirebluff Canal", 286, Rarity.RARE, mage.cards.s.SpirebluffCanal.class)); + cards.add(new SetCardInfo("Spireside Infiltrator", 148, Rarity.COMMON, mage.cards.s.SpiresideInfiltrator.class)); + cards.add(new SetCardInfo("Sram's Expertise", 33, Rarity.RARE, mage.cards.s.SramsExpertise.class)); + cards.add(new SetCardInfo("Sram, Senior Edificer", 32, Rarity.RARE, mage.cards.s.SramSeniorEdificer.class)); + cards.add(new SetCardInfo("Subtle Strike", 107, Rarity.COMMON, mage.cards.s.SubtleStrike.class)); + cards.add(new SetCardInfo("Swamp", 293, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 294, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 295, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sweatworks Brawler", 149, Rarity.COMMON, mage.cards.s.SweatworksBrawler.class)); + cards.add(new SetCardInfo("Tezzeret the Schemer", 209, Rarity.MYTHIC, mage.cards.t.TezzeretTheSchemer.class)); + cards.add(new SetCardInfo("Tezzeret's Ambition", 68, Rarity.COMMON, mage.cards.t.TezzeretsAmbition.class)); + cards.add(new SetCardInfo("Tezzeret's Touch", 210, Rarity.UNCOMMON, mage.cards.t.TezzeretsTouch.class)); + cards.add(new SetCardInfo("Thopter Arrest", 34, Rarity.UNCOMMON, mage.cards.t.ThopterArrest.class)); + cards.add(new SetCardInfo("Thriving Rhino", 183, Rarity.COMMON, mage.cards.t.ThrivingRhino.class)); + cards.add(new SetCardInfo("Thriving Turtle", 69, Rarity.COMMON, mage.cards.t.ThrivingTurtle.class)); + cards.add(new SetCardInfo("Toolcraft Exemplar", 35, Rarity.RARE, mage.cards.t.ToolcraftExemplar.class)); + cards.add(new SetCardInfo("Torrential Gearhulk", 70, Rarity.MYTHIC, mage.cards.t.TorrentialGearhulk.class)); + cards.add(new SetCardInfo("Trophy Mage", 71, Rarity.UNCOMMON, mage.cards.t.TrophyMage.class)); + cards.add(new SetCardInfo("Unbridled Growth", 184, Rarity.COMMON, mage.cards.u.UnbridledGrowth.class)); + cards.add(new SetCardInfo("Underhanded Designs", 108, Rarity.UNCOMMON, mage.cards.u.UnderhandedDesigns.class)); + cards.add(new SetCardInfo("Universal Solvent", 273, Rarity.COMMON, mage.cards.u.UniversalSolvent.class)); + cards.add(new SetCardInfo("Unlicensed Disintegration", 211, Rarity.UNCOMMON, mage.cards.u.UnlicensedDisintegration.class)); + cards.add(new SetCardInfo("Untethered Express", 274, Rarity.UNCOMMON, mage.cards.u.UntetheredExpress.class)); + cards.add(new SetCardInfo("Vengeful Rebel", 109, Rarity.UNCOMMON, mage.cards.v.VengefulRebel.class)); + cards.add(new SetCardInfo("Verdurous Gearhulk", 185, Rarity.MYTHIC, mage.cards.v.VerdurousGearhulk.class)); + cards.add(new SetCardInfo("Veteran Motorist", 212, Rarity.UNCOMMON, mage.cards.v.VeteranMotorist.class)); + cards.add(new SetCardInfo("Visionary Augmenter", 36, Rarity.UNCOMMON, mage.cards.v.VisionaryAugmenter.class)); + cards.add(new SetCardInfo("Voltaic Brawler", 213, Rarity.UNCOMMON, mage.cards.v.VoltaicBrawler.class)); + cards.add(new SetCardInfo("Weaponcraft Enthusiast", 110, Rarity.UNCOMMON, mage.cards.w.WeaponcraftEnthusiast.class)); + cards.add(new SetCardInfo("Weldfast Engineer", 214, Rarity.UNCOMMON, mage.cards.w.WeldfastEngineer.class)); + cards.add(new SetCardInfo("Weldfast Monitor", 275, Rarity.COMMON, mage.cards.w.WeldfastMonitor.class)); + cards.add(new SetCardInfo("Weldfast Wingsmith", 72, Rarity.COMMON, mage.cards.w.WeldfastWingsmith.class)); + cards.add(new SetCardInfo("Welding Sparks", 150, Rarity.COMMON, mage.cards.w.WeldingSparks.class)); + cards.add(new SetCardInfo("Whir of Invention", 73, Rarity.RARE, mage.cards.w.WhirOfInvention.class)); + cards.add(new SetCardInfo("Whirler Virtuoso", 215, Rarity.UNCOMMON, mage.cards.w.WhirlerVirtuoso.class)); + cards.add(new SetCardInfo("Whirlermaker", 276, Rarity.UNCOMMON, mage.cards.w.Whirlermaker.class)); + cards.add(new SetCardInfo("Wild Wanderer", 186, Rarity.COMMON, mage.cards.w.WildWanderer.class)); + cards.add(new SetCardInfo("Wildest Dreams", 187, Rarity.RARE, mage.cards.w.WildestDreams.class)); + cards.add(new SetCardInfo("Wind-Kin Raiders", 74, Rarity.UNCOMMON, mage.cards.w.WindKinRaiders.class)); + cards.add(new SetCardInfo("Winding Constrictor", 216, Rarity.UNCOMMON, mage.cards.w.WindingConstrictor.class)); + cards.add(new SetCardInfo("Wispweaver Angel", 37, Rarity.UNCOMMON, mage.cards.w.WispweaverAngel.class)); + cards.add(new SetCardInfo("Woodweaver's Puzzleknot", 277, Rarity.COMMON, mage.cards.w.WoodweaversPuzzleknot.class)); + cards.add(new SetCardInfo("Workshop Assistant", 278, Rarity.COMMON, mage.cards.w.WorkshopAssistant.class)); + cards.add(new SetCardInfo("Yahenni's Expertise", 112, Rarity.RARE, mage.cards.y.YahennisExpertise.class)); + cards.add(new SetCardInfo("Yahenni, Undying Partisan", 111, Rarity.RARE, mage.cards.y.YahenniUndyingPartisan.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/Kaldheim.java b/Mage.Sets/src/mage/sets/Kaldheim.java index 60a483787235..9bcfe6ecf5b8 100644 --- a/Mage.Sets/src/mage/sets/Kaldheim.java +++ b/Mage.Sets/src/mage/sets/Kaldheim.java @@ -4,6 +4,10 @@ import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.SetType; @@ -26,7 +30,7 @@ public static Kaldheim getInstance() { private final List savedSpecialLand = new ArrayList<>(); private Kaldheim() { - super("Kaldheim", "KHM", ExpansionSet.buildDate(2021, 2, 5), SetType.EXPANSION); + super("Kaldheim", "KHM", ExpansionSet.buildDate(2021, 2, 5), SetType.EXPANSION, new KaldheimCollator()); this.blockName = "Kaldheim"; this.hasBasicLands = true; this.hasBoosters = true; @@ -489,3 +493,158 @@ public List getSpecialLand() { return new ArrayList<>(savedSpecialLand); } } + +// Booster collation info from https://www.lethe.xyz/mtg/collation/khm.html +// Using USA collation for common/uncommon and JP for rare/mythic +class KaldheimCollator implements BoosterCollator { + + private static class KaldheimRun extends CardRun { + private static final KaldheimRun commonA = new KaldheimRun(true, "34","77","136","13","78","149","3","47","127","14","67","140","19","54","124","38","49","147","39","55","157","1","53","141","37","66","126","10","71","155","4","65","121","13","77","136","34","78","127","3","47","149","14","54","124","38","67","140","19","55","147","39","49","157","37","53","141","10","65","155","1","71","121","4","66","126"); + private static final KaldheimRun commonB = new KaldheimRun(true, "102","176","87","183","93","184","104","178","117","174","111","171","96","194","84","176","119","180","83","164","89","172","87","175","102","183","104","178","93","174","117","184","111","171","84","194","119","164","96","180","89","176","83","172","102","175","87","178","104","174","93","183","117","171","119","184","84","164","111","194","89","180","96","172","83","175"); + private static final KaldheimRun commonC1 = new KaldheimRun(true, "187","152","242","46","173","23","101","246","48","190","32","151","99","68","267","31","91","192","143","57","100","243","105","16","134","42","196","238","187","46","23","242","152","173","48","32","246","190","151","101","31","68","99","267","91","134","105","57","16","192","100","143","243","196","42"); + private static final KaldheimRun commonC2 = new KaldheimRun(true, "11","193","95","158","17","239","44","159","129","7","118","85","138","74","165","11","129","193","150","72","5","95","159","74","158","17","85","239","118","138","44","7","238","193","150","165","5","72","158","95","11","44","159","239","129","17","85","74","7","118","5","150","165","138","72"); + private static final KaldheimRun uncommonA = new KaldheimRun(true, "215","236","212","208","195","224","332","6","232","18","106","268","209","162","8","76","122","88","182","206","202","62","110","132","200","325","271","211","144","103","215","236","258","56","163","113","28","226","2","58","263","148","232","162","224","208","195","323","268","18","106","6","233","8","76","122","209","88","182","206","202","62","110","132","321","220","271","211","144","258","2","28","263","113","226","103","236","163","56","215","148","58","329","195","6","232","233","18","212","162","268","106","208","103","322","76","122","88","182","206","202","62","110","132","200","220","271","211","144","8","58","28","258","113","56","148","2","263","226","163"); + private static final KaldheimRun uncommonB = new KaldheimRun(true, "30","166","75","201","265","222","45","135","256","191","231","235","36","250","316","128","25","247","264","35","97","186","223","59","60","130","216","80","244","259","217","133","64","245","108","189","331","137","116","253","30","166","75","201","265","327","45","128","256","247","235","36","191","25","170","250","135","231","186","35","60","324","97","130","59","264","244","80","328","259","133","217","64","245","108","189","230","137","116","253","30","166","75","201","265","222","45","256","191","235","170","135","36","128","25","247","250","231","35","223","60","130","97","264","216","186","59","244","80","259","217","133","304","245","108","189","230","137","116","253"); + private static final KaldheimRun rareA = new KaldheimRun(false, "9","20","21","24","26","29","43","50","51","52","61","63","69","73","82","86","90","92","107","109","112","115","120","123","125","131","142","146","161","169","179","181","185","188","197","203","204","205","207","210","213","219","227","228","229","234","237","240","241","251","252","254","255","260","272","275","9","20","21","24","26","29","43","50","51","52","61","63","69","73","82","86","90","92","107","109","112","115","120","123","125","131","142","146","161","169","179","181","185","188","197","203","204","205","207","210","213","219","227","228","229","234","237","240","241","251","252","254","255","260","272","275","15","22","33","40","41","70","94","98","114","145","154","160","168","198","218","221","225","320"); + private static final KaldheimRun rareB = new KaldheimRun(false, "9","20","300","24","26","301","43","303","51","52","61","63","69","73","82","86","90","306","107","109","307","309","310","311","125","131","312","146","161","315","317","318","185","188","319","203","204","205","207","210","213","219","227","330","229","234","237","240","241","290","291","292","255","293","272","275","9","20","300","24","26","301","43","303","51","52","61","63","69","73","82","86","90","306","107","109","307","309","310","311","125","131","312","146","161","315","317","318","185","188","319","203","204","205","207","210","213","219","227","330","229","234","237","240","241","290","291","292","255","293","272","275","299","22","294","302","295","305","94","296","308","297","313","298","314","287","288","326","289","320"); + private static final KaldheimRun land = new KaldheimRun(true, "276","277","283","249","278","285","248","276","285","279","261","269","257","249","248","283","270","285","277","282","284","270","278","248","279","269","281","274","280","279","257","281","284","277","257","274","273","279","276","262","266","284","281","273","282","278","262","280","279","274","262","282","283","278","262","279","261","285","273","266","283","261","280","284","266","278","270","285","282","280","276","277","273","278","269","273","249","261","274"); + + private KaldheimRun(boolean keepOrder, String... numbers) { + super(keepOrder, numbers); + } + } + + private static class KaldheimStructure extends BoosterStructure { + private static final KaldheimStructure C1 = new KaldheimStructure( + KaldheimRun.commonA, + KaldheimRun.commonA, + KaldheimRun.commonB, + KaldheimRun.commonB, + KaldheimRun.commonC1, + KaldheimRun.commonC1, + KaldheimRun.commonC1, + KaldheimRun.commonC1, + KaldheimRun.commonC1, + KaldheimRun.commonC1 + ); + private static final KaldheimStructure C2 = new KaldheimStructure( + KaldheimRun.commonA, + KaldheimRun.commonA, + KaldheimRun.commonA, + KaldheimRun.commonB, + KaldheimRun.commonB, + KaldheimRun.commonC1, + KaldheimRun.commonC1, + KaldheimRun.commonC1, + KaldheimRun.commonC1, + KaldheimRun.commonC1 + ); + private static final KaldheimStructure C3 = new KaldheimStructure( + KaldheimRun.commonA, + KaldheimRun.commonA, + KaldheimRun.commonA, + KaldheimRun.commonB, + KaldheimRun.commonB, + KaldheimRun.commonB, + KaldheimRun.commonC2, + KaldheimRun.commonC2, + KaldheimRun.commonC2, + KaldheimRun.commonC2 + ); + private static final KaldheimStructure C4 = new KaldheimStructure( + KaldheimRun.commonA, + KaldheimRun.commonA, + KaldheimRun.commonA, + KaldheimRun.commonA, + KaldheimRun.commonB, + KaldheimRun.commonB, + KaldheimRun.commonB, + KaldheimRun.commonC2, + KaldheimRun.commonC2, + KaldheimRun.commonC2 + ); + private static final KaldheimStructure C5 = new KaldheimStructure( + KaldheimRun.commonA, + KaldheimRun.commonA, + KaldheimRun.commonA, + KaldheimRun.commonA, + KaldheimRun.commonB, + KaldheimRun.commonB, + KaldheimRun.commonC2, + KaldheimRun.commonC2, + KaldheimRun.commonC2, + KaldheimRun.commonC2 + ); + private static final KaldheimStructure U1 = new KaldheimStructure( + KaldheimRun.uncommonA, + KaldheimRun.uncommonA, + KaldheimRun.uncommonA + ); + private static final KaldheimStructure U2 = new KaldheimStructure( + KaldheimRun.uncommonB, + KaldheimRun.uncommonB, + KaldheimRun.uncommonB + ); + private static final KaldheimStructure R1 = new KaldheimStructure( + KaldheimRun.rareA + ); + private static final KaldheimStructure R2 = new KaldheimStructure( + KaldheimRun.rareB + ); + private static final KaldheimStructure L1 = new KaldheimStructure( + KaldheimRun.land + ); + + private KaldheimStructure(CardRun... runs) { + super(runs); + } + } + + private final RarityConfiguration commonRuns = new RarityConfiguration( + false, + KaldheimStructure.C1, + KaldheimStructure.C2, + KaldheimStructure.C3, + KaldheimStructure.C4, + KaldheimStructure.C5, + KaldheimStructure.C1, + KaldheimStructure.C2, + KaldheimStructure.C3, + KaldheimStructure.C4, + KaldheimStructure.C5, + KaldheimStructure.C4, + KaldheimStructure.C5 + ); + private final RarityConfiguration uncommonRuns = new RarityConfiguration( + KaldheimStructure.U1, + KaldheimStructure.U2 + ); + private final RarityConfiguration rareRuns = new RarityConfiguration( + false, + KaldheimStructure.R1, + KaldheimStructure.R1, + KaldheimStructure.R2 + ); + private final RarityConfiguration landRuns = new RarityConfiguration( + KaldheimStructure.L1 + ); + + + @Override + public void shuffle() { + commonRuns.shuffle(); + uncommonRuns.shuffle(); + rareRuns.shuffle(); + landRuns.shuffle(); + } + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; + } +} diff --git a/Mage.Sets/src/mage/sets/OathOfTheGatewatch.java b/Mage.Sets/src/mage/sets/OathOfTheGatewatch.java index 80f4bae9c88f..10920e383447 100644 --- a/Mage.Sets/src/mage/sets/OathOfTheGatewatch.java +++ b/Mage.Sets/src/mage/sets/OathOfTheGatewatch.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.sets; import mage.cards.ExpansionSet; diff --git a/Mage.Sets/src/mage/sets/SecretLairDrop.java b/Mage.Sets/src/mage/sets/SecretLairDrop.java index 8074c6df92b3..bf480d151ac9 100644 --- a/Mage.Sets/src/mage/sets/SecretLairDrop.java +++ b/Mage.Sets/src/mage/sets/SecretLairDrop.java @@ -42,6 +42,7 @@ private SecretLairDrop() { cards.add(new SetCardInfo("Bitterblossom", 12, Rarity.MYTHIC, mage.cards.b.Bitterblossom.class)); cards.add(new SetCardInfo("Blood Artist", 42, Rarity.RARE, mage.cards.b.BloodArtist.class)); cards.add(new SetCardInfo("Bloodghast", 6, Rarity.RARE, mage.cards.b.Bloodghast.class)); + cards.add(new SetCardInfo("Boros Charm", 217, Rarity.RARE, mage.cards.b.BorosCharm.class)); cards.add(new SetCardInfo("Captain Sisay", 51, Rarity.MYTHIC, mage.cards.c.CaptainSisay.class)); cards.add(new SetCardInfo("Chandra, Fire Artisan", 512, Rarity.RARE, mage.cards.c.ChandraFireArtisan.class)); cards.add(new SetCardInfo("Chatter of the Squirrel", 195, Rarity.RARE, mage.cards.c.ChatterOfTheSquirrel.class)); @@ -49,6 +50,7 @@ private SecretLairDrop() { cards.add(new SetCardInfo("Collected Company", 166, Rarity.RARE, mage.cards.c.CollectedCompany.class)); cards.add(new SetCardInfo("Commander's Sphere", 203, Rarity.RARE, mage.cards.c.CommandersSphere.class)); cards.add(new SetCardInfo("Consecrated Sphinx", 165, Rarity.RARE, mage.cards.c.ConsecratedSphinx.class)); + cards.add(new SetCardInfo("Cultivate", 246, Rarity.RARE, mage.cards.c.Cultivate.class)); cards.add(new SetCardInfo("Damnation", 121, Rarity.RARE, mage.cards.d.Damnation.class)); cards.add(new SetCardInfo("Darksteel Colossus", 57, Rarity.MYTHIC, mage.cards.d.DarksteelColossus.class)); cards.add(new SetCardInfo("Darksteel Ingot", 204, Rarity.RARE, mage.cards.d.DarksteelIngot.class)); @@ -65,29 +67,39 @@ private SecretLairDrop() { cards.add(new SetCardInfo("Ephara, God of the Polis", 72, Rarity.MYTHIC, mage.cards.e.EpharaGodOfThePolis.class)); cards.add(new SetCardInfo("Erebos, God of the Dead", 74, Rarity.MYTHIC, mage.cards.e.ErebosGodOfTheDead.class)); cards.add(new SetCardInfo("Eternal Witness", 43, Rarity.RARE, mage.cards.e.EternalWitness.class)); + cards.add(new SetCardInfo("Evolving Wilds", 538, Rarity.RARE, mage.cards.e.EvolvingWilds.class)); cards.add(new SetCardInfo("Explore", 114, Rarity.RARE, mage.cards.e.Explore.class)); cards.add(new SetCardInfo("Fatal Push", 112, Rarity.RARE, mage.cards.f.FatalPush.class)); cards.add(new SetCardInfo("Forest", 108, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 109, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 243, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Forest", 67, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Frost Titan", 220, Rarity.MYTHIC, mage.cards.f.FrostTitan.class)); cards.add(new SetCardInfo("Gideon Blackblade", 503, Rarity.MYTHIC, mage.cards.g.GideonBlackblade.class)); cards.add(new SetCardInfo("Gilded Goose", 93, Rarity.RARE, mage.cards.g.GildedGoose.class)); cards.add(new SetCardInfo("Gilded Lotus", 205, Rarity.RARE, mage.cards.g.GildedLotus.class)); + cards.add(new SetCardInfo("Gisela, Blade of Goldnight", 218, Rarity.MYTHIC, mage.cards.g.GiselaBladeOfGoldnight.class)); + cards.add(new SetCardInfo("Glen Elendra Archmage", 115, Rarity.RARE, mage.cards.g.GlenElendraArchmage.class)); cards.add(new SetCardInfo("Glenn, the Voice of Calm", 145, Rarity.MYTHIC, mage.cards.g.GlennTheVoiceOfCalm.class)); cards.add(new SetCardInfo("Goblin Bushwhacker", 17, Rarity.RARE, mage.cards.g.GoblinBushwhacker.class)); cards.add(new SetCardInfo("Goblin King", 19, Rarity.RARE, mage.cards.g.GoblinKing.class)); cards.add(new SetCardInfo("Goblin Lackey", 20, Rarity.RARE, mage.cards.g.GoblinLackey.class)); cards.add(new SetCardInfo("Goblin Piledriver", 21, Rarity.RARE, mage.cards.g.GoblinPiledriver.class)); + cards.add(new SetCardInfo("Goblin Rabblemaster", 215, Rarity.RARE, mage.cards.g.GoblinRabblemaster.class)); cards.add(new SetCardInfo("Goblin Sharpshooter", 18, Rarity.RARE, mage.cards.g.GoblinSharpshooter.class)); cards.add(new SetCardInfo("Goblin Snowman", 61, Rarity.RARE, mage.cards.g.GoblinSnowman.class)); cards.add(new SetCardInfo("Golgari Thug", 7, Rarity.RARE, mage.cards.g.GolgariThug.class)); + cards.add(new SetCardInfo("Grave Titan", 223, Rarity.MYTHIC, mage.cards.g.GraveTitan.class)); cards.add(new SetCardInfo("Heliod, God of the Sun", 68, Rarity.MYTHIC, mage.cards.h.HeliodGodOfTheSun.class)); + cards.add(new SetCardInfo("Heliod, Sun-Crowned", 214, Rarity.MYTHIC, mage.cards.h.HeliodSunCrowned.class)); cards.add(new SetCardInfo("Huatli, the Sun's Heart", 530, Rarity.UNCOMMON, mage.cards.h.HuatliTheSunsHeart.class)); + cards.add(new SetCardInfo("Inferno Titan", 224, Rarity.MYTHIC, mage.cards.i.InfernoTitan.class)); cards.add(new SetCardInfo("Ink-Eyes, Servant of Oni", 33, Rarity.RARE, mage.cards.i.InkEyesServantOfOni.class)); cards.add(new SetCardInfo("Inkmoth Nexus", 45, Rarity.RARE, mage.cards.i.InkmothNexus.class)); cards.add(new SetCardInfo("Iroas, God of Victory", 70, Rarity.MYTHIC, mage.cards.i.IroasGodOfVictory.class)); cards.add(new SetCardInfo("Island", 102, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 103, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 240, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Island", 64, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Jace, Wielder of Mysteries", 506, Rarity.RARE, mage.cards.j.JaceWielderOfMysteries.class)); cards.add(new SetCardInfo("Jaya, Venerated Firemage", 513, Rarity.UNCOMMON, mage.cards.j.JayaVeneratedFiremage.class)); @@ -96,9 +108,11 @@ private SecretLairDrop() { cards.add(new SetCardInfo("Karn, the Great Creator", 501, Rarity.RARE, mage.cards.k.KarnTheGreatCreator.class)); cards.add(new SetCardInfo("Kasmina, Enigmatic Mentor", 507, Rarity.UNCOMMON, mage.cards.k.KasminaEnigmaticMentor.class)); cards.add(new SetCardInfo("Kaya, Bane of the Dead", 531, Rarity.UNCOMMON, mage.cards.k.KayaBaneOfTheDead.class)); + cards.add(new SetCardInfo("Kaya, Ghost Assassin", 247, Rarity.RARE, mage.cards.k.KayaGhostAssassin.class)); cards.add(new SetCardInfo("Keranos, God of Storms", 79, Rarity.MYTHIC, mage.cards.k.KeranosGodOfStorms.class)); cards.add(new SetCardInfo("Kiora, Behemoth Beckoner", 532, Rarity.UNCOMMON, mage.cards.k.KioraBehemothBeckoner.class)); cards.add(new SetCardInfo("Krosan Beast", 196, Rarity.RARE, mage.cards.k.KrosanBeast.class)); + cards.add(new SetCardInfo("Kroxa, Titan of Death's Hunger", 225, Rarity.MYTHIC, mage.cards.k.KroxaTitanOfDeathsHunger.class)); cards.add(new SetCardInfo("Kruphix, God of Horizons", 73, Rarity.MYTHIC, mage.cards.k.KruphixGodOfHorizons.class)); cards.add(new SetCardInfo("Leonin Warleader", 22, Rarity.RARE, mage.cards.l.LeoninWarleader.class)); cards.add(new SetCardInfo("Life from the Loam", 8, Rarity.RARE, mage.cards.l.LifeFromTheLoam.class)); @@ -113,9 +127,12 @@ private SecretLairDrop() { cards.add(new SetCardInfo("Meren of Clan Nel Toth", 52, Rarity.MYTHIC, mage.cards.m.MerenOfClanNelToth.class)); cards.add(new SetCardInfo("Michonne, Ruthless Survivor", 146, Rarity.MYTHIC, mage.cards.m.MichonneRuthlessSurvivor.class)); cards.add(new SetCardInfo("Mirri, Weatherlight Duelist", 26, Rarity.MYTHIC, mage.cards.m.MirriWeatherlightDuelist.class)); + cards.add(new SetCardInfo("Mistbind Clique", 116, Rarity.RARE, mage.cards.m.MistbindClique.class)); cards.add(new SetCardInfo("Mogis, God of Slaughter", 78, Rarity.MYTHIC, mage.cards.m.MogisGodOfSlaughter.class)); + cards.add(new SetCardInfo("Monastery Swiftspear", 216, Rarity.RARE, mage.cards.m.MonasterySwiftspear.class)); cards.add(new SetCardInfo("Mountain", 106, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 107, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 242, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Mountain", 66, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Mudhole", 62, Rarity.RARE, mage.cards.m.Mudhole.class)); cards.add(new SetCardInfo("Nahiri, Storm of Stone", 533, Rarity.UNCOMMON, mage.cards.n.NahiriStormOfStone.class)); @@ -130,12 +147,16 @@ private SecretLairDrop() { cards.add(new SetCardInfo("Oona, Queen of the Fae", 54, Rarity.MYTHIC, mage.cards.o.OonaQueenOfTheFae.class)); cards.add(new SetCardInfo("Opt", 111, Rarity.RARE, mage.cards.o.Opt.class)); cards.add(new SetCardInfo("Pack Rat", 35, Rarity.RARE, mage.cards.p.PackRat.class)); + cards.add(new SetCardInfo("Path of Ancestry", 250, Rarity.RARE, mage.cards.p.PathOfAncestry.class)); cards.add(new SetCardInfo("Pharika, God of Affliction", 82, Rarity.MYTHIC, mage.cards.p.PharikaGodOfAffliction.class)); cards.add(new SetCardInfo("Phenax, God of Deception", 75, Rarity.MYTHIC, mage.cards.p.PhenaxGodOfDeception.class)); cards.add(new SetCardInfo("Pithing Needle", 44, Rarity.RARE, mage.cards.p.PithingNeedle.class)); cards.add(new SetCardInfo("Plains", 100, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 101, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 239, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Plains", 63, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Ponder", 245, Rarity.RARE, mage.cards.p.Ponder.class)); + cards.add(new SetCardInfo("Primeval Titan", 221, Rarity.MYTHIC, mage.cards.p.PrimevalTitan.class)); cards.add(new SetCardInfo("Purphoros, God of the Forge", 77, Rarity.MYTHIC, mage.cards.p.PurphorosGodOfTheForge.class)); cards.add(new SetCardInfo("Qasali Slingers", 24, Rarity.RARE, mage.cards.q.QasaliSlingers.class)); cards.add(new SetCardInfo("Ral, Storm Conduit", 523, Rarity.RARE, mage.cards.r.RalStormConduit.class)); @@ -154,28 +175,34 @@ private SecretLairDrop() { cards.add(new SetCardInfo("Serum Visions", 30, Rarity.RARE, mage.cards.s.SerumVisions.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Serum Visions", 31, Rarity.RARE, mage.cards.s.SerumVisions.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Serum Visions", 32, Rarity.RARE, mage.cards.s.SerumVisions.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Sliver Overlord", 10, Rarity.MYTHIC, mage.cards.s.SliverOverlord.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Snow-Covered Forest", 5, Rarity.LAND, mage.cards.s.SnowCoveredForest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Snow-Covered Island", 2, Rarity.LAND, mage.cards.s.SnowCoveredIsland.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Snow-Covered Mountain", 4, Rarity.LAND, mage.cards.s.SnowCoveredMountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Snow-Covered Plains", 1, Rarity.LAND, mage.cards.s.SnowCoveredPlains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Snow-Covered Swamp", 3, Rarity.LAND, mage.cards.s.SnowCoveredSwamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shalai, Voice of Plenty", 244, Rarity.RARE, mage.cards.s.ShalaiVoiceOfPlenty.class)); + cards.add(new SetCardInfo("Sliver Overlord", 10, Rarity.MYTHIC, mage.cards.s.SliverOverlord.class)); + cards.add(new SetCardInfo("Sol Ring", 249, Rarity.RARE, mage.cards.s.SolRing.class)); cards.add(new SetCardInfo("Sorin, Vengeful Bloodlord", 524, Rarity.RARE, mage.cards.s.SorinVengefulBloodlord.class)); cards.add(new SetCardInfo("Sower of Temptation", 120, Rarity.RARE, mage.cards.s.SowerOfTemptation.class)); cards.add(new SetCardInfo("Spell Pierce", 41, Rarity.RARE, mage.cards.s.SpellPierce.class)); + cards.add(new SetCardInfo("Spellstutter Sprite", 117, Rarity.RARE, mage.cards.s.SpellstutterSprite.class)); cards.add(new SetCardInfo("Squirrel Mob", 197, Rarity.RARE, mage.cards.s.SquirrelMob.class)); cards.add(new SetCardInfo("Squirrel Wrangler", 198, Rarity.RARE, mage.cards.s.SquirrelWrangler.class)); cards.add(new SetCardInfo("Storm Crow", 60, Rarity.RARE, mage.cards.s.StormCrow.class)); cards.add(new SetCardInfo("Swamp", 104, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 105, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 119, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 241, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Swamp", 65, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 539, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Swan Song", 91, Rarity.RARE, mage.cards.s.SwanSong.class)); cards.add(new SetCardInfo("Swarmyard", 199, Rarity.RARE, mage.cards.s.Swarmyard.class)); cards.add(new SetCardInfo("Swords to Plowshares", 110, Rarity.RARE, mage.cards.s.SwordsToPlowshares.class)); cards.add(new SetCardInfo("Tamiyo, Collector of Tales", 525, Rarity.RARE, mage.cards.t.TamiyoCollectorOfTales.class)); cards.add(new SetCardInfo("Tamiyo, Field Researcher", 89, Rarity.MYTHIC, mage.cards.t.TamiyoFieldResearcher.class)); cards.add(new SetCardInfo("Teferi's Protection", 164, Rarity.RARE, mage.cards.t.TeferisProtection.class)); + cards.add(new SetCardInfo("Teferi, Hero of Dominaria", 248, Rarity.MYTHIC, mage.cards.t.TeferiHeroOfDominaria.class)); cards.add(new SetCardInfo("Teferi, Time Raveler", 526, Rarity.RARE, mage.cards.t.TeferiTimeRaveler.class)); cards.add(new SetCardInfo("Teyo, the Shieldmage", 504, Rarity.UNCOMMON, mage.cards.t.TeyoTheShieldmage.class)); cards.add(new SetCardInfo("Thalia, Guardian of Thraben", 37, Rarity.RARE, mage.cards.t.ThaliaGuardianOfThraben.class, NON_FULL_USE_VARIOUS)); @@ -190,6 +217,8 @@ private SecretLairDrop() { cards.add(new SetCardInfo("Tibalt, Rakish Instigator", 515, Rarity.UNCOMMON, mage.cards.t.TibaltRakishInstigator.class)); cards.add(new SetCardInfo("Tibalt, the Fiend-Blooded", 537, Rarity.MYTHIC, mage.cards.t.TibaltTheFiendBlooded.class)); cards.add(new SetCardInfo("Ugin, the Ineffable", 502, Rarity.RARE, mage.cards.u.UginTheIneffable.class)); + cards.add(new SetCardInfo("Uro, Titan of Nature's Wrath", 222, Rarity.MYTHIC, mage.cards.u.UroTitanOfNaturesWrath.class)); + cards.add(new SetCardInfo("Vendilion Clique", 118, Rarity.RARE, mage.cards.v.VendilionClique.class)); cards.add(new SetCardInfo("Vivien, Champion of the Wilds", 519, Rarity.RARE, mage.cards.v.VivienChampionOfTheWilds.class)); cards.add(new SetCardInfo("Voidslime", 137, Rarity.RARE, mage.cards.v.Voidslime.class)); cards.add(new SetCardInfo("Vraska, Golgari Queen", 90, Rarity.MYTHIC, mage.cards.v.VraskaGolgariQueen.class)); diff --git a/Mage.Sets/src/mage/sets/SecretLairUltimateEdition.java b/Mage.Sets/src/mage/sets/SecretLairUltimateEdition.java index edb3b4e22b94..8d4c088d7cf9 100644 --- a/Mage.Sets/src/mage/sets/SecretLairUltimateEdition.java +++ b/Mage.Sets/src/mage/sets/SecretLairUltimateEdition.java @@ -21,9 +21,19 @@ private SecretLairUltimateEdition() { this.hasBasicLands = false; cards.add(new SetCardInfo("Arid Mesa", 4, Rarity.RARE, mage.cards.a.AridMesa.class)); + cards.add(new SetCardInfo("Barkchannel Pathway", 11, Rarity.RARE, mage.cards.b.BarkchannelPathway.class)); + cards.add(new SetCardInfo("Blightstep Pathway", 12, Rarity.RARE, mage.cards.b.BlightstepPathway.class)); + cards.add(new SetCardInfo("Branchloft Pathway", 13, Rarity.RARE, mage.cards.b.BranchloftPathway.class)); + cards.add(new SetCardInfo("Brightclimb Pathway", 14, Rarity.RARE, mage.cards.b.BrightclimbPathway.class)); + cards.add(new SetCardInfo("Clearwater Pathway", 15, Rarity.RARE, mage.cards.c.ClearwaterPathway.class)); + cards.add(new SetCardInfo("Cragcrown Pathway", 16, Rarity.RARE, mage.cards.c.CragcrownPathway.class)); + cards.add(new SetCardInfo("Darkbore Pathway", 17, Rarity.RARE, mage.cards.d.DarkborePathway.class)); + cards.add(new SetCardInfo("Hengegate Pathway", 18, Rarity.RARE, mage.cards.h.HengegatePathway.class)); cards.add(new SetCardInfo("Marsh Flats", 1, Rarity.RARE, mage.cards.m.MarshFlats.class)); cards.add(new SetCardInfo("Misty Rainforest", 5, Rarity.RARE, mage.cards.m.MistyRainforest.class)); + cards.add(new SetCardInfo("Needleverge Pathway", 19, Rarity.RARE, mage.cards.n.NeedlevergePathway.class)); + cards.add(new SetCardInfo("Riverglide Pathway", 20, Rarity.RARE, mage.cards.r.RiverglidePathway.class)); cards.add(new SetCardInfo("Scalding Tarn", 2, Rarity.RARE, mage.cards.s.ScaldingTarn.class)); cards.add(new SetCardInfo("Verdant Catacombs", 3, Rarity.RARE, mage.cards.v.VerdantCatacombs.class)); - } + } } diff --git a/Mage.Sets/src/mage/sets/StrixhavenMysticalArchive.java b/Mage.Sets/src/mage/sets/StrixhavenMysticalArchive.java new file mode 100644 index 000000000000..f7058980a371 --- /dev/null +++ b/Mage.Sets/src/mage/sets/StrixhavenMysticalArchive.java @@ -0,0 +1,151 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * @author TheElk801 + */ +public final class StrixhavenMysticalArchive extends ExpansionSet { + + private static final StrixhavenMysticalArchive instance = new StrixhavenMysticalArchive(); + + public static StrixhavenMysticalArchive getInstance() { + return instance; + } + + private StrixhavenMysticalArchive() { + super("Strixhaven Mystical Archive", "STA", ExpansionSet.buildDate(2021, 4, 23), SetType.SUPPLEMENTAL); + this.hasBoosters = false; + this.hasBasicLands = false; + this.maxCardNumberInBooster = 63; + + cards.add(new SetCardInfo("Abundant Harvest", 111, Rarity.RARE, mage.cards.a.AbundantHarvest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Abundant Harvest", 48, Rarity.RARE, mage.cards.a.AbundantHarvest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Adventurous Impulse", 112, Rarity.UNCOMMON, mage.cards.a.AdventurousImpulse.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Adventurous Impulse", 49, Rarity.UNCOMMON, mage.cards.a.AdventurousImpulse.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Agonizing Remorse", 24, Rarity.UNCOMMON, mage.cards.a.AgonizingRemorse.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Agonizing Remorse", 87, Rarity.UNCOMMON, mage.cards.a.AgonizingRemorse.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Approach of the Second Sun", 1, Rarity.MYTHIC, mage.cards.a.ApproachOfTheSecondSun.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Approach of the Second Sun", 64, Rarity.MYTHIC, mage.cards.a.ApproachOfTheSecondSun.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Blue Sun's Zenith", 12, Rarity.MYTHIC, mage.cards.b.BlueSunsZenith.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Blue Sun's Zenith", 75, Rarity.MYTHIC, mage.cards.b.BlueSunsZenith.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Brainstorm", 13, Rarity.RARE, mage.cards.b.Brainstorm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Brainstorm", 76, Rarity.RARE, mage.cards.b.Brainstorm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Channel", 113, Rarity.MYTHIC, mage.cards.c.Channel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Channel", 50, Rarity.MYTHIC, mage.cards.c.Channel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chaos Warp", 36, Rarity.MYTHIC, mage.cards.c.ChaosWarp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chaos Warp", 99, Rarity.MYTHIC, mage.cards.c.ChaosWarp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Claim the Firstborn", 100, Rarity.UNCOMMON, mage.cards.c.ClaimTheFirstborn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Claim the Firstborn", 37, Rarity.UNCOMMON, mage.cards.c.ClaimTheFirstborn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Compulsive Research", 14, Rarity.RARE, mage.cards.c.CompulsiveResearch.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Compulsive Research", 77, Rarity.RARE, mage.cards.c.CompulsiveResearch.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Counterspell", 15, Rarity.RARE, mage.cards.c.Counterspell.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Counterspell", 78, Rarity.RARE, mage.cards.c.Counterspell.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Crux of Fate", 25, Rarity.MYTHIC, mage.cards.c.CruxOfFate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Crux of Fate", 88, Rarity.MYTHIC, mage.cards.c.CruxOfFate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cultivate", 114, Rarity.UNCOMMON, mage.cards.c.Cultivate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cultivate", 51, Rarity.UNCOMMON, mage.cards.c.Cultivate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dark Ritual", 26, Rarity.RARE, mage.cards.d.DarkRitual.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dark Ritual", 89, Rarity.RARE, mage.cards.d.DarkRitual.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Day of Judgment", 2, Rarity.MYTHIC, mage.cards.d.DayOfJudgment.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Day of Judgment", 65, Rarity.MYTHIC, mage.cards.d.DayOfJudgment.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Defiant Strike", 3, Rarity.UNCOMMON, mage.cards.d.DefiantStrike.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Defiant Strike", 66, Rarity.UNCOMMON, mage.cards.d.DefiantStrike.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Demonic Tutor", 27, Rarity.MYTHIC, mage.cards.d.DemonicTutor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Demonic Tutor", 90, Rarity.MYTHIC, mage.cards.d.DemonicTutor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Despark", 122, Rarity.RARE, mage.cards.d.Despark.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Despark", 59, Rarity.RARE, mage.cards.d.Despark.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Divine Gambit", 4, Rarity.UNCOMMON, mage.cards.d.DivineGambit.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Divine Gambit", 67, Rarity.UNCOMMON, mage.cards.d.DivineGambit.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Doom Blade", 28, Rarity.RARE, mage.cards.d.DoomBlade.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Doom Blade", 91, Rarity.RARE, mage.cards.d.DoomBlade.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Duress", 29, Rarity.UNCOMMON, mage.cards.d.Duress.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Duress", 92, Rarity.UNCOMMON, mage.cards.d.Duress.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Electrolyze", 123, Rarity.RARE, mage.cards.e.Electrolyze.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Electrolyze", 60, Rarity.RARE, mage.cards.e.Electrolyze.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eliminate", 30, Rarity.UNCOMMON, mage.cards.e.Eliminate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eliminate", 93, Rarity.UNCOMMON, mage.cards.e.Eliminate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ephemerate", 5, Rarity.RARE, mage.cards.e.Ephemerate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ephemerate", 68, Rarity.RARE, mage.cards.e.Ephemerate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Faithless Looting", 101, Rarity.RARE, mage.cards.f.FaithlessLooting.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Faithless Looting", 38, Rarity.RARE, mage.cards.f.FaithlessLooting.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gift of Estates", 6, Rarity.RARE, mage.cards.g.GiftOfEstates.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gift of Estates", 69, Rarity.RARE, mage.cards.g.GiftOfEstates.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gods Willing", 7, Rarity.RARE, mage.cards.g.GodsWilling.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gods Willing", 70, Rarity.RARE, mage.cards.g.GodsWilling.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Grapeshot", 102, Rarity.RARE, mage.cards.g.Grapeshot.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Grapeshot", 39, Rarity.RARE, mage.cards.g.Grapeshot.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Growth Spiral", 124, Rarity.RARE, mage.cards.g.GrowthSpiral.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Growth Spiral", 61, Rarity.RARE, mage.cards.g.GrowthSpiral.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Harmonize", 115, Rarity.RARE, mage.cards.h.Harmonize.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Harmonize", 52, Rarity.RARE, mage.cards.h.Harmonize.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Increasing Vengeance", 103, Rarity.MYTHIC, mage.cards.i.IncreasingVengeance.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Increasing Vengeance", 40, Rarity.MYTHIC, mage.cards.i.IncreasingVengeance.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Infuriate", 104, Rarity.UNCOMMON, mage.cards.i.Infuriate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Infuriate", 41, Rarity.UNCOMMON, mage.cards.i.Infuriate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Inquisition of Kozilek", 31, Rarity.RARE, mage.cards.i.InquisitionOfKozilek.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Inquisition of Kozilek", 94, Rarity.RARE, mage.cards.i.InquisitionOfKozilek.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Krosan Grip", 116, Rarity.RARE, mage.cards.k.KrosanGrip.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Krosan Grip", 53, Rarity.RARE, mage.cards.k.KrosanGrip.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lightning Bolt", 105, Rarity.RARE, mage.cards.l.LightningBolt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lightning Bolt", 42, Rarity.RARE, mage.cards.l.LightningBolt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lightning Helix", 125, Rarity.RARE, mage.cards.l.LightningHelix.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lightning Helix", 62, Rarity.RARE, mage.cards.l.LightningHelix.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mana Tithe", 71, Rarity.RARE, mage.cards.m.ManaTithe.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mana Tithe", 8, Rarity.RARE, mage.cards.m.ManaTithe.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Memory Lapse", 16, Rarity.RARE, mage.cards.m.MemoryLapse.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Memory Lapse", 79, Rarity.RARE, mage.cards.m.MemoryLapse.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mind's Desire", 17, Rarity.MYTHIC, mage.cards.m.MindsDesire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mind's Desire", 80, Rarity.MYTHIC, mage.cards.m.MindsDesire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mizzix's Mastery", 106, Rarity.MYTHIC, mage.cards.m.MizzixsMastery.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mizzix's Mastery", 43, Rarity.MYTHIC, mage.cards.m.MizzixsMastery.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Natural Order", 117, Rarity.MYTHIC, mage.cards.n.NaturalOrder.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Natural Order", 54, Rarity.MYTHIC, mage.cards.n.NaturalOrder.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Negate", 18, Rarity.UNCOMMON, mage.cards.n.Negate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Negate", 81, Rarity.UNCOMMON, mage.cards.n.Negate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Opt", 19, Rarity.UNCOMMON, mage.cards.o.Opt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Opt", 82, Rarity.UNCOMMON, mage.cards.o.Opt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Primal Command", 118, Rarity.MYTHIC, mage.cards.p.PrimalCommand.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Primal Command", 55, Rarity.MYTHIC, mage.cards.p.PrimalCommand.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Putrefy", 126, Rarity.RARE, mage.cards.p.Putrefy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Putrefy", 63, Rarity.RARE, mage.cards.p.Putrefy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Regrowth", 119, Rarity.RARE, mage.cards.r.Regrowth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Regrowth", 56, Rarity.RARE, mage.cards.r.Regrowth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Revitalize", 72, Rarity.UNCOMMON, mage.cards.r.Revitalize.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Revitalize", 9, Rarity.UNCOMMON, mage.cards.r.Revitalize.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shock", 107, Rarity.UNCOMMON, mage.cards.s.Shock.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shock", 44, Rarity.UNCOMMON, mage.cards.s.Shock.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sign in Blood", 32, Rarity.RARE, mage.cards.s.SignInBlood.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sign in Blood", 95, Rarity.RARE, mage.cards.s.SignInBlood.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Snakeskin Veil", 120, Rarity.UNCOMMON, mage.cards.s.SnakeskinVeil.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Snakeskin Veil", 57, Rarity.UNCOMMON, mage.cards.s.SnakeskinVeil.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stone Rain", 108, Rarity.RARE, mage.cards.s.StoneRain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stone Rain", 45, Rarity.RARE, mage.cards.s.StoneRain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Strategic Planning", 20, Rarity.UNCOMMON, mage.cards.s.StrategicPlanning.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Strategic Planning", 83, Rarity.UNCOMMON, mage.cards.s.StrategicPlanning.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swords to Plowshares", 10, Rarity.RARE, mage.cards.s.SwordsToPlowshares.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swords to Plowshares", 73, Rarity.RARE, mage.cards.s.SwordsToPlowshares.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tainted Pact", 33, Rarity.MYTHIC, mage.cards.t.TaintedPact.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tainted Pact", 96, Rarity.MYTHIC, mage.cards.t.TaintedPact.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teferi's Protection", 11, Rarity.MYTHIC, mage.cards.t.TeferisProtection.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teferi's Protection", 74, Rarity.MYTHIC, mage.cards.t.TeferisProtection.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tendrils of Agony", 34, Rarity.RARE, mage.cards.t.TendrilsOfAgony.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tendrils of Agony", 97, Rarity.RARE, mage.cards.t.TendrilsOfAgony.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tezzeret's Gambit", 21, Rarity.RARE, mage.cards.t.TezzeretsGambit.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tezzeret's Gambit", 84, Rarity.RARE, mage.cards.t.TezzeretsGambit.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Thrill of Possibility", 109, Rarity.UNCOMMON, mage.cards.t.ThrillOfPossibility.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Thrill of Possibility", 46, Rarity.UNCOMMON, mage.cards.t.ThrillOfPossibility.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Time Warp", 22, Rarity.MYTHIC, mage.cards.t.TimeWarp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Time Warp", 85, Rarity.MYTHIC, mage.cards.t.TimeWarp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urza's Rage", 110, Rarity.RARE, mage.cards.u.UrzasRage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urza's Rage", 47, Rarity.RARE, mage.cards.u.UrzasRage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Village Rites", 35, Rarity.UNCOMMON, mage.cards.v.VillageRites.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Village Rites", 98, Rarity.UNCOMMON, mage.cards.v.VillageRites.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Weather the Storm", 121, Rarity.RARE, mage.cards.w.WeatherTheStorm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Weather the Storm", 58, Rarity.RARE, mage.cards.w.WeatherTheStorm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Whirlwind Denial", 23, Rarity.UNCOMMON, mage.cards.w.WhirlwindDenial.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Whirlwind Denial", 86, Rarity.UNCOMMON, mage.cards.w.WhirlwindDenial.class, NON_FULL_USE_VARIOUS)); + } +} diff --git a/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java b/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java new file mode 100644 index 000000000000..57813151db6b --- /dev/null +++ b/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java @@ -0,0 +1,643 @@ +package mage.sets; + +import mage.cards.Card; +import mage.cards.ExpansionSet; +import mage.cards.repository.CardCriteria; +import mage.cards.repository.CardInfo; +import mage.cards.repository.CardRepository; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; +import mage.constants.Rarity; +import mage.constants.SetType; +import mage.constants.SubType; +import mage.util.RandomUtil; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author TheElk801 + */ +public final class StrixhavenSchoolOfMages extends ExpansionSet { + + private static final StrixhavenSchoolOfMages instance = new StrixhavenSchoolOfMages(); + + public static StrixhavenSchoolOfMages getInstance() { + return instance; + } + + private StrixhavenSchoolOfMages() { + super("Strixhaven: School of Mages", "STX", ExpansionSet.buildDate(2021, 4, 23), SetType.EXPANSION, new StrixhavenSchoolOfMagesCollator()); + this.blockName = "Strixhaven: School of Mages"; + this.hasBoosters = true; + this.hasBasicLands = true; + this.numBoosterCommon = 9; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 7.4; + this.maxCardNumberInBooster = 275; + + cards.add(new SetCardInfo("Academic Dispute", 91, Rarity.UNCOMMON, mage.cards.a.AcademicDispute.class)); + cards.add(new SetCardInfo("Academic Probation", 287, Rarity.RARE, mage.cards.a.AcademicProbation.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Academic Probation", 7, Rarity.RARE, mage.cards.a.AcademicProbation.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Access Tunnel", 262, Rarity.UNCOMMON, mage.cards.a.AccessTunnel.class)); + cards.add(new SetCardInfo("Accomplished Alchemist", 119, Rarity.RARE, mage.cards.a.AccomplishedAlchemist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Accomplished Alchemist", 314, Rarity.RARE, mage.cards.a.AccomplishedAlchemist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Aether Helix", 162, Rarity.UNCOMMON, mage.cards.a.AetherHelix.class)); + cards.add(new SetCardInfo("Ageless Guardian", 8, Rarity.COMMON, mage.cards.a.AgelessGuardian.class)); + cards.add(new SetCardInfo("Arcane Subtraction", 36, Rarity.COMMON, mage.cards.a.ArcaneSubtraction.class)); + cards.add(new SetCardInfo("Archmage Emeritus", 295, Rarity.RARE, mage.cards.a.ArchmageEmeritus.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Archmage Emeritus", 37, Rarity.RARE, mage.cards.a.ArchmageEmeritus.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Archmage Emeritus", 377, Rarity.RARE, mage.cards.a.ArchmageEmeritus.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Archway Commons", 263, Rarity.COMMON, mage.cards.a.ArchwayCommons.class)); + cards.add(new SetCardInfo("Ardent Dustspeaker", 92, Rarity.UNCOMMON, mage.cards.a.ArdentDustspeaker.class)); + cards.add(new SetCardInfo("Arrogant Poet", 63, Rarity.COMMON, mage.cards.a.ArrogantPoet.class)); + cards.add(new SetCardInfo("Augmenter Pugilist", 147, Rarity.RARE, mage.cards.a.AugmenterPugilist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Augmenter Pugilist", 321, Rarity.RARE, mage.cards.a.AugmenterPugilist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Baleful Mastery", 301, Rarity.RARE, mage.cards.b.BalefulMastery.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Baleful Mastery", 64, Rarity.RARE, mage.cards.b.BalefulMastery.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Basic Conjuration", 120, Rarity.RARE, mage.cards.b.BasicConjuration.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Basic Conjuration", 315, Rarity.RARE, mage.cards.b.BasicConjuration.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bayou Groff", 121, Rarity.COMMON, mage.cards.b.BayouGroff.class)); + cards.add(new SetCardInfo("Beaming Defiance", 9, Rarity.COMMON, mage.cards.b.BeamingDefiance.class)); + cards.add(new SetCardInfo("Beledros Witherbloom", 163, Rarity.MYTHIC, mage.cards.b.BeledrosWitherbloom.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Beledros Witherbloom", 282, Rarity.MYTHIC, mage.cards.b.BeledrosWitherbloom.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Biblioplex Assistant", 251, Rarity.COMMON, mage.cards.b.BiblioplexAssistant.class)); + cards.add(new SetCardInfo("Big Play", 122, Rarity.COMMON, mage.cards.b.BigPlay.class)); + cards.add(new SetCardInfo("Biomathematician", 164, Rarity.COMMON, mage.cards.b.Biomathematician.class)); + cards.add(new SetCardInfo("Blade Historian", 165, Rarity.RARE, mage.cards.b.BladeHistorian.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Blade Historian", 334, Rarity.RARE, mage.cards.b.BladeHistorian.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Blex, Vexing Pest", 148, Rarity.MYTHIC, mage.cards.b.BlexVexingPest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Blex, Vexing Pest", 322, Rarity.MYTHIC, mage.cards.b.BlexVexingPest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Blood Age General", 93, Rarity.COMMON, mage.cards.b.BloodAgeGeneral.class)); + cards.add(new SetCardInfo("Blood Researcher", 166, Rarity.COMMON, mage.cards.b.BloodResearcher.class)); + cards.add(new SetCardInfo("Blot Out the Sky", 167, Rarity.MYTHIC, mage.cards.b.BlotOutTheSky.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Blot Out the Sky", 335, Rarity.MYTHIC, mage.cards.b.BlotOutTheSky.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Body of Research", 168, Rarity.MYTHIC, mage.cards.b.BodyOfResearch.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Body of Research", 336, Rarity.MYTHIC, mage.cards.b.BodyOfResearch.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bookwurm", 123, Rarity.UNCOMMON, mage.cards.b.Bookwurm.class)); + cards.add(new SetCardInfo("Brackish Trudge", 65, Rarity.UNCOMMON, mage.cards.b.BrackishTrudge.class)); + cards.add(new SetCardInfo("Burrog Befuddler", 38, Rarity.COMMON, mage.cards.b.BurrogBefuddler.class)); + cards.add(new SetCardInfo("Bury in Books", 39, Rarity.COMMON, mage.cards.b.BuryInBooks.class)); + cards.add(new SetCardInfo("Callous Bloodmage", 302, Rarity.RARE, mage.cards.c.CallousBloodmage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Callous Bloodmage", 66, Rarity.RARE, mage.cards.c.CallousBloodmage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Campus Guide", 252, Rarity.COMMON, mage.cards.c.CampusGuide.class)); + cards.add(new SetCardInfo("Charge Through", 124, Rarity.COMMON, mage.cards.c.ChargeThrough.class)); + cards.add(new SetCardInfo("Clever Lumimancer", 10, Rarity.UNCOMMON, mage.cards.c.CleverLumimancer.class)); + cards.add(new SetCardInfo("Closing Statement", 169, Rarity.UNCOMMON, mage.cards.c.ClosingStatement.class)); + cards.add(new SetCardInfo("Codie, Vociferous Codex", 253, Rarity.RARE, mage.cards.c.CodieVociferousCodex.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Codie, Vociferous Codex", 357, Rarity.RARE, mage.cards.c.CodieVociferousCodex.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cogwork Archivist", 254, Rarity.COMMON, mage.cards.c.CogworkArchivist.class)); + cards.add(new SetCardInfo("Combat Professor", 11, Rarity.COMMON, mage.cards.c.CombatProfessor.class)); + cards.add(new SetCardInfo("Confront the Past", 303, Rarity.RARE, mage.cards.c.ConfrontThePast.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Confront the Past", 67, Rarity.RARE, mage.cards.c.ConfrontThePast.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Conspiracy Theorist", 307, Rarity.RARE, mage.cards.c.ConspiracyTheorist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Conspiracy Theorist", 94, Rarity.RARE, mage.cards.c.ConspiracyTheorist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Containment Breach", 125, Rarity.UNCOMMON, mage.cards.c.ContainmentBreach.class)); + cards.add(new SetCardInfo("Crackle with Power", 308, Rarity.MYTHIC, mage.cards.c.CrackleWithPower.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Crackle with Power", 95, Rarity.MYTHIC, mage.cards.c.CrackleWithPower.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cram Session", 170, Rarity.COMMON, mage.cards.c.CramSession.class)); + cards.add(new SetCardInfo("Creative Outburst", 171, Rarity.UNCOMMON, mage.cards.c.CreativeOutburst.class)); + cards.add(new SetCardInfo("Crushing Disappointment", 68, Rarity.COMMON, mage.cards.c.CrushingDisappointment.class)); + cards.add(new SetCardInfo("Culling Ritual", 172, Rarity.RARE, mage.cards.c.CullingRitual.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Culling Ritual", 337, Rarity.RARE, mage.cards.c.CullingRitual.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Culmination of Studies", 173, Rarity.RARE, mage.cards.c.CulminationOfStudies.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Culmination of Studies", 338, Rarity.RARE, mage.cards.c.CulminationOfStudies.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Curate", 40, Rarity.COMMON, mage.cards.c.Curate.class)); + cards.add(new SetCardInfo("Daemogoth Titan", 174, Rarity.RARE, mage.cards.d.DaemogothTitan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Daemogoth Titan", 339, Rarity.RARE, mage.cards.d.DaemogothTitan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Daemogoth Woe-Eater", 175, Rarity.UNCOMMON, mage.cards.d.DaemogothWoeEater.class)); + cards.add(new SetCardInfo("Deadly Brew", 176, Rarity.UNCOMMON, mage.cards.d.DeadlyBrew.class)); + cards.add(new SetCardInfo("Decisive Denial", 177, Rarity.UNCOMMON, mage.cards.d.DecisiveDenial.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Decisive Denial", 382, Rarity.UNCOMMON, mage.cards.d.DecisiveDenial.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Defend the Campus", 12, Rarity.COMMON, mage.cards.d.DefendTheCampus.class)); + cards.add(new SetCardInfo("Detention Vortex", 13, Rarity.UNCOMMON, mage.cards.d.DetentionVortex.class)); + cards.add(new SetCardInfo("Devastating Mastery", 14, Rarity.RARE, mage.cards.d.DevastatingMastery.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Devastating Mastery", 288, Rarity.RARE, mage.cards.d.DevastatingMastery.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Devouring Tendrils", 126, Rarity.UNCOMMON, mage.cards.d.DevouringTendrils.class)); + cards.add(new SetCardInfo("Dina, Soul Steeper", 178, Rarity.UNCOMMON, mage.cards.d.DinaSoulSteeper.class)); + cards.add(new SetCardInfo("Divide by Zero", 41, Rarity.UNCOMMON, mage.cards.d.DivideByZero.class)); + cards.add(new SetCardInfo("Double Major", 179, Rarity.RARE, mage.cards.d.DoubleMajor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Double Major", 340, Rarity.RARE, mage.cards.d.DoubleMajor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Draconic Intervention", 309, Rarity.RARE, mage.cards.d.DraconicIntervention.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Draconic Intervention", 96, Rarity.RARE, mage.cards.d.DraconicIntervention.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dragon's Approach", 97, Rarity.COMMON, mage.cards.d.DragonsApproach.class)); + cards.add(new SetCardInfo("Dragonsguard Elite", 127, Rarity.RARE, mage.cards.d.DragonsguardElite.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dragonsguard Elite", 316, Rarity.RARE, mage.cards.d.DragonsguardElite.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dragonsguard Elite", 376, Rarity.RARE, mage.cards.d.DragonsguardElite.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dramatic Finale", 180, Rarity.RARE, mage.cards.d.DramaticFinale.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dramatic Finale", 341, Rarity.RARE, mage.cards.d.DramaticFinale.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dream Strix", 296, Rarity.RARE, mage.cards.d.DreamStrix.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dream Strix", 42, Rarity.RARE, mage.cards.d.DreamStrix.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dueling Coach", 15, Rarity.UNCOMMON, mage.cards.d.DuelingCoach.class)); + cards.add(new SetCardInfo("Eager First-Year", 16, Rarity.COMMON, mage.cards.e.EagerFirstYear.class)); + cards.add(new SetCardInfo("Ecological Appreciation", 128, Rarity.MYTHIC, mage.cards.e.EcologicalAppreciation.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ecological Appreciation", 317, Rarity.MYTHIC, mage.cards.e.EcologicalAppreciation.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Efreet Flamepainter", 310, Rarity.RARE, mage.cards.e.EfreetFlamepainter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Efreet Flamepainter", 98, Rarity.RARE, mage.cards.e.EfreetFlamepainter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Elemental Expressionist", 181, Rarity.RARE, mage.cards.e.ElementalExpressionist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Elemental Expressionist", 342, Rarity.RARE, mage.cards.e.ElementalExpressionist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Elemental Masterpiece", 182, Rarity.COMMON, mage.cards.e.ElementalMasterpiece.class)); + cards.add(new SetCardInfo("Elemental Summoning", 183, Rarity.COMMON, mage.cards.e.ElementalSummoning.class)); + cards.add(new SetCardInfo("Elite Spellbinder", 17, Rarity.RARE, mage.cards.e.EliteSpellbinder.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Elite Spellbinder", 289, Rarity.RARE, mage.cards.e.EliteSpellbinder.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Emergent Sequence", 129, Rarity.UNCOMMON, mage.cards.e.EmergentSequence.class)); + cards.add(new SetCardInfo("Enthusiastic Study", 99, Rarity.COMMON, mage.cards.e.EnthusiasticStudy.class)); + cards.add(new SetCardInfo("Environmental Sciences", 1, Rarity.COMMON, mage.cards.e.EnvironmentalSciences.class)); + cards.add(new SetCardInfo("Essence Infusion", 69, Rarity.COMMON, mage.cards.e.EssenceInfusion.class)); + cards.add(new SetCardInfo("Eureka Moment", 184, Rarity.COMMON, mage.cards.e.EurekaMoment.class)); + cards.add(new SetCardInfo("Excavated Wall", 255, Rarity.COMMON, mage.cards.e.ExcavatedWall.class)); + cards.add(new SetCardInfo("Exhilarating Elocution", 185, Rarity.COMMON, mage.cards.e.ExhilaratingElocution.class)); + cards.add(new SetCardInfo("Expanded Anatomy", 2, Rarity.COMMON, mage.cards.e.ExpandedAnatomy.class)); + cards.add(new SetCardInfo("Expel", 18, Rarity.COMMON, mage.cards.e.Expel.class)); + cards.add(new SetCardInfo("Explosive Welcome", 100, Rarity.UNCOMMON, mage.cards.e.ExplosiveWelcome.class)); + cards.add(new SetCardInfo("Exponential Growth", 130, Rarity.RARE, mage.cards.e.ExponentialGrowth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Exponential Growth", 318, Rarity.RARE, mage.cards.e.ExponentialGrowth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Expressive Iteration", 186, Rarity.UNCOMMON, mage.cards.e.ExpressiveIteration.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Expressive Iteration", 379, Rarity.UNCOMMON, mage.cards.e.ExpressiveIteration.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Extus, Oriq Overlord", 149, Rarity.MYTHIC, mage.cards.e.ExtusOriqOverlord.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Extus, Oriq Overlord", 323, Rarity.MYTHIC, mage.cards.e.ExtusOriqOverlord.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eyetwitch", 70, Rarity.UNCOMMON, mage.cards.e.Eyetwitch.class)); + cards.add(new SetCardInfo("Fervent Mastery", 101, Rarity.RARE, mage.cards.f.FerventMastery.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fervent Mastery", 311, Rarity.RARE, mage.cards.f.FerventMastery.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Field Trip", 131, Rarity.COMMON, mage.cards.f.FieldTrip.class)); + cards.add(new SetCardInfo("First Day of Class", 102, Rarity.COMMON, mage.cards.f.FirstDayOfClass.class)); + cards.add(new SetCardInfo("Flamescroll Celebrant", 150, Rarity.RARE, mage.cards.f.FlamescrollCelebrant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Flamescroll Celebrant", 324, Rarity.RARE, mage.cards.f.FlamescrollCelebrant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Flunk", 71, Rarity.UNCOMMON, mage.cards.f.Flunk.class)); + cards.add(new SetCardInfo("Forest", 374, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 375, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fortifying Draught", 132, Rarity.UNCOMMON, mage.cards.f.FortifyingDraught.class)); + cards.add(new SetCardInfo("Fractal Summoning", 187, Rarity.COMMON, mage.cards.f.FractalSummoning.class)); + cards.add(new SetCardInfo("Fracture", 188, Rarity.UNCOMMON, mage.cards.f.Fracture.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fracture", 378, Rarity.UNCOMMON, mage.cards.f.Fracture.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Frost Trickster", 43, Rarity.COMMON, mage.cards.f.FrostTrickster.class)); + cards.add(new SetCardInfo("Frostboil Snarl", 265, Rarity.RARE, mage.cards.f.FrostboilSnarl.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Frostboil Snarl", 360, Rarity.RARE, mage.cards.f.FrostboilSnarl.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fuming Effigy", 103, Rarity.COMMON, mage.cards.f.FumingEffigy.class)); + cards.add(new SetCardInfo("Furycalm Snarl", 266, Rarity.RARE, mage.cards.f.FurycalmSnarl.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Furycalm Snarl", 361, Rarity.RARE, mage.cards.f.FurycalmSnarl.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Galazeth Prismari", 189, Rarity.MYTHIC, mage.cards.g.GalazethPrismari.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Galazeth Prismari", 281, Rarity.MYTHIC, mage.cards.g.GalazethPrismari.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gnarled Professor", 133, Rarity.RARE, mage.cards.g.GnarledProfessor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gnarled Professor", 319, Rarity.RARE, mage.cards.g.GnarledProfessor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Go Blank", 72, Rarity.UNCOMMON, mage.cards.g.GoBlank.class)); + cards.add(new SetCardInfo("Golden Ratio", 190, Rarity.UNCOMMON, mage.cards.g.GoldenRatio.class)); + cards.add(new SetCardInfo("Grinning Ignus", 104, Rarity.UNCOMMON, mage.cards.g.GrinningIgnus.class)); + cards.add(new SetCardInfo("Guiding Voice", 19, Rarity.COMMON, mage.cards.g.GuidingVoice.class)); + cards.add(new SetCardInfo("Hall Monitor", 105, Rarity.UNCOMMON, mage.cards.h.HallMonitor.class)); + cards.add(new SetCardInfo("Hall of Oracles", 267, Rarity.RARE, mage.cards.h.HallOfOracles.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hall of Oracles", 362, Rarity.RARE, mage.cards.h.HallOfOracles.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Harness Infinity", 191, Rarity.MYTHIC, mage.cards.h.HarnessInfinity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Harness Infinity", 343, Rarity.MYTHIC, mage.cards.h.HarnessInfinity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Heated Debate", 106, Rarity.COMMON, mage.cards.h.HeatedDebate.class)); + cards.add(new SetCardInfo("Hofri Ghostforge", 192, Rarity.MYTHIC, mage.cards.h.HofriGhostforge.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hofri Ghostforge", 344, Rarity.MYTHIC, mage.cards.h.HofriGhostforge.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Honor Troll", 134, Rarity.UNCOMMON, mage.cards.h.HonorTroll.class)); + cards.add(new SetCardInfo("Humiliate", 193, Rarity.UNCOMMON, mage.cards.h.Humiliate.class)); + cards.add(new SetCardInfo("Hunt for Specimens", 73, Rarity.COMMON, mage.cards.h.HuntForSpecimens.class)); + cards.add(new SetCardInfo("Igneous Inspiration", 107, Rarity.UNCOMMON, mage.cards.i.IgneousInspiration.class)); + cards.add(new SetCardInfo("Illuminate History", 108, Rarity.RARE, mage.cards.i.IlluminateHistory.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Illuminate History", 312, Rarity.RARE, mage.cards.i.IlluminateHistory.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Illustrious Historian", 109, Rarity.COMMON, mage.cards.i.IllustriousHistorian.class)); + cards.add(new SetCardInfo("Infuse with Vitality", 194, Rarity.COMMON, mage.cards.i.InfuseWithVitality.class)); + cards.add(new SetCardInfo("Ingenious Mastery", 297, Rarity.RARE, mage.cards.i.IngeniousMastery.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ingenious Mastery", 44, Rarity.RARE, mage.cards.i.IngeniousMastery.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Inkling Summoning", 195, Rarity.COMMON, mage.cards.i.InklingSummoning.class)); + cards.add(new SetCardInfo("Introduction to Annihilation", 3, Rarity.COMMON, mage.cards.i.IntroductionToAnnihilation.class)); + cards.add(new SetCardInfo("Introduction to Prophecy", 4, Rarity.COMMON, mage.cards.i.IntroductionToProphecy.class)); + cards.add(new SetCardInfo("Island", 368, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 369, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jadzi, Oracle of Arcavios", 151, Rarity.MYTHIC, mage.cards.j.JadziOracleOfArcavios.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jadzi, Oracle of Arcavios", 325, Rarity.MYTHIC, mage.cards.j.JadziOracleOfArcavios.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Karok Wrangler", 135, Rarity.UNCOMMON, mage.cards.k.KarokWrangler.class)); + cards.add(new SetCardInfo("Kasmina, Enigma Sage", 196, Rarity.MYTHIC, mage.cards.k.KasminaEnigmaSage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kasmina, Enigma Sage", 279, Rarity.MYTHIC, mage.cards.k.KasminaEnigmaSage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kelpie Guide", 45, Rarity.UNCOMMON, mage.cards.k.KelpieGuide.class)); + cards.add(new SetCardInfo("Kianne, Dean of Substance", 152, Rarity.RARE, mage.cards.k.KianneDeanOfSubstance.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kianne, Dean of Substance", 326, Rarity.RARE, mage.cards.k.KianneDeanOfSubstance.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Killian, Ink Duelist", 197, Rarity.UNCOMMON, mage.cards.k.KillianInkDuelist.class)); + cards.add(new SetCardInfo("Lash of Malice", 74, Rarity.COMMON, mage.cards.l.LashOfMalice.class)); + cards.add(new SetCardInfo("Leech Fanatic", 75, Rarity.COMMON, mage.cards.l.LeechFanatic.class)); + cards.add(new SetCardInfo("Leonin Lightscribe", 20, Rarity.RARE, mage.cards.l.LeoninLightscribe.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Leonin Lightscribe", 290, Rarity.RARE, mage.cards.l.LeoninLightscribe.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Letter of Acceptance", 256, Rarity.COMMON, mage.cards.l.LetterOfAcceptance.class)); + cards.add(new SetCardInfo("Leyline Invocation", 136, Rarity.COMMON, mage.cards.l.LeylineInvocation.class)); + cards.add(new SetCardInfo("Lorehold Apprentice", 198, Rarity.UNCOMMON, mage.cards.l.LoreholdApprentice.class)); + cards.add(new SetCardInfo("Lorehold Campus", 268, Rarity.COMMON, mage.cards.l.LoreholdCampus.class)); + cards.add(new SetCardInfo("Lorehold Command", 199, Rarity.RARE, mage.cards.l.LoreholdCommand.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lorehold Command", 345, Rarity.RARE, mage.cards.l.LoreholdCommand.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lorehold Excavation", 200, Rarity.UNCOMMON, mage.cards.l.LoreholdExcavation.class)); + cards.add(new SetCardInfo("Lorehold Pledgemage", 201, Rarity.COMMON, mage.cards.l.LoreholdPledgemage.class)); + cards.add(new SetCardInfo("Maelstrom Muse", 202, Rarity.UNCOMMON, mage.cards.m.MaelstromMuse.class)); + cards.add(new SetCardInfo("Mage Duel", 137, Rarity.COMMON, mage.cards.m.MageDuel.class)); + cards.add(new SetCardInfo("Mage Hunter", 76, Rarity.UNCOMMON, mage.cards.m.MageHunter.class)); + cards.add(new SetCardInfo("Mage Hunters' Onslaught", 77, Rarity.COMMON, mage.cards.m.MageHuntersOnslaught.class)); + cards.add(new SetCardInfo("Magma Opus", 203, Rarity.MYTHIC, mage.cards.m.MagmaOpus.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Magma Opus", 346, Rarity.MYTHIC, mage.cards.m.MagmaOpus.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Make Your Mark", 204, Rarity.COMMON, mage.cards.m.MakeYourMark.class)); + cards.add(new SetCardInfo("Manifestation Sage", 205, Rarity.RARE, mage.cards.m.ManifestationSage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Manifestation Sage", 347, Rarity.RARE, mage.cards.m.ManifestationSage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mascot Exhibition", 285, Rarity.MYTHIC, mage.cards.m.MascotExhibition.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mascot Exhibition", 5, Rarity.MYTHIC, mage.cards.m.MascotExhibition.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mascot Interception", 110, Rarity.UNCOMMON, mage.cards.m.MascotInterception.class)); + cards.add(new SetCardInfo("Master Symmetrist", 138, Rarity.UNCOMMON, mage.cards.m.MasterSymmetrist.class)); + cards.add(new SetCardInfo("Mavinda, Students' Advocate", 21, Rarity.MYTHIC, mage.cards.m.MavindaStudentsAdvocate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mavinda, Students' Advocate", 291, Rarity.MYTHIC, mage.cards.m.MavindaStudentsAdvocate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mentor's Guidance", 46, Rarity.UNCOMMON, mage.cards.m.MentorsGuidance.class)); + cards.add(new SetCardInfo("Mercurial Transformation", 47, Rarity.UNCOMMON, mage.cards.m.MercurialTransformation.class)); + cards.add(new SetCardInfo("Mila, Crafty Companion", 153, Rarity.MYTHIC, mage.cards.m.MilaCraftyCompanion.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mila, Crafty Companion", 277, Rarity.MYTHIC, mage.cards.m.MilaCraftyCompanion.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Moldering Karok", 206, Rarity.COMMON, mage.cards.m.MolderingKarok.class)); + cards.add(new SetCardInfo("Mortality Spear", 207, Rarity.UNCOMMON, mage.cards.m.MortalitySpear.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mortality Spear", 380, Rarity.UNCOMMON, mage.cards.m.MortalitySpear.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 372, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 373, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Multiple Choice", 298, Rarity.RARE, mage.cards.m.MultipleChoice.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Multiple Choice", 48, Rarity.RARE, mage.cards.m.MultipleChoice.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Necroblossom Snarl", 269, Rarity.RARE, mage.cards.n.NecroblossomSnarl.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Necroblossom Snarl", 363, Rarity.RARE, mage.cards.n.NecroblossomSnarl.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Necrotic Fumes", 78, Rarity.UNCOMMON, mage.cards.n.NecroticFumes.class)); + cards.add(new SetCardInfo("Needlethorn Drake", 208, Rarity.COMMON, mage.cards.n.NeedlethornDrake.class)); + cards.add(new SetCardInfo("Novice Dissector", 79, Rarity.COMMON, mage.cards.n.NoviceDissector.class)); + cards.add(new SetCardInfo("Oggyar Battle-Seer", 209, Rarity.COMMON, mage.cards.o.OggyarBattleSeer.class)); + cards.add(new SetCardInfo("Oriq Loremage", 304, Rarity.RARE, mage.cards.o.OriqLoremage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Oriq Loremage", 80, Rarity.RARE, mage.cards.o.OriqLoremage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Overgrown Arch", 139, Rarity.UNCOMMON, mage.cards.o.OvergrownArch.class)); + cards.add(new SetCardInfo("Owlin Shieldmage", 210, Rarity.COMMON, mage.cards.o.OwlinShieldmage.class)); + cards.add(new SetCardInfo("Pest Summoning", 211, Rarity.COMMON, mage.cards.p.PestSummoning.class)); + cards.add(new SetCardInfo("Pestilent Cauldron", 154, Rarity.RARE, mage.cards.p.PestilentCauldron.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Pestilent Cauldron", 327, Rarity.RARE, mage.cards.p.PestilentCauldron.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Pigment Storm", 111, Rarity.COMMON, mage.cards.p.PigmentStorm.class)); + cards.add(new SetCardInfo("Pilgrim of the Ages", 22, Rarity.COMMON, mage.cards.p.PilgrimOfTheAges.class)); + cards.add(new SetCardInfo("Pillardrop Rescuer", 23, Rarity.COMMON, mage.cards.p.PillardropRescuer.class)); + cards.add(new SetCardInfo("Pillardrop Warden", 112, Rarity.COMMON, mage.cards.p.PillardropWarden.class)); + cards.add(new SetCardInfo("Plains", 366, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 367, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plargg, Dean of Chaos", 155, Rarity.RARE, mage.cards.p.PlarggDeanOfChaos.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plargg, Dean of Chaos", 328, Rarity.RARE, mage.cards.p.PlarggDeanOfChaos.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plumb the Forbidden", 81, Rarity.UNCOMMON, mage.cards.p.PlumbTheForbidden.class)); + cards.add(new SetCardInfo("Poet's Quill", 305, Rarity.RARE, mage.cards.p.PoetsQuill.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Poet's Quill", 82, Rarity.RARE, mage.cards.p.PoetsQuill.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Pop Quiz", 49, Rarity.COMMON, mage.cards.p.PopQuiz.class)); + cards.add(new SetCardInfo("Practical Research", 212, Rarity.UNCOMMON, mage.cards.p.PracticalResearch.class)); + cards.add(new SetCardInfo("Prismari Apprentice", 213, Rarity.UNCOMMON, mage.cards.p.PrismariApprentice.class)); + cards.add(new SetCardInfo("Prismari Campus", 270, Rarity.COMMON, mage.cards.p.PrismariCampus.class)); + cards.add(new SetCardInfo("Prismari Command", 214, Rarity.RARE, mage.cards.p.PrismariCommand.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Prismari Command", 348, Rarity.RARE, mage.cards.p.PrismariCommand.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Prismari Pledgemage", 215, Rarity.COMMON, mage.cards.p.PrismariPledgemage.class)); + cards.add(new SetCardInfo("Professor Onyx", 276, Rarity.MYTHIC, mage.cards.p.ProfessorOnyx.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Professor Onyx", 83, Rarity.MYTHIC, mage.cards.p.ProfessorOnyx.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Professor of Symbology", 24, Rarity.UNCOMMON, mage.cards.p.ProfessorOfSymbology.class)); + cards.add(new SetCardInfo("Professor of Zoomancy", 140, Rarity.COMMON, mage.cards.p.ProfessorOfZoomancy.class)); + cards.add(new SetCardInfo("Professor's Warning", 84, Rarity.COMMON, mage.cards.p.ProfessorsWarning.class)); + cards.add(new SetCardInfo("Promising Duskmage", 85, Rarity.COMMON, mage.cards.p.PromisingDuskmage.class)); + cards.add(new SetCardInfo("Quandrix Apprentice", 216, Rarity.UNCOMMON, mage.cards.q.QuandrixApprentice.class)); + cards.add(new SetCardInfo("Quandrix Campus", 271, Rarity.COMMON, mage.cards.q.QuandrixCampus.class)); + cards.add(new SetCardInfo("Quandrix Command", 217, Rarity.RARE, mage.cards.q.QuandrixCommand.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Quandrix Command", 349, Rarity.RARE, mage.cards.q.QuandrixCommand.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Quandrix Cultivator", 218, Rarity.UNCOMMON, mage.cards.q.QuandrixCultivator.class)); + cards.add(new SetCardInfo("Quandrix Pledgemage", 219, Rarity.COMMON, mage.cards.q.QuandrixPledgemage.class)); + cards.add(new SetCardInfo("Quintorius, Field Historian", 220, Rarity.UNCOMMON, mage.cards.q.QuintoriusFieldHistorian.class)); + cards.add(new SetCardInfo("Radiant Scrollwielder", 221, Rarity.RARE, mage.cards.r.RadiantScrollwielder.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Radiant Scrollwielder", 350, Rarity.RARE, mage.cards.r.RadiantScrollwielder.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reckless Amplimancer", 141, Rarity.COMMON, mage.cards.r.RecklessAmplimancer.class)); + cards.add(new SetCardInfo("Reconstruct History", 222, Rarity.UNCOMMON, mage.cards.r.ReconstructHistory.class)); + cards.add(new SetCardInfo("Reduce to Memory", 25, Rarity.UNCOMMON, mage.cards.r.ReduceToMemory.class)); + cards.add(new SetCardInfo("Reflective Golem", 257, Rarity.UNCOMMON, mage.cards.r.ReflectiveGolem.class)); + cards.add(new SetCardInfo("Reject", 50, Rarity.COMMON, mage.cards.r.Reject.class)); + cards.add(new SetCardInfo("Relic Sloth", 223, Rarity.COMMON, mage.cards.r.RelicSloth.class)); + cards.add(new SetCardInfo("Resculpt", 51, Rarity.COMMON, mage.cards.r.Resculpt.class)); + cards.add(new SetCardInfo("Retriever Phoenix", 113, Rarity.RARE, mage.cards.r.RetrieverPhoenix.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Retriever Phoenix", 313, Rarity.RARE, mage.cards.r.RetrieverPhoenix.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Returned Pastcaller", 224, Rarity.UNCOMMON, mage.cards.r.ReturnedPastcaller.class)); + cards.add(new SetCardInfo("Rip Apart", 225, Rarity.UNCOMMON, mage.cards.r.RipApart.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rip Apart", 381, Rarity.UNCOMMON, mage.cards.r.RipApart.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rise of Extus", 226, Rarity.COMMON, mage.cards.r.RiseOfExtus.class)); + cards.add(new SetCardInfo("Rootha, Mercurial Artist", 227, Rarity.UNCOMMON, mage.cards.r.RoothaMercurialArtist.class)); + cards.add(new SetCardInfo("Rowan, Scholar of Sparks", 156, Rarity.MYTHIC, mage.cards.r.RowanScholarOfSparks.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rowan, Scholar of Sparks", 278, Rarity.MYTHIC, mage.cards.r.RowanScholarOfSparks.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rushed Rebirth", 228, Rarity.RARE, mage.cards.r.RushedRebirth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rushed Rebirth", 351, Rarity.RARE, mage.cards.r.RushedRebirth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Scurrid Colony", 142, Rarity.COMMON, mage.cards.s.ScurridColony.class)); + cards.add(new SetCardInfo("Secret Rendezvous", 26, Rarity.UNCOMMON, mage.cards.s.SecretRendezvous.class)); + cards.add(new SetCardInfo("Sedgemoor Witch", 306, Rarity.RARE, mage.cards.s.SedgemoorWitch.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sedgemoor Witch", 86, Rarity.RARE, mage.cards.s.SedgemoorWitch.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Selfless Glyphweaver", 157, Rarity.RARE, mage.cards.s.SelflessGlyphweaver.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Selfless Glyphweaver", 329, Rarity.RARE, mage.cards.s.SelflessGlyphweaver.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Semester's End", 27, Rarity.RARE, mage.cards.s.SemestersEnd.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Semester's End", 292, Rarity.RARE, mage.cards.s.SemestersEnd.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Serpentine Curve", 52, Rarity.COMMON, mage.cards.s.SerpentineCurve.class)); + cards.add(new SetCardInfo("Shadewing Laureate", 229, Rarity.UNCOMMON, mage.cards.s.ShadewingLaureate.class)); + cards.add(new SetCardInfo("Shadrix Silverquill", 230, Rarity.MYTHIC, mage.cards.s.ShadrixSilverquill.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shadrix Silverquill", 280, Rarity.MYTHIC, mage.cards.s.ShadrixSilverquill.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shaile, Dean of Radiance", 158, Rarity.RARE, mage.cards.s.ShaileDeanOfRadiance.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shaile, Dean of Radiance", 330, Rarity.RARE, mage.cards.s.ShaileDeanOfRadiance.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shineshadow Snarl", 272, Rarity.RARE, mage.cards.s.ShineshadowSnarl.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shineshadow Snarl", 364, Rarity.RARE, mage.cards.s.ShineshadowSnarl.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Show of Confidence", 28, Rarity.UNCOMMON, mage.cards.s.ShowOfConfidence.class)); + cards.add(new SetCardInfo("Silverquill Apprentice", 231, Rarity.UNCOMMON, mage.cards.s.SilverquillApprentice.class)); + cards.add(new SetCardInfo("Silverquill Campus", 273, Rarity.COMMON, mage.cards.s.SilverquillCampus.class)); + cards.add(new SetCardInfo("Silverquill Command", 232, Rarity.RARE, mage.cards.s.SilverquillCommand.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Silverquill Command", 352, Rarity.RARE, mage.cards.s.SilverquillCommand.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Silverquill Pledgemage", 233, Rarity.COMMON, mage.cards.s.SilverquillPledgemage.class)); + cards.add(new SetCardInfo("Silverquill Silencer", 234, Rarity.RARE, mage.cards.s.SilverquillSilencer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Silverquill Silencer", 353, Rarity.RARE, mage.cards.s.SilverquillSilencer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Snow Day", 53, Rarity.UNCOMMON, mage.cards.s.SnowDay.class)); + cards.add(new SetCardInfo("Solve the Equation", 54, Rarity.UNCOMMON, mage.cards.s.SolveTheEquation.class)); + cards.add(new SetCardInfo("Soothsayer Adept", 55, Rarity.COMMON, mage.cards.s.SoothsayerAdept.class)); + cards.add(new SetCardInfo("Sparring Regimen", 29, Rarity.RARE, mage.cards.s.SparringRegimen.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sparring Regimen", 293, Rarity.RARE, mage.cards.s.SparringRegimen.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spectacle Mage", 235, Rarity.COMMON, mage.cards.s.SpectacleMage.class)); + cards.add(new SetCardInfo("Specter of the Fens", 87, Rarity.COMMON, mage.cards.s.SpecterOfTheFens.class)); + cards.add(new SetCardInfo("Spell Satchel", 258, Rarity.UNCOMMON, mage.cards.s.SpellSatchel.class)); + cards.add(new SetCardInfo("Spined Karok", 143, Rarity.COMMON, mage.cards.s.SpinedKarok.class)); + cards.add(new SetCardInfo("Spirit Summoning", 236, Rarity.COMMON, mage.cards.s.SpiritSummoning.class)); + cards.add(new SetCardInfo("Spiteful Squad", 237, Rarity.COMMON, mage.cards.s.SpitefulSquad.class)); + cards.add(new SetCardInfo("Springmane Cervin", 144, Rarity.COMMON, mage.cards.s.SpringmaneCervin.class)); + cards.add(new SetCardInfo("Square Up", 238, Rarity.COMMON, mage.cards.s.SquareUp.class)); + cards.add(new SetCardInfo("Star Pupil", 30, Rarity.COMMON, mage.cards.s.StarPupil.class)); + cards.add(new SetCardInfo("Start from Scratch", 114, Rarity.UNCOMMON, mage.cards.s.StartFromScratch.class)); + cards.add(new SetCardInfo("Stonebinder's Familiar", 31, Rarity.UNCOMMON, mage.cards.s.StonebindersFamiliar.class)); + cards.add(new SetCardInfo("Stonebound Mentor", 239, Rarity.COMMON, mage.cards.s.StoneboundMentor.class)); + cards.add(new SetCardInfo("Stonerise Spirit", 32, Rarity.COMMON, mage.cards.s.StoneriseSpirit.class)); + cards.add(new SetCardInfo("Storm-Kiln Artist", 115, Rarity.UNCOMMON, mage.cards.s.StormKilnArtist.class)); + cards.add(new SetCardInfo("Strict Proctor", 294, Rarity.RARE, mage.cards.s.StrictProctor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Strict Proctor", 33, Rarity.RARE, mage.cards.s.StrictProctor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Strixhaven Stadium", 259, Rarity.RARE, mage.cards.s.StrixhavenStadium.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Strixhaven Stadium", 358, Rarity.RARE, mage.cards.s.StrixhavenStadium.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Study Break", 34, Rarity.COMMON, mage.cards.s.StudyBreak.class)); + cards.add(new SetCardInfo("Sudden Breakthrough", 116, Rarity.COMMON, mage.cards.s.SuddenBreakthrough.class)); + cards.add(new SetCardInfo("Swamp", 370, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 371, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Symmetry Sage", 56, Rarity.UNCOMMON, mage.cards.s.SymmetrySage.class)); + cards.add(new SetCardInfo("Tanazir Quandrix", 240, Rarity.MYTHIC, mage.cards.t.TanazirQuandrix.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tanazir Quandrix", 284, Rarity.MYTHIC, mage.cards.t.TanazirQuandrix.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tangletrap", 145, Rarity.COMMON, mage.cards.t.Tangletrap.class)); + cards.add(new SetCardInfo("Teach by Example", 241, Rarity.COMMON, mage.cards.t.TeachByExample.class)); + cards.add(new SetCardInfo("Teachings of the Archaics", 299, Rarity.RARE, mage.cards.t.TeachingsOfTheArchaics.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teachings of the Archaics", 57, Rarity.RARE, mage.cards.t.TeachingsOfTheArchaics.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Team Pennant", 260, Rarity.UNCOMMON, mage.cards.t.TeamPennant.class)); + cards.add(new SetCardInfo("Tempted by the Oriq", 300, Rarity.RARE, mage.cards.t.TemptedByTheOriq.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tempted by the Oriq", 58, Rarity.RARE, mage.cards.t.TemptedByTheOriq.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tend the Pests", 242, Rarity.UNCOMMON, mage.cards.t.TendThePests.class)); + cards.add(new SetCardInfo("Tenured Inkcaster", 88, Rarity.UNCOMMON, mage.cards.t.TenuredInkcaster.class)); + cards.add(new SetCardInfo("Test of Talents", 59, Rarity.UNCOMMON, mage.cards.t.TestOfTalents.class)); + cards.add(new SetCardInfo("The Biblioplex", 264, Rarity.RARE, mage.cards.t.TheBiblioplex.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Biblioplex", 359, Rarity.RARE, mage.cards.t.TheBiblioplex.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Thrilling Discovery", 243, Rarity.COMMON, mage.cards.t.ThrillingDiscovery.class)); + cards.add(new SetCardInfo("Thunderous Orator", 35, Rarity.UNCOMMON, mage.cards.t.ThunderousOrator.class)); + cards.add(new SetCardInfo("Tome Shredder", 117, Rarity.COMMON, mage.cards.t.TomeShredder.class)); + cards.add(new SetCardInfo("Torrent Sculptor", 159, Rarity.RARE, mage.cards.t.TorrentSculptor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Torrent Sculptor", 331, Rarity.RARE, mage.cards.t.TorrentSculptor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Twinscroll Shaman", 118, Rarity.COMMON, mage.cards.t.TwinscrollShaman.class)); + cards.add(new SetCardInfo("Umbral Juke", 89, Rarity.UNCOMMON, mage.cards.u.UmbralJuke.class)); + cards.add(new SetCardInfo("Unwilling Ingredient", 90, Rarity.COMMON, mage.cards.u.UnwillingIngredient.class)); + cards.add(new SetCardInfo("Uvilda, Dean of Perfection", 160, Rarity.RARE, mage.cards.u.UvildaDeanOfPerfection.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Uvilda, Dean of Perfection", 332, Rarity.RARE, mage.cards.u.UvildaDeanOfPerfection.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Valentin, Dean of the Vein", 161, Rarity.RARE, mage.cards.v.ValentinDeanOfTheVein.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Valentin, Dean of the Vein", 333, Rarity.RARE, mage.cards.v.ValentinDeanOfTheVein.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vanishing Verse", 244, Rarity.RARE, mage.cards.v.VanishingVerse.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vanishing Verse", 354, Rarity.RARE, mage.cards.v.VanishingVerse.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Velomachus Lorehold", 245, Rarity.MYTHIC, mage.cards.v.VelomachusLorehold.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Velomachus Lorehold", 283, Rarity.MYTHIC, mage.cards.v.VelomachusLorehold.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Venerable Warsinger", 246, Rarity.RARE, mage.cards.v.VenerableWarsinger.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Venerable Warsinger", 355, Rarity.RARE, mage.cards.v.VenerableWarsinger.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Verdant Mastery", 146, Rarity.RARE, mage.cards.v.VerdantMastery.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Verdant Mastery", 320, Rarity.RARE, mage.cards.v.VerdantMastery.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vineglimmer Snarl", 274, Rarity.RARE, mage.cards.v.VineglimmerSnarl.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vineglimmer Snarl", 365, Rarity.RARE, mage.cards.v.VineglimmerSnarl.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vortex Runner", 60, Rarity.COMMON, mage.cards.v.VortexRunner.class)); + cards.add(new SetCardInfo("Wandering Archaic", 286, Rarity.RARE, mage.cards.w.WanderingArchaic.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Wandering Archaic", 6, Rarity.RARE, mage.cards.w.WanderingArchaic.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Waterfall Aerialist", 61, Rarity.COMMON, mage.cards.w.WaterfallAerialist.class)); + cards.add(new SetCardInfo("Witherbloom Apprentice", 247, Rarity.UNCOMMON, mage.cards.w.WitherbloomApprentice.class)); + cards.add(new SetCardInfo("Witherbloom Campus", 275, Rarity.COMMON, mage.cards.w.WitherbloomCampus.class)); + cards.add(new SetCardInfo("Witherbloom Command", 248, Rarity.RARE, mage.cards.w.WitherbloomCommand.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Witherbloom Command", 356, Rarity.RARE, mage.cards.w.WitherbloomCommand.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Witherbloom Pledgemage", 249, Rarity.COMMON, mage.cards.w.WitherbloomPledgemage.class)); + cards.add(new SetCardInfo("Wormhole Serpent", 62, Rarity.UNCOMMON, mage.cards.w.WormholeSerpent.class)); + cards.add(new SetCardInfo("Zephyr Boots", 261, Rarity.UNCOMMON, mage.cards.z.ZephyrBoots.class)); + cards.add(new SetCardInfo("Zimone, Quandrix Prodigy", 250, Rarity.UNCOMMON, mage.cards.z.ZimoneQuandrixProdigy.class)); + } + + @Override + public List tryBooster() { + List booster = super.tryBooster(); + addArchive(booster); + addLesson(booster); + return booster; + } + + private void addArchive(List booster) { + // Boosters have one card from STA, odds are 2/3 for uncommon, 4/15 for rare, 1/15 for mythic + final Rarity rarity; + int i = RandomUtil.nextInt(15); + if (i == 14) { + rarity = Rarity.MYTHIC; + } else if (i >= 10) { + rarity = Rarity.RARE; + } else { + rarity = Rarity.UNCOMMON; + } + addToBooster(booster, StrixhavenMysticalArchive.getInstance().getCardsByRarity(rarity)); + } + + private void addLesson(List booster) { + // Boosters have one Lesson card + final Rarity rarity; + int i = RandomUtil.nextInt(121); + if (i < 2) { + rarity = Rarity.MYTHIC; + } else if (i < 22) { + rarity = Rarity.RARE; + } else { + rarity = Rarity.COMMON; + } + List cards = super.getCardsByRarity(rarity); + cards.removeIf(cardInfo -> !cardInfo.getCard().hasSubtype(SubType.LESSON, null)); + addToBooster(booster, cards); + } + + @Override + public List getCardsByRarity(Rarity rarity) { + List cards = super.getCardsByRarity(rarity); + if (rarity != Rarity.UNCOMMON) { + cards.removeIf(cardInfo -> cardInfo.getCard().hasSubtype(SubType.LESSON, null)); + } + return cards; + } + + @Override + protected void generateBoosterMap() { + super.generateBoosterMap(); + CardRepository + .instance + .findCards(new CardCriteria().setCodes("STA")) + .stream() + .forEach(cardInfo -> inBoosterMap.put("STA_" + cardInfo.getCardNumber(), cardInfo)); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/stx.html +// Using Belgian collation plus other info inferred from various sources +class StrixhavenSchoolOfMagesCollator implements BoosterCollator { + + private static class StrixhavenSchoolOfMagesRun extends CardRun { + private static final StrixhavenSchoolOfMagesRun commonA = new StrixhavenSchoolOfMagesRun(true, "249", "206", "182", "226", "237", "255", "210", "209", "239", "226", "251", "185", "219", "243", "206", "164", "215", "238", "256", "184", "239", "208", "254", "237", "185", "223", "251", "206", "166", "182", "164", "238", "204", "233", "184", "210", "182", "252", "243", "254", "208", "194", "255", "243", "249", "201", "204", "194", "235", "239", "166", "251", "223", "252", "237", "208", "233", "241", "219", "255", "201", "194", "164", "252", "170", "223", "241", "215", "166", "249", "237", "238", "184", "210", "243", "209", "235", "252", "204", "210", "185", "233", "249", "256", "239", "235", "209", "170", "201", "226", "241", "215", "206", "256", "208", "254", "185", "251", "226", "170", "184", "256", "235", "233", "209", "238", "254", "219", "201", "241", "182", "164", "194", "223", "170", "204", "166", "219", "215", "255"); + private static final StrixhavenSchoolOfMagesRun commonB = new StrixhavenSchoolOfMagesRun(true, "79", "9", "111", "136", "52", "85", "12", "118", "131", "40", "90", "34", "117", "141", "60", "69", "30", "93", "140", "36", "74", "16", "97", "143", "40", "87", "11", "99", "121", "39", "75", "30", "106", "122", "38", "79", "22", "97", "131", "51", "90", "23", "112", "145", "50", "87", "18", "103", "124", "43", "68", "34", "102", "142", "49", "77", "16", "93", "140", "52", "73", "32", "116", "144", "60", "63", "22", "109", "124", "55", "69", "9", "106", "143", "39", "73", "23", "116", "142", "61", "84", "8", "99", "122", "43", "74", "32", "117", "145", "36", "75", "19", "112", "141", "61", "77", "8", "109", "144", "50", "84", "18", "118", "137", "55", "68", "12", "111", "121", "38", "63", "19", "103", "136", "51", "85", "11", "102", "137", "49"); + private static final StrixhavenSchoolOfMagesRun commonC = new StrixhavenSchoolOfMagesRun(true, "263", "268", "270", "271", "273", "275"); + private static final StrixhavenSchoolOfMagesRun uncommonA = new StrixhavenSchoolOfMagesRun(true, "257", "65", "56", "132", "92", "125", "72", "62", "104", "89", "123", "54", "10", "135", "114", "53", "105", "188", "45", "115", "47", "70", "100", "129", "88", "260", "46", "76", "257", "110", "65", "13", "139", "78", "92", "26", "72", "15", "56", "89", "134", "28", "70", "91", "132", "105", "31", "123", "88", "104", "135", "54", "115", "25", "76", "13", "62", "125", "35", "188", "65", "260", "10", "114", "129", "47", "100", "15", "257", "53", "26", "110", "139", "28", "134", "56", "35", "45", "91", "72", "10", "31", "132", "54", "89", "15", "78", "46", "123", "25", "92", "135", "104", "70", "125", "105", "260", "62", "188", "129", "114", "88", "13", "53", "26", "115", "47", "76", "28", "139", "100", "35", "91", "45", "78", "134", "31", "46", "25", "110"); + private static final StrixhavenSchoolOfMagesRun uncommonB = new StrixhavenSchoolOfMagesRun(true, "229", "218", "216", "222", "212", "198", "71", "177", "202", "138", "258", "162", "81", "175", "213", "224", "220", "176", "227", "107", "247", "186", "169", "216", "261", "197", "242", "126", "262", "198", "24", "225", "207", "229", "190", "202", "81", "178", "200", "193", "41", "162", "71", "171", "59", "212", "218", "222", "227", "138", "231", "220", "258", "175", "169", "186", "107", "193", "250", "197", "176", "171", "126", "261", "247", "227", "213", "177", "262", "207", "190", "224", "225", "24", "216", "200", "242", "71", "178", "41", "212", "59", "198", "81", "231", "220", "229", "218", "202", "169", "175", "222", "193", "197", "250", "213", "186", "126", "177", "190", "258", "138", "162", "107", "261", "224", "200", "176", "178", "24", "247", "262", "207", "171", "225", "59", "231", "242", "41", "250"); + private static final StrixhavenSchoolOfMagesRun rareA = new StrixhavenSchoolOfMagesRun(false, "6", "14", "17", "20", "27", "29", "33", "37", "42", "44", "48", "58", "64", "66", "80", "82", "86", "94", "96", "98", "101", "113", "119", "127", "130", "133", "146", "147", "150", "152", "154", "155", "157", "158", "159", "160", "161", "165", "172", "173", "174", "179", "180", "181", "199", "205", "214", "217", "221", "228", "232", "234", "244", "246", "248", "253", "259", "264", "265", "266", "267", "269", "272", "274", "6", "14", "17", "20", "27", "29", "33", "37", "42", "44", "48", "58", "64", "66", "80", "82", "86", "94", "96", "98", "101", "113", "119", "127", "130", "133", "146", "147", "150", "152", "154", "155", "157", "158", "159", "160", "161", "165", "172", "173", "174", "179", "180", "181", "199", "205", "214", "217", "221", "228", "232", "234", "244", "246", "248", "253", "259", "264", "265", "266", "267", "269", "272", "274", "21", "83", "95", "128", "148", "149", "151", "153", "156", "163", "167", "168", "189", "191", "192", "196", "203", "230", "240", "245"); + private static final StrixhavenSchoolOfMagesRun commonLesson = new StrixhavenSchoolOfMagesRun(false, "1", "2", "3", "4", "183", "187", "195", "211", "236"); + private static final StrixhavenSchoolOfMagesRun rareLesson = new StrixhavenSchoolOfMagesRun(false, "5", "7", "57", "67", "108", "120", "7", "57", "67", "108", "120"); + private static final StrixhavenSchoolOfMagesRun uncommonArchive = new StrixhavenSchoolOfMagesRun(false, "3", "4", "9", "18", "19", "20", "23", "24", "29", "30", "35", "37", "41", "44", "46", "49", "51", "57"); + private static final StrixhavenSchoolOfMagesRun rareArchive = new StrixhavenSchoolOfMagesRun(false, "5", "6", "7", "8", "10", "13", "14", "15", "16", "21", "26", "28", "31", "32", "34", "38", "39", "42", "45", "47", "48", "52", "53", "56", "58", "59", "60", "61", "62", "63"); + private static final StrixhavenSchoolOfMagesRun mythicArchive = new StrixhavenSchoolOfMagesRun(false, "1", "2", "11", "12", "17", "22", "25", "27", "33", "36", "40", "43", "50", "54", "55"); + + private StrixhavenSchoolOfMagesRun(boolean keepOrder, String... numbers) { + super(keepOrder, numbers); + } + } + + private static class StrixhavenSchoolOfMagesStructure extends BoosterStructure { + private static final StrixhavenSchoolOfMagesStructure C1 = new StrixhavenSchoolOfMagesStructure( + StrixhavenSchoolOfMagesRun.commonA, + StrixhavenSchoolOfMagesRun.commonA, + StrixhavenSchoolOfMagesRun.commonC, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB + ); + private static final StrixhavenSchoolOfMagesStructure C2 = new StrixhavenSchoolOfMagesStructure( + StrixhavenSchoolOfMagesRun.commonA, + StrixhavenSchoolOfMagesRun.commonA, + StrixhavenSchoolOfMagesRun.commonA, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB + ); + private static final StrixhavenSchoolOfMagesStructure C3 = new StrixhavenSchoolOfMagesStructure( + StrixhavenSchoolOfMagesRun.commonA, + StrixhavenSchoolOfMagesRun.commonA, + StrixhavenSchoolOfMagesRun.commonA, + StrixhavenSchoolOfMagesRun.commonC, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB + ); + private static final StrixhavenSchoolOfMagesStructure C4 = new StrixhavenSchoolOfMagesStructure( + StrixhavenSchoolOfMagesRun.commonA, + StrixhavenSchoolOfMagesRun.commonA, + StrixhavenSchoolOfMagesRun.commonA, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB + ); + private static final StrixhavenSchoolOfMagesStructure U1 = new StrixhavenSchoolOfMagesStructure( + StrixhavenSchoolOfMagesRun.uncommonA, + StrixhavenSchoolOfMagesRun.uncommonB, + StrixhavenSchoolOfMagesRun.uncommonB + ); + private static final StrixhavenSchoolOfMagesStructure U2 = new StrixhavenSchoolOfMagesStructure( + StrixhavenSchoolOfMagesRun.uncommonA, + StrixhavenSchoolOfMagesRun.uncommonA, + StrixhavenSchoolOfMagesRun.uncommonB + ); + private static final StrixhavenSchoolOfMagesStructure R1 = new StrixhavenSchoolOfMagesStructure( + StrixhavenSchoolOfMagesRun.rareA + ); + private static final StrixhavenSchoolOfMagesStructure L1 = new StrixhavenSchoolOfMagesStructure( + StrixhavenSchoolOfMagesRun.commonLesson + ); + private static final StrixhavenSchoolOfMagesStructure L2 = new StrixhavenSchoolOfMagesStructure( + StrixhavenSchoolOfMagesRun.rareLesson + ); + private static final StrixhavenSchoolOfMagesStructure A1 = new StrixhavenSchoolOfMagesStructure( + StrixhavenSchoolOfMagesRun.uncommonArchive + ); + private static final StrixhavenSchoolOfMagesStructure A2 = new StrixhavenSchoolOfMagesStructure( + StrixhavenSchoolOfMagesRun.rareArchive + ); + private static final StrixhavenSchoolOfMagesStructure A3 = new StrixhavenSchoolOfMagesStructure( + StrixhavenSchoolOfMagesRun.mythicArchive + ); + + private StrixhavenSchoolOfMagesStructure(CardRun... runs) { + super(runs); + } + } + + private final RarityConfiguration commonRuns = new RarityConfiguration( + false, + StrixhavenSchoolOfMagesStructure.C1, + StrixhavenSchoolOfMagesStructure.C2, + StrixhavenSchoolOfMagesStructure.C3, + StrixhavenSchoolOfMagesStructure.C4 + ); + private final RarityConfiguration uncommonRuns = new RarityConfiguration( + StrixhavenSchoolOfMagesStructure.U1, + StrixhavenSchoolOfMagesStructure.U2 + ); + private final RarityConfiguration rareRuns = new RarityConfiguration( + StrixhavenSchoolOfMagesStructure.R1 + ); + private final RarityConfiguration lessonRuns = new RarityConfiguration( + false, + StrixhavenSchoolOfMagesStructure.L1, StrixhavenSchoolOfMagesStructure.L1, + StrixhavenSchoolOfMagesStructure.L1, StrixhavenSchoolOfMagesStructure.L1, + StrixhavenSchoolOfMagesStructure.L1, StrixhavenSchoolOfMagesStructure.L1, + StrixhavenSchoolOfMagesStructure.L1, StrixhavenSchoolOfMagesStructure.L1, + StrixhavenSchoolOfMagesStructure.L1, StrixhavenSchoolOfMagesStructure.L1, + StrixhavenSchoolOfMagesStructure.L1, + + StrixhavenSchoolOfMagesStructure.L2, StrixhavenSchoolOfMagesStructure.L2 + ); + private final RarityConfiguration archiveRuns = new RarityConfiguration( + false, + StrixhavenSchoolOfMagesStructure.A1, StrixhavenSchoolOfMagesStructure.A1, + StrixhavenSchoolOfMagesStructure.A1, StrixhavenSchoolOfMagesStructure.A1, + StrixhavenSchoolOfMagesStructure.A1, StrixhavenSchoolOfMagesStructure.A1, + StrixhavenSchoolOfMagesStructure.A1, StrixhavenSchoolOfMagesStructure.A1, + StrixhavenSchoolOfMagesStructure.A1, StrixhavenSchoolOfMagesStructure.A1, + + StrixhavenSchoolOfMagesStructure.A2, StrixhavenSchoolOfMagesStructure.A2, + StrixhavenSchoolOfMagesStructure.A2, StrixhavenSchoolOfMagesStructure.A2, + + StrixhavenSchoolOfMagesStructure.A3 + ); + + @Override + public void shuffle() { + commonRuns.shuffle(); + uncommonRuns.shuffle(); + rareRuns.shuffle(); + lessonRuns.shuffle(); + archiveRuns.shuffle(); + } + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(lessonRuns.getNext().makeRun()); + archiveRuns.getNext().makeRun().stream().map(s -> "STA_" + s).forEach(booster::add); + return booster; + } +} diff --git a/Mage.Sets/src/mage/sets/TherosBeyondDeath.java b/Mage.Sets/src/mage/sets/TherosBeyondDeath.java index d9408a02c60b..fe0c9e0142f9 100644 --- a/Mage.Sets/src/mage/sets/TherosBeyondDeath.java +++ b/Mage.Sets/src/mage/sets/TherosBeyondDeath.java @@ -1,9 +1,16 @@ package mage.sets; import mage.cards.ExpansionSet; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; +import java.util.ArrayList; +import java.util.List; + /** * @author TheElk801 */ @@ -16,7 +23,7 @@ public static TherosBeyondDeath getInstance() { } private TherosBeyondDeath() { - super("Theros Beyond Death", "THB", ExpansionSet.buildDate(2020, 1, 24), SetType.EXPANSION); + super("Theros Beyond Death", "THB", ExpansionSet.buildDate(2020, 1, 24), SetType.EXPANSION, new TherosBeyondDeathCollator()); this.blockName = "Theros Beyond Death"; this.hasBoosters = true; this.numBoosterLands = 1; @@ -385,3 +392,158 @@ private TherosBeyondDeath() { cards.add(new SetCardInfo("Wrap in Flames", 164, Rarity.COMMON, mage.cards.w.WrapInFlames.class)); } } + +// Booster collation info from https://www.lethe.xyz/mtg/collation/thb.html +// Using USA collation for common/uncommon, rare collation inferred from other sets +class TherosBeyondDeathCollator implements BoosterCollator { + + private static class TherosBeyondDeathRun extends CardRun { + private static final TherosBeyondDeathRun commonA = new TherosBeyondDeathRun(true, "155","29","79","127","38","57","159","41","66","140","30","78","163","28","56","137","25","68","144","20","67","146","26","49","134","40","61","159","29","51","164","17","57","149","38","66","127","30","47","144","36","79","155","41","67","137","28","78","140","25","56","163","20","49","146","40","68","134","17","51","149","26","47","164","36","61"); + private static final TherosBeyondDeathRun commonB = new TherosBeyondDeathRun(true, "186","85","191","116","201","103","202","115","184","120","194","110","192","88","177","113","171","86","195","109","179","114","202","85","201","103","184","116","186","115","192","110","191","114","177","120","194","88","171","113","179","86","195","109","201","85","184","116","202","110","186","103","191","115","192","114","179","113","194","109","195","86","177","88","171","120"); + private static final TherosBeyondDeathRun commonC1 = new TherosBeyondDeathRun(true, "203","154","106","77","10","174","58","16","141","238","122","46","173","152","22","240","100","74","200","142","97","11","48","203","241","154","106","35","82","174","77","10","240","141","100","58","238","122","232","152","22","46","111","173","241","16","74","97","48","11","200","142","111","82","35"); + private static final TherosBeyondDeathRun commonC2 = new TherosBeyondDeathRun(true, "44","96","197","145","232","34","126","204","249","54","135","231","187","175","44","143","95","96","197","135","107","6","32","204","126","34","54","249","145","231","187","96","6","143","44","107","34","175","135","249","95","197","54","204","126","32","6","175","95","231","145","107","187","32","143"); + private static final TherosBeyondDeathRun uncommonA = new TherosBeyondDeathRun(true, "223","65","153","8","112","227","99","167","33","138","4","189","228","45","59","180","105","1","136","196","206","139","83","89","233","31","131","91","219","193","27","133","64","199","213","264","42","153","205","8","136","4","189","33","223","2","138","112","27","233","260","180","31","59","99","131","105","267","81","139","228","167","133","219","65","1","83","125","206","193","42","91","227","89","199","153","8","81","213","64","112","223","4","136","205","105","139","99","65","2","180","228","59","1","233","45","189","227","33","196","83","138","206","42","219","167","131","31","89","193","91","125","213","199","81","27","2","64","133","205"); + private static final TherosBeyondDeathRun uncommonB = new TherosBeyondDeathRun(true, "226","101","128","183","21","234","87","50","242","176","239","132","9","216","62","119","172","160","104","69","168","225","130","237","63","15","102","166","5","129","121","53","239","70","182","128","21","234","92","69","101","160","23","230","75","130","104","172","50","7","162","87","183","226","62","216","258","132","176","237","263","15","242","63","5","225","168","129","121","53","230","21","70","102","166","128","92","234","23","183","160","104","75","226","162","7","239","182","9","132","101","69","172","216","242","50","176","87","225","62","15","168","119","237","130","5","70","102","166","63","23","129","121","53","182","7","162","230","92","75"); + private static final TherosBeyondDeathRun rareA = new TherosBeyondDeathRun(false, "207","84","165","3","43","209","210","212","214","169","90","12","13","215","94","217","98","218","19","24","222","243","178","55","181","108","188","235","148","60","151","198","236","37","156","157","39","158","244","245","246","247","248","72","73","124","170","76","117","118","161","80","123","207","84","165","3","43","209","210","212","214","169","90","12","13","215","94","217","98","218","19","24","222","243","178","55","181","108","188","235","148","60","151","198","236","37","156","157","39","158","244","245","246","247","248","72","73","124","170","76","117","118","161","80","123","14","18","52","71","93","147","150","185","190","208","211","220","221","224","229"); + private static final TherosBeyondDeathRun rareB = new TherosBeyondDeathRun(false, "207","84","165","3","43","209","210","212","214","169","90","12","13","215","94","217","98","218","19","24","222","243","178","55","181","108","188","235","148","60","151","198","236","37","156","157","39","158","244","245","246","247","248","72","73","124","170","76","117","118","161","80","123","207","84","165","3","43","209","210","212","214","169","90","12","13","215","94","217","98","218","19","24","222","243","178","55","181","108","188","235","148","60","151","198","236","37","156","157","39","158","244","245","246","247","248","72","73","124","170","76","117","118","161","80","123","255","259","52","261","262","147","265","266","190","256","257","268","221","224","229"); + private static final TherosBeyondDeathRun land = new TherosBeyondDeathRun(false, "250","251","252","253","254"); + + private TherosBeyondDeathRun(boolean keepOrder, String... numbers) { + super(keepOrder, numbers); + } + } + + private static class TherosBeyondDeathStructure extends BoosterStructure { + private static final TherosBeyondDeathStructure C1 = new TherosBeyondDeathStructure( + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonC1, + TherosBeyondDeathRun.commonC1, + TherosBeyondDeathRun.commonC1, + TherosBeyondDeathRun.commonC1, + TherosBeyondDeathRun.commonC1, + TherosBeyondDeathRun.commonC1 + ); + private static final TherosBeyondDeathStructure C2 = new TherosBeyondDeathStructure( + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonC1, + TherosBeyondDeathRun.commonC1, + TherosBeyondDeathRun.commonC1, + TherosBeyondDeathRun.commonC1, + TherosBeyondDeathRun.commonC1 + ); + private static final TherosBeyondDeathStructure C3 = new TherosBeyondDeathStructure( + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonC2, + TherosBeyondDeathRun.commonC2, + TherosBeyondDeathRun.commonC2, + TherosBeyondDeathRun.commonC2 + ); + private static final TherosBeyondDeathStructure C4 = new TherosBeyondDeathStructure( + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonC2, + TherosBeyondDeathRun.commonC2, + TherosBeyondDeathRun.commonC2 + ); + private static final TherosBeyondDeathStructure C5 = new TherosBeyondDeathStructure( + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonC2, + TherosBeyondDeathRun.commonC2 + ); + private static final TherosBeyondDeathStructure U1 = new TherosBeyondDeathStructure( + TherosBeyondDeathRun.uncommonA, + TherosBeyondDeathRun.uncommonB, + TherosBeyondDeathRun.uncommonB + ); + private static final TherosBeyondDeathStructure U2 = new TherosBeyondDeathStructure( + TherosBeyondDeathRun.uncommonA, + TherosBeyondDeathRun.uncommonA, + TherosBeyondDeathRun.uncommonB + ); + private static final TherosBeyondDeathStructure R1 = new TherosBeyondDeathStructure( + TherosBeyondDeathRun.rareA + ); + private static final TherosBeyondDeathStructure R2 = new TherosBeyondDeathStructure( + TherosBeyondDeathRun.rareB + ); + private static final TherosBeyondDeathStructure L1 = new TherosBeyondDeathStructure( + TherosBeyondDeathRun.land + ); + + private TherosBeyondDeathStructure(CardRun... runs) { + super(runs); + } + } + + private final RarityConfiguration commonRuns = new RarityConfiguration( + false, + TherosBeyondDeathStructure.C1, + TherosBeyondDeathStructure.C2, + TherosBeyondDeathStructure.C3, + TherosBeyondDeathStructure.C4, + TherosBeyondDeathStructure.C5, + TherosBeyondDeathStructure.C1, + TherosBeyondDeathStructure.C2, + TherosBeyondDeathStructure.C3, + TherosBeyondDeathStructure.C4, + TherosBeyondDeathStructure.C5, + TherosBeyondDeathStructure.C4, + TherosBeyondDeathStructure.C5 + ); + private final RarityConfiguration uncommonRuns = new RarityConfiguration( + TherosBeyondDeathStructure.U1, + TherosBeyondDeathStructure.U2 + ); + private final RarityConfiguration rareRuns = new RarityConfiguration( + false, + TherosBeyondDeathStructure.R1, + TherosBeyondDeathStructure.R1, + TherosBeyondDeathStructure.R2 + ); + private final RarityConfiguration landRuns = new RarityConfiguration( + TherosBeyondDeathStructure.L1 + ); + + + @Override + public void shuffle() { + commonRuns.shuffle(); + uncommonRuns.shuffle(); + rareRuns.shuffle(); + landRuns.shuffle(); + } + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; + } +} diff --git a/Mage.Sets/src/mage/sets/TimeSpiralRemastered.java b/Mage.Sets/src/mage/sets/TimeSpiralRemastered.java new file mode 100644 index 000000000000..bd8b39451bf1 --- /dev/null +++ b/Mage.Sets/src/mage/sets/TimeSpiralRemastered.java @@ -0,0 +1,621 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.cards.repository.CardCriteria; +import mage.cards.repository.CardInfo; +import mage.cards.repository.CardRepository; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; +import mage.constants.Rarity; +import mage.constants.SetType; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author TheElk801 + */ +public class TimeSpiralRemastered extends ExpansionSet { + + private static final TimeSpiralRemastered instance = new TimeSpiralRemastered(); + + public static TimeSpiralRemastered getInstance() { + return instance; + } + + private final List savedSpecialBonus = new ArrayList<>(); + + private TimeSpiralRemastered() { + super("Time Spiral Remastered", "TSR", ExpansionSet.buildDate(2021, 3, 19), SetType.SUPPLEMENTAL, new TimeSpiralRemasteredCollator()); + this.hasBoosters = true; + this.hasBasicLands = true; + this.maxCardNumberInBooster = 410; + this.numBoosterCommon = 10; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.numBoosterSpecial = 1; + this.ratioBoosterMythic = 8; + + cards.add(new SetCardInfo("Abrupt Decay", 370, Rarity.SPECIAL, mage.cards.a.AbruptDecay.class)); + cards.add(new SetCardInfo("Aeon Chronicler", 51, Rarity.RARE, mage.cards.a.AeonChronicler.class)); + cards.add(new SetCardInfo("Ajani's Pridemate", 290, Rarity.SPECIAL, mage.cards.a.AjanisPridemate.class)); + cards.add(new SetCardInfo("Akroma's Memorial", 262, Rarity.MYTHIC, mage.cards.a.AkromasMemorial.class)); + cards.add(new SetCardInfo("Akroma, Angel of Fury", 150, Rarity.MYTHIC, mage.cards.a.AkromaAngelOfFury.class)); + cards.add(new SetCardInfo("Alesha, Who Smiles at Death", 338, Rarity.SPECIAL, mage.cards.a.AleshaWhoSmilesAtDeath.class)); + cards.add(new SetCardInfo("Amrou Scout", 1, Rarity.COMMON, mage.cards.a.AmrouScout.class)); + cards.add(new SetCardInfo("Amrou Seekers", 2, Rarity.COMMON, mage.cards.a.AmrouSeekers.class)); + cards.add(new SetCardInfo("Ancestral Vision", 52, Rarity.MYTHIC, mage.cards.a.AncestralVision.class)); + cards.add(new SetCardInfo("Ancient Den", 403, Rarity.SPECIAL, mage.cards.a.AncientDen.class)); + cards.add(new SetCardInfo("Ancient Grudge", 151, Rarity.COMMON, mage.cards.a.AncientGrudge.class)); + cards.add(new SetCardInfo("Ancient Stirrings", 355, Rarity.SPECIAL, mage.cards.a.AncientStirrings.class)); + cards.add(new SetCardInfo("Angel of Salvation", 3, Rarity.RARE, mage.cards.a.AngelOfSalvation.class)); + cards.add(new SetCardInfo("Angel's Grace", 4, Rarity.RARE, mage.cards.a.AngelsGrace.class)); + cards.add(new SetCardInfo("Anger of the Gods", 339, Rarity.SPECIAL, mage.cards.a.AngerOfTheGods.class)); + cards.add(new SetCardInfo("Arc Blade", 152, Rarity.UNCOMMON, mage.cards.a.ArcBlade.class)); + cards.add(new SetCardInfo("Arcades, the Strategist", 371, Rarity.SPECIAL, mage.cards.a.ArcadesTheStrategist.class)); + cards.add(new SetCardInfo("Arch of Orazca", 404, Rarity.SPECIAL, mage.cards.a.ArchOfOrazca.class)); + cards.add(new SetCardInfo("Assassinate", 101, Rarity.COMMON, mage.cards.a.Assassinate.class)); + cards.add(new SetCardInfo("Aven Mindcensor", 5, Rarity.UNCOMMON, mage.cards.a.AvenMindcensor.class)); + cards.add(new SetCardInfo("Aven Riftwatcher", 6, Rarity.COMMON, mage.cards.a.AvenRiftwatcher.class)); + cards.add(new SetCardInfo("Banishing Light", 291, Rarity.SPECIAL, mage.cards.b.BanishingLight.class)); + cards.add(new SetCardInfo("Baral, Chief of Compliance", 306, Rarity.SPECIAL, mage.cards.b.BaralChiefOfCompliance.class)); + cards.add(new SetCardInfo("Basalt Gargoyle", 153, Rarity.UNCOMMON, mage.cards.b.BasaltGargoyle.class)); + cards.add(new SetCardInfo("Battering Sliver", 154, Rarity.COMMON, mage.cards.b.BatteringSliver.class)); + cards.add(new SetCardInfo("Beast Whisperer", 356, Rarity.SPECIAL, mage.cards.b.BeastWhisperer.class)); + cards.add(new SetCardInfo("Beast Within", 357, Rarity.SPECIAL, mage.cards.b.BeastWithin.class)); + cards.add(new SetCardInfo("Become Immense", 358, Rarity.SPECIAL, mage.cards.b.BecomeImmense.class)); + cards.add(new SetCardInfo("Bedlam Reveler", 340, Rarity.SPECIAL, mage.cards.b.BedlamReveler.class)); + cards.add(new SetCardInfo("Benalish Cavalry", 7, Rarity.COMMON, mage.cards.b.BenalishCavalry.class)); + cards.add(new SetCardInfo("Benalish Commander", 8, Rarity.RARE, mage.cards.b.BenalishCommander.class)); + cards.add(new SetCardInfo("Bewilder", 53, Rarity.COMMON, mage.cards.b.Bewilder.class)); + cards.add(new SetCardInfo("Big Game Hunter", 102, Rarity.UNCOMMON, mage.cards.b.BigGameHunter.class)); + cards.add(new SetCardInfo("Blade of the Sixth Pride", 9, Rarity.COMMON, mage.cards.b.BladeOfTheSixthPride.class)); + cards.add(new SetCardInfo("Blighted Woodland", 405, Rarity.SPECIAL, mage.cards.b.BlightedWoodland.class)); + cards.add(new SetCardInfo("Blightspeaker", 103, Rarity.COMMON, mage.cards.b.Blightspeaker.class)); + cards.add(new SetCardInfo("Bloodbraid Elf", 372, Rarity.SPECIAL, mage.cards.b.BloodbraidElf.class)); + cards.add(new SetCardInfo("Bojuka Bog", 406, Rarity.SPECIAL, mage.cards.b.BojukaBog.class)); + cards.add(new SetCardInfo("Bonded Fetch", 54, Rarity.UNCOMMON, mage.cards.b.BondedFetch.class)); + cards.add(new SetCardInfo("Bonesplitter Sliver", 155, Rarity.COMMON, mage.cards.b.BonesplitterSliver.class)); + cards.add(new SetCardInfo("Boom // Bust", 156, Rarity.RARE, mage.cards.b.BoomBust.class)); + cards.add(new SetCardInfo("Bound in Silence", 10, Rarity.COMMON, mage.cards.b.BoundInSilence.class)); + cards.add(new SetCardInfo("Brine Elemental", 55, Rarity.UNCOMMON, mage.cards.b.BrineElemental.class)); + cards.add(new SetCardInfo("Brute Force", 157, Rarity.COMMON, mage.cards.b.BruteForce.class)); + cards.add(new SetCardInfo("Calciderm", 11, Rarity.UNCOMMON, mage.cards.c.Calciderm.class)); + cards.add(new SetCardInfo("Calciform Pools", 275, Rarity.UNCOMMON, mage.cards.c.CalciformPools.class)); + cards.add(new SetCardInfo("Careful Consideration", 56, Rarity.UNCOMMON, mage.cards.c.CarefulConsideration.class)); + cards.add(new SetCardInfo("Castle Raptors", 12, Rarity.COMMON, mage.cards.c.CastleRaptors.class)); + cards.add(new SetCardInfo("Cautery Sliver", 248, Rarity.UNCOMMON, mage.cards.c.CauterySliver.class)); + cards.add(new SetCardInfo("Celestial Crusader", 13, Rarity.UNCOMMON, mage.cards.c.CelestialCrusader.class)); + cards.add(new SetCardInfo("Chalice of the Void", 390, Rarity.SPECIAL, mage.cards.c.ChaliceOfTheVoid.class)); + cards.add(new SetCardInfo("Char-Rumbler", 158, Rarity.UNCOMMON, mage.cards.c.CharRumbler.class)); + cards.add(new SetCardInfo("Children of Korlis", 14, Rarity.COMMON, mage.cards.c.ChildrenOfKorlis.class)); + cards.add(new SetCardInfo("Chromatic Star", 263, Rarity.COMMON, mage.cards.c.ChromaticStar.class)); + cards.add(new SetCardInfo("Citanul Woodreaders", 199, Rarity.COMMON, mage.cards.c.CitanulWoodreaders.class)); + cards.add(new SetCardInfo("Clockwork Hydra", 264, Rarity.UNCOMMON, mage.cards.c.ClockworkHydra.class)); + cards.add(new SetCardInfo("Cloud Key", 265, Rarity.RARE, mage.cards.c.CloudKey.class)); + cards.add(new SetCardInfo("Cloudseeder", 57, Rarity.UNCOMMON, mage.cards.c.Cloudseeder.class)); + cards.add(new SetCardInfo("Cloudshredder Sliver", 373, Rarity.SPECIAL, mage.cards.c.CloudshredderSliver.class)); + cards.add(new SetCardInfo("Coal Stoker", 159, Rarity.COMMON, mage.cards.c.CoalStoker.class)); + cards.add(new SetCardInfo("Coalition Relic", 266, Rarity.RARE, mage.cards.c.CoalitionRelic.class)); + cards.add(new SetCardInfo("Conflagrate", 160, Rarity.UNCOMMON, mage.cards.c.Conflagrate.class)); + cards.add(new SetCardInfo("Consuming Aberration", 374, Rarity.SPECIAL, mage.cards.c.ConsumingAberration.class)); + cards.add(new SetCardInfo("Contagion Clasp", 391, Rarity.SPECIAL, mage.cards.c.ContagionClasp.class)); + cards.add(new SetCardInfo("Containment Priest", 292, Rarity.SPECIAL, mage.cards.c.ContainmentPriest.class)); + cards.add(new SetCardInfo("Coral Trickster", 58, Rarity.COMMON, mage.cards.c.CoralTrickster.class)); + cards.add(new SetCardInfo("Corpulent Corpse", 104, Rarity.COMMON, mage.cards.c.CorpulentCorpse.class)); + cards.add(new SetCardInfo("Courser of Kruphix", 359, Rarity.SPECIAL, mage.cards.c.CourserOfKruphix.class)); + cards.add(new SetCardInfo("Cranial Plating", 392, Rarity.SPECIAL, mage.cards.c.CranialPlating.class)); + cards.add(new SetCardInfo("Crookclaw Transmuter", 59, Rarity.COMMON, mage.cards.c.CrookclawTransmuter.class)); + cards.add(new SetCardInfo("Crovax, Ascendant Hero", 15, Rarity.MYTHIC, mage.cards.c.CrovaxAscendantHero.class)); + cards.add(new SetCardInfo("Cryptic Annelid", 60, Rarity.UNCOMMON, mage.cards.c.CrypticAnnelid.class)); + cards.add(new SetCardInfo("Crystal Shard", 393, Rarity.SPECIAL, mage.cards.c.CrystalShard.class)); + cards.add(new SetCardInfo("Cutthroat il-Dal", 105, Rarity.COMMON, mage.cards.c.CutthroatIlDal.class)); + cards.add(new SetCardInfo("Damnation", 106, Rarity.MYTHIC, mage.cards.d.Damnation.class)); + cards.add(new SetCardInfo("Dark Withering", 107, Rarity.COMMON, mage.cards.d.DarkWithering.class)); + cards.add(new SetCardInfo("Darkheart Sliver", 249, Rarity.UNCOMMON, mage.cards.d.DarkheartSliver.class)); + cards.add(new SetCardInfo("Dead // Gone", 161, Rarity.COMMON, mage.cards.d.DeadGone.class)); + cards.add(new SetCardInfo("Deadly Grub", 108, Rarity.COMMON, mage.cards.d.DeadlyGrub.class)); + cards.add(new SetCardInfo("Deathspore Thallid", 109, Rarity.COMMON, mage.cards.d.DeathsporeThallid.class)); + cards.add(new SetCardInfo("Deepcavern Imp", 110, Rarity.COMMON, mage.cards.d.DeepcavernImp.class)); + cards.add(new SetCardInfo("Delay", 61, Rarity.UNCOMMON, mage.cards.d.Delay.class)); + cards.add(new SetCardInfo("Disdainful Stroke", 307, Rarity.SPECIAL, mage.cards.d.DisdainfulStroke.class)); + cards.add(new SetCardInfo("Dismember", 322, Rarity.SPECIAL, mage.cards.d.Dismember.class)); + cards.add(new SetCardInfo("Dormant Sliver", 250, Rarity.UNCOMMON, mage.cards.d.DormantSliver.class)); + cards.add(new SetCardInfo("Dovin's Veto", 375, Rarity.SPECIAL, mage.cards.d.DovinsVeto.class)); + cards.add(new SetCardInfo("Draining Whelk", 62, Rarity.RARE, mage.cards.d.DrainingWhelk.class)); + cards.add(new SetCardInfo("Dralnu, Lich Lord", 251, Rarity.RARE, mage.cards.d.DralnuLichLord.class)); + cards.add(new SetCardInfo("Dread Return", 111, Rarity.UNCOMMON, mage.cards.d.DreadReturn.class)); + cards.add(new SetCardInfo("Dreadhorde Arcanist", 341, Rarity.SPECIAL, mage.cards.d.DreadhordeArcanist.class)); + cards.add(new SetCardInfo("Dreadship Reef", 276, Rarity.UNCOMMON, mage.cards.d.DreadshipReef.class)); + cards.add(new SetCardInfo("Dream Stalker", 63, Rarity.COMMON, mage.cards.d.DreamStalker.class)); + cards.add(new SetCardInfo("Dreamscape Artist", 64, Rarity.COMMON, mage.cards.d.DreamscapeArtist.class)); + cards.add(new SetCardInfo("Drifter il-Dal", 65, Rarity.COMMON, mage.cards.d.DrifterIlDal.class)); + cards.add(new SetCardInfo("Dryad Arbor", 277, Rarity.RARE, mage.cards.d.DryadArbor.class)); + cards.add(new SetCardInfo("Dunerider Outlaw", 112, Rarity.UNCOMMON, mage.cards.d.DuneriderOutlaw.class)); + cards.add(new SetCardInfo("Durkwood Baloth", 200, Rarity.COMMON, mage.cards.d.DurkwoodBaloth.class)); + cards.add(new SetCardInfo("Duskrider Peregrine", 16, Rarity.UNCOMMON, mage.cards.d.DuskriderPeregrine.class)); + cards.add(new SetCardInfo("Edge of Autumn", 201, Rarity.COMMON, mage.cards.e.EdgeOfAutumn.class)); + cards.add(new SetCardInfo("Elvish Mystic", 360, Rarity.SPECIAL, mage.cards.e.ElvishMystic.class)); + cards.add(new SetCardInfo("Empty the Warrens", 162, Rarity.COMMON, mage.cards.e.EmptyTheWarrens.class)); + cards.add(new SetCardInfo("Enslave", 113, Rarity.UNCOMMON, mage.cards.e.Enslave.class)); + cards.add(new SetCardInfo("Epic Experiment", 376, Rarity.SPECIAL, mage.cards.e.EpicExperiment.class)); + cards.add(new SetCardInfo("Errant Doomsayers", 17, Rarity.COMMON, mage.cards.e.ErrantDoomsayers.class)); + cards.add(new SetCardInfo("Errant Ephemeron", 66, Rarity.COMMON, mage.cards.e.ErrantEphemeron.class)); + cards.add(new SetCardInfo("Erratic Mutation", 67, Rarity.COMMON, mage.cards.e.ErraticMutation.class)); + cards.add(new SetCardInfo("Etali, Primal Storm", 342, Rarity.SPECIAL, mage.cards.e.EtaliPrimalStorm.class)); + cards.add(new SetCardInfo("Eternal Witness", 361, Rarity.SPECIAL, mage.cards.e.EternalWitness.class)); + cards.add(new SetCardInfo("Ethereal Armor", 293, Rarity.SPECIAL, mage.cards.e.EtherealArmor.class)); + cards.add(new SetCardInfo("Everflowing Chalice", 394, Rarity.SPECIAL, mage.cards.e.EverflowingChalice.class)); + cards.add(new SetCardInfo("Evolution Charm", 202, Rarity.COMMON, mage.cards.e.EvolutionCharm.class)); + cards.add(new SetCardInfo("Evolutionary Leap", 362, Rarity.SPECIAL, mage.cards.e.EvolutionaryLeap.class)); + cards.add(new SetCardInfo("Exquisite Firecraft", 343, Rarity.SPECIAL, mage.cards.e.ExquisiteFirecraft.class)); + cards.add(new SetCardInfo("Extirpate", 114, Rarity.RARE, mage.cards.e.Extirpate.class)); + cards.add(new SetCardInfo("Faceless Devourer", 115, Rarity.UNCOMMON, mage.cards.f.FacelessDevourer.class)); + cards.add(new SetCardInfo("Farseek", 363, Rarity.SPECIAL, mage.cards.f.Farseek.class)); + cards.add(new SetCardInfo("Fathom Seer", 68, Rarity.COMMON, mage.cards.f.FathomSeer.class)); + cards.add(new SetCardInfo("Fblthp, the Lost", 308, Rarity.SPECIAL, mage.cards.f.FblthpTheLost.class)); + cards.add(new SetCardInfo("Feather, the Redeemed", 377, Rarity.SPECIAL, mage.cards.f.FeatherTheRedeemed.class)); + cards.add(new SetCardInfo("Feebleness", 116, Rarity.COMMON, mage.cards.f.Feebleness.class)); + cards.add(new SetCardInfo("Feldon of the Third Path", 344, Rarity.SPECIAL, mage.cards.f.FeldonOfTheThirdPath.class)); + cards.add(new SetCardInfo("Field of Ruin", 407, Rarity.SPECIAL, mage.cards.f.FieldOfRuin.class)); + cards.add(new SetCardInfo("Firemaw Kavu", 163, Rarity.UNCOMMON, mage.cards.f.FiremawKavu.class)); + cards.add(new SetCardInfo("Firewake Sliver", 252, Rarity.UNCOMMON, mage.cards.f.FirewakeSliver.class)); + cards.add(new SetCardInfo("Flagstones of Trokair", 278, Rarity.RARE, mage.cards.f.FlagstonesOfTrokair.class)); + cards.add(new SetCardInfo("Flickerwisp", 294, Rarity.SPECIAL, mage.cards.f.Flickerwisp.class)); + cards.add(new SetCardInfo("Foresee", 69, Rarity.COMMON, mage.cards.f.Foresee.class)); + cards.add(new SetCardInfo("Fortify", 18, Rarity.COMMON, mage.cards.f.Fortify.class)); + cards.add(new SetCardInfo("Fungal Reaches", 279, Rarity.UNCOMMON, mage.cards.f.FungalReaches.class)); + cards.add(new SetCardInfo("Fungus Sliver", 203, Rarity.RARE, mage.cards.f.FungusSliver.class)); + cards.add(new SetCardInfo("Fury Sliver", 164, Rarity.UNCOMMON, mage.cards.f.FurySliver.class)); + cards.add(new SetCardInfo("Gaea's Anthem", 204, Rarity.UNCOMMON, mage.cards.g.GaeasAnthem.class)); + cards.add(new SetCardInfo("Gathan Raiders", 165, Rarity.COMMON, mage.cards.g.GathanRaiders.class)); + cards.add(new SetCardInfo("Gauntlet of Power", 267, Rarity.MYTHIC, mage.cards.g.GauntletOfPower.class)); + cards.add(new SetCardInfo("Gemhide Sliver", 205, Rarity.COMMON, mage.cards.g.GemhideSliver.class)); + cards.add(new SetCardInfo("Gemstone Caverns", 280, Rarity.MYTHIC, mage.cards.g.GemstoneCaverns.class)); + cards.add(new SetCardInfo("Giant Dustwasp", 206, Rarity.COMMON, mage.cards.g.GiantDustwasp.class)); + cards.add(new SetCardInfo("Glittering Wish", 253, Rarity.RARE, mage.cards.g.GlitteringWish.class)); + cards.add(new SetCardInfo("Goblin Engineer", 345, Rarity.SPECIAL, mage.cards.g.GoblinEngineer.class)); + cards.add(new SetCardInfo("Gorgon Recluse", 117, Rarity.COMMON, mage.cards.g.GorgonRecluse.class)); + cards.add(new SetCardInfo("Gossamer Phantasm", 70, Rarity.COMMON, mage.cards.g.GossamerPhantasm.class)); + cards.add(new SetCardInfo("Grapeshot", 166, Rarity.COMMON, mage.cards.g.Grapeshot.class)); + cards.add(new SetCardInfo("Grave Scrabbler", 118, Rarity.COMMON, mage.cards.g.GraveScrabbler.class)); + cards.add(new SetCardInfo("Gray Merchant of Asphodel", 323, Rarity.SPECIAL, mage.cards.g.GrayMerchantOfAsphodel.class)); + cards.add(new SetCardInfo("Greater Gargadon", 167, Rarity.RARE, mage.cards.g.GreaterGargadon.class)); + cards.add(new SetCardInfo("Greenseeker", 207, Rarity.COMMON, mage.cards.g.Greenseeker.class)); + cards.add(new SetCardInfo("Grenzo, Dungeon Warden", 378, Rarity.SPECIAL, mage.cards.g.GrenzoDungeonWarden.class)); + cards.add(new SetCardInfo("Griffin Guide", 19, Rarity.UNCOMMON, mage.cards.g.GriffinGuide.class)); + cards.add(new SetCardInfo("Grinning Ignus", 168, Rarity.COMMON, mage.cards.g.GrinningIgnus.class)); + cards.add(new SetCardInfo("Gurmag Angler", 324, Rarity.SPECIAL, mage.cards.g.GurmagAngler.class)); + cards.add(new SetCardInfo("Harmonic Sliver", 254, Rarity.UNCOMMON, mage.cards.h.HarmonicSliver.class)); + cards.add(new SetCardInfo("Harmonize", 208, Rarity.UNCOMMON, mage.cards.h.Harmonize.class)); + cards.add(new SetCardInfo("Harvester of Souls", 325, Rarity.SPECIAL, mage.cards.h.HarvesterOfSouls.class)); + cards.add(new SetCardInfo("Haze of Rage", 169, Rarity.UNCOMMON, mage.cards.h.HazeOfRage.class)); + cards.add(new SetCardInfo("Heartwood Storyteller", 209, Rarity.RARE, mage.cards.h.HeartwoodStoryteller.class)); + cards.add(new SetCardInfo("Hedron Archive", 395, Rarity.SPECIAL, mage.cards.h.HedronArchive.class)); + cards.add(new SetCardInfo("Henchfiend of Ukor", 170, Rarity.UNCOMMON, mage.cards.h.HenchfiendOfUkor.class)); + cards.add(new SetCardInfo("Hivestone", 268, Rarity.RARE, mage.cards.h.Hivestone.class)); + cards.add(new SetCardInfo("Hollow One", 396, Rarity.SPECIAL, mage.cards.h.HollowOne.class)); + cards.add(new SetCardInfo("Homing Sliver", 171, Rarity.COMMON, mage.cards.h.HomingSliver.class)); + cards.add(new SetCardInfo("Hypergenesis", 210, Rarity.MYTHIC, mage.cards.h.Hypergenesis.class)); + cards.add(new SetCardInfo("Ichor Slick", 119, Rarity.COMMON, mage.cards.i.IchorSlick.class)); + cards.add(new SetCardInfo("Imperiosaur", 211, Rarity.UNCOMMON, mage.cards.i.Imperiosaur.class)); + cards.add(new SetCardInfo("Infiltrator il-Kor", 71, Rarity.COMMON, mage.cards.i.InfiltratorIlKor.class)); + cards.add(new SetCardInfo("Intangible Virtue", 295, Rarity.SPECIAL, mage.cards.i.IntangibleVirtue.class)); + cards.add(new SetCardInfo("Ith, High Arcanist", 255, Rarity.RARE, mage.cards.i.IthHighArcanist.class)); + cards.add(new SetCardInfo("Ivory Giant", 20, Rarity.COMMON, mage.cards.i.IvoryGiant.class)); + cards.add(new SetCardInfo("Jaya Ballard, Task Mage", 172, Rarity.RARE, mage.cards.j.JayaBallardTaskMage.class)); + cards.add(new SetCardInfo("Jhoira of the Ghitu", 256, Rarity.RARE, mage.cards.j.JhoiraOfTheGhitu.class)); + cards.add(new SetCardInfo("Jhoira's Timebug", 269, Rarity.COMMON, mage.cards.j.JhoirasTimebug.class)); + cards.add(new SetCardInfo("Jodah's Avenger", 72, Rarity.UNCOMMON, mage.cards.j.JodahsAvenger.class)); + cards.add(new SetCardInfo("Judge Unworthy", 21, Rarity.COMMON, mage.cards.j.JudgeUnworthy.class)); + cards.add(new SetCardInfo("Kaervek the Merciless", 257, Rarity.RARE, mage.cards.k.KaervekTheMerciless.class)); + cards.add(new SetCardInfo("Kavu Primarch", 212, Rarity.COMMON, mage.cards.k.KavuPrimarch.class)); + cards.add(new SetCardInfo("Keen Sense", 213, Rarity.UNCOMMON, mage.cards.k.KeenSense.class)); + cards.add(new SetCardInfo("Keldon Halberdier", 173, Rarity.COMMON, mage.cards.k.KeldonHalberdier.class)); + cards.add(new SetCardInfo("Kher Keep", 281, Rarity.RARE, mage.cards.k.KherKeep.class)); + cards.add(new SetCardInfo("Kiki-Jiki, Mirror Breaker", 346, Rarity.SPECIAL, mage.cards.k.KikiJikiMirrorBreaker.class)); + cards.add(new SetCardInfo("Knight of Sursi", 22, Rarity.COMMON, mage.cards.k.KnightOfSursi.class)); + cards.add(new SetCardInfo("Knight of the Holy Nimbus", 23, Rarity.UNCOMMON, mage.cards.k.KnightOfTheHolyNimbus.class)); + cards.add(new SetCardInfo("Knight of the Reliquary", 379, Rarity.SPECIAL, mage.cards.k.KnightOfTheReliquary.class)); + cards.add(new SetCardInfo("Kor Dirge", 120, Rarity.UNCOMMON, mage.cards.k.KorDirge.class)); + cards.add(new SetCardInfo("Krosan Grip", 214, Rarity.UNCOMMON, mage.cards.k.KrosanGrip.class)); + cards.add(new SetCardInfo("Laboratory Maniac", 309, Rarity.SPECIAL, mage.cards.l.LaboratoryManiac.class)); + cards.add(new SetCardInfo("Lava Spike", 347, Rarity.SPECIAL, mage.cards.l.LavaSpike.class)); + cards.add(new SetCardInfo("Lavinia, Azorius Renegade", 380, Rarity.SPECIAL, mage.cards.l.LaviniaAzoriusRenegade.class)); + cards.add(new SetCardInfo("Leveler", 397, Rarity.SPECIAL, mage.cards.l.Leveler.class)); + cards.add(new SetCardInfo("Leyline of the Void", 326, Rarity.SPECIAL, mage.cards.l.LeylineOfTheVoid.class)); + cards.add(new SetCardInfo("Life and Limb", 215, Rarity.RARE, mage.cards.l.LifeAndLimb.class)); + cards.add(new SetCardInfo("Lightning Axe", 174, Rarity.UNCOMMON, mage.cards.l.LightningAxe.class)); + cards.add(new SetCardInfo("Liliana's Triumph", 327, Rarity.SPECIAL, mage.cards.l.LilianasTriumph.class)); + cards.add(new SetCardInfo("Lingering Souls", 296, Rarity.SPECIAL, mage.cards.l.LingeringSouls.class)); + cards.add(new SetCardInfo("Living End", 121, Rarity.MYTHIC, mage.cards.l.LivingEnd.class)); + cards.add(new SetCardInfo("Llanowar Mentor", 216, Rarity.UNCOMMON, mage.cards.l.LlanowarMentor.class)); + cards.add(new SetCardInfo("Logic Knot", 73, Rarity.COMMON, mage.cards.l.LogicKnot.class)); + cards.add(new SetCardInfo("Looter il-Kor", 74, Rarity.COMMON, mage.cards.l.LooterIlKor.class)); + cards.add(new SetCardInfo("Lost Auramancers", 24, Rarity.UNCOMMON, mage.cards.l.LostAuramancers.class)); + cards.add(new SetCardInfo("Lotus Bloom", 270, Rarity.RARE, mage.cards.l.LotusBloom.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lotus Bloom", 411, Rarity.RARE, mage.cards.l.LotusBloom.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lymph Sliver", 25, Rarity.COMMON, mage.cards.l.LymphSliver.class)); + cards.add(new SetCardInfo("Magus of the Future", 75, Rarity.RARE, mage.cards.m.MagusOfTheFuture.class)); + cards.add(new SetCardInfo("Magus of the Moon", 175, Rarity.RARE, mage.cards.m.MagusOfTheMoon.class)); + cards.add(new SetCardInfo("Mana Tithe", 26, Rarity.COMMON, mage.cards.m.ManaTithe.class)); + cards.add(new SetCardInfo("Mangara of Corondor", 27, Rarity.RARE, mage.cards.m.MangaraOfCorondor.class)); + cards.add(new SetCardInfo("Manifold Key", 398, Rarity.SPECIAL, mage.cards.m.ManifoldKey.class)); + cards.add(new SetCardInfo("Mass of Ghouls", 122, Rarity.COMMON, mage.cards.m.MassOfGhouls.class)); + cards.add(new SetCardInfo("Master of the Pearl Trident", 310, Rarity.SPECIAL, mage.cards.m.MasterOfThePearlTrident.class)); + cards.add(new SetCardInfo("Might Sliver", 218, Rarity.UNCOMMON, mage.cards.m.MightSliver.class)); + cards.add(new SetCardInfo("Might of Old Krosa", 217, Rarity.UNCOMMON, mage.cards.m.MightOfOldKrosa.class)); + cards.add(new SetCardInfo("Mindstab", 123, Rarity.COMMON, mage.cards.m.Mindstab.class)); + cards.add(new SetCardInfo("Minions' Murmurs", 124, Rarity.UNCOMMON, mage.cards.m.MinionsMurmurs.class)); + cards.add(new SetCardInfo("Mire Boa", 219, Rarity.UNCOMMON, mage.cards.m.MireBoa.class)); + cards.add(new SetCardInfo("Mirri the Cursed", 125, Rarity.RARE, mage.cards.m.MirriTheCursed.class)); + cards.add(new SetCardInfo("Mirror Entity", 297, Rarity.SPECIAL, mage.cards.m.MirrorEntity.class)); + cards.add(new SetCardInfo("Mogg War Marshal", 176, Rarity.COMMON, mage.cards.m.MoggWarMarshal.class)); + cards.add(new SetCardInfo("Molten Rain", 348, Rarity.SPECIAL, mage.cards.m.MoltenRain.class)); + cards.add(new SetCardInfo("Molten Slagheap", 282, Rarity.UNCOMMON, mage.cards.m.MoltenSlagheap.class)); + cards.add(new SetCardInfo("Momentary Blink", 28, Rarity.COMMON, mage.cards.m.MomentaryBlink.class)); + cards.add(new SetCardInfo("Monastery Swiftspear", 349, Rarity.SPECIAL, mage.cards.m.MonasterySwiftspear.class)); + cards.add(new SetCardInfo("Mortify", 381, Rarity.SPECIAL, mage.cards.m.Mortify.class)); + cards.add(new SetCardInfo("Muck Drubb", 126, Rarity.UNCOMMON, mage.cards.m.MuckDrubb.class)); + cards.add(new SetCardInfo("Mulldrifter", 311, Rarity.SPECIAL, mage.cards.m.Mulldrifter.class)); + cards.add(new SetCardInfo("Muraganda Petroglyphs", 220, Rarity.RARE, mage.cards.m.MuragandaPetroglyphs.class)); + cards.add(new SetCardInfo("Mycologist", 29, Rarity.UNCOMMON, mage.cards.m.Mycologist.class)); + cards.add(new SetCardInfo("Mystic Confluence", 312, Rarity.SPECIAL, mage.cards.m.MysticConfluence.class)); + cards.add(new SetCardInfo("Mystic Sanctuary", 408, Rarity.SPECIAL, mage.cards.m.MysticSanctuary.class)); + cards.add(new SetCardInfo("Mystical Teachings", 76, Rarity.UNCOMMON, mage.cards.m.MysticalTeachings.class)); + cards.add(new SetCardInfo("Nantuko Shaman", 221, Rarity.COMMON, mage.cards.n.NantukoShaman.class)); + cards.add(new SetCardInfo("Nature's Claim", 364, Rarity.SPECIAL, mage.cards.n.NaturesClaim.class)); + cards.add(new SetCardInfo("Necrotic Sliver", 258, Rarity.UNCOMMON, mage.cards.n.NecroticSliver.class)); + cards.add(new SetCardInfo("Needlepeak Spider", 177, Rarity.COMMON, mage.cards.n.NeedlepeakSpider.class)); + cards.add(new SetCardInfo("Nether Traitor", 127, Rarity.RARE, mage.cards.n.NetherTraitor.class)); + cards.add(new SetCardInfo("Nightshade Assassin", 128, Rarity.UNCOMMON, mage.cards.n.NightshadeAssassin.class)); + cards.add(new SetCardInfo("Ninja of the Deep Hours", 313, Rarity.SPECIAL, mage.cards.n.NinjaOfTheDeepHours.class)); + cards.add(new SetCardInfo("Orcish Cannonade", 178, Rarity.COMMON, mage.cards.o.OrcishCannonade.class)); + cards.add(new SetCardInfo("Outrider en-Kor", 30, Rarity.UNCOMMON, mage.cards.o.OutriderEnKor.class)); + cards.add(new SetCardInfo("Pact of Negation", 77, Rarity.RARE, mage.cards.p.PactOfNegation.class)); + cards.add(new SetCardInfo("Pact of the Titan", 179, Rarity.RARE, mage.cards.p.PactOfTheTitan.class)); + cards.add(new SetCardInfo("Palace Jailer", 298, Rarity.SPECIAL, mage.cards.p.PalaceJailer.class)); + cards.add(new SetCardInfo("Pallid Mycoderm", 31, Rarity.COMMON, mage.cards.p.PallidMycoderm.class)); + cards.add(new SetCardInfo("Panharmonicon", 399, Rarity.SPECIAL, mage.cards.p.Panharmonicon.class)); + cards.add(new SetCardInfo("Paradise Plume", 271, Rarity.UNCOMMON, mage.cards.p.ParadisePlume.class)); + cards.add(new SetCardInfo("Paradoxical Outcome", 314, Rarity.SPECIAL, mage.cards.p.ParadoxicalOutcome.class)); + cards.add(new SetCardInfo("Past in Flames", 350, Rarity.SPECIAL, mage.cards.p.PastInFlames.class)); + cards.add(new SetCardInfo("Path to Exile", 299, Rarity.SPECIAL, mage.cards.p.PathToExile.class)); + cards.add(new SetCardInfo("Pendelhaven Elder", 222, Rarity.UNCOMMON, mage.cards.p.PendelhavenElder.class)); + cards.add(new SetCardInfo("Penumbra Spider", 223, Rarity.COMMON, mage.cards.p.PenumbraSpider.class)); + cards.add(new SetCardInfo("Phantom Wurm", 224, Rarity.UNCOMMON, mage.cards.p.PhantomWurm.class)); + cards.add(new SetCardInfo("Phthisis", 129, Rarity.UNCOMMON, mage.cards.p.Phthisis.class)); + cards.add(new SetCardInfo("Piracy Charm", 78, Rarity.COMMON, mage.cards.p.PiracyCharm.class)); + cards.add(new SetCardInfo("Pit Keeper", 130, Rarity.COMMON, mage.cards.p.PitKeeper.class)); + cards.add(new SetCardInfo("Ponder", 315, Rarity.SPECIAL, mage.cards.p.Ponder.class)); + cards.add(new SetCardInfo("Pongify", 79, Rarity.UNCOMMON, mage.cards.p.Pongify.class)); + cards.add(new SetCardInfo("Porphyry Nodes", 32, Rarity.RARE, mage.cards.p.PorphyryNodes.class)); + cards.add(new SetCardInfo("Poultice Sliver", 33, Rarity.UNCOMMON, mage.cards.p.PoulticeSliver.class)); + cards.add(new SetCardInfo("Premature Burial", 131, Rarity.UNCOMMON, mage.cards.p.PrematureBurial.class)); + cards.add(new SetCardInfo("Primal Forcemage", 225, Rarity.UNCOMMON, mage.cards.p.PrimalForcemage.class)); + cards.add(new SetCardInfo("Primal Plasma", 80, Rarity.COMMON, mage.cards.p.PrimalPlasma.class)); + cards.add(new SetCardInfo("Primeval Titan", 365, Rarity.SPECIAL, mage.cards.p.PrimevalTitan.class)); + cards.add(new SetCardInfo("Prismatic Lens", 272, Rarity.COMMON, mage.cards.p.PrismaticLens.class)); + cards.add(new SetCardInfo("Prized Amalgam", 382, Rarity.SPECIAL, mage.cards.p.PrizedAmalgam.class)); + cards.add(new SetCardInfo("Prodigal Pyromancer", 180, Rarity.UNCOMMON, mage.cards.p.ProdigalPyromancer.class)); + cards.add(new SetCardInfo("Psychotic Episode", 132, Rarity.COMMON, mage.cards.p.PsychoticEpisode.class)); + cards.add(new SetCardInfo("Pulmonic Sliver", 34, Rarity.RARE, mage.cards.p.PulmonicSliver.class)); + cards.add(new SetCardInfo("Qasali Pridemage", 383, Rarity.SPECIAL, mage.cards.q.QasaliPridemage.class)); + cards.add(new SetCardInfo("Radha, Heir to Keld", 259, Rarity.RARE, mage.cards.r.RadhaHeirToKeld.class)); + cards.add(new SetCardInfo("Rakdos Charm", 384, Rarity.SPECIAL, mage.cards.r.RakdosCharm.class)); + cards.add(new SetCardInfo("Ramunap Ruins", 409, Rarity.SPECIAL, mage.cards.r.RamunapRuins.class)); + cards.add(new SetCardInfo("Rathi Trapper", 133, Rarity.COMMON, mage.cards.r.RathiTrapper.class)); + cards.add(new SetCardInfo("Read the Bones", 328, Rarity.SPECIAL, mage.cards.r.ReadTheBones.class)); + cards.add(new SetCardInfo("Reality Acid", 81, Rarity.COMMON, mage.cards.r.RealityAcid.class)); + cards.add(new SetCardInfo("Rebuff the Wicked", 35, Rarity.UNCOMMON, mage.cards.r.RebuffTheWicked.class)); + cards.add(new SetCardInfo("Reckless Wurm", 181, Rarity.COMMON, mage.cards.r.RecklessWurm.class)); + cards.add(new SetCardInfo("Reclamation Sage", 366, Rarity.SPECIAL, mage.cards.r.ReclamationSage.class)); + cards.add(new SetCardInfo("Reflex Sliver", 226, Rarity.COMMON, mage.cards.r.ReflexSliver.class)); + cards.add(new SetCardInfo("Reiterate", 182, Rarity.RARE, mage.cards.r.Reiterate.class)); + cards.add(new SetCardInfo("Relentless Rats", 329, Rarity.SPECIAL, mage.cards.r.RelentlessRats.class)); + cards.add(new SetCardInfo("Remand", 316, Rarity.SPECIAL, mage.cards.r.Remand.class)); + cards.add(new SetCardInfo("Repeal", 317, Rarity.SPECIAL, mage.cards.r.Repeal.class)); + cards.add(new SetCardInfo("Restoration Angel", 300, Rarity.SPECIAL, mage.cards.r.RestorationAngel.class)); + cards.add(new SetCardInfo("Restore Balance", 36, Rarity.MYTHIC, mage.cards.r.RestoreBalance.class)); + cards.add(new SetCardInfo("Return to Dust", 37, Rarity.UNCOMMON, mage.cards.r.ReturnToDust.class)); + cards.add(new SetCardInfo("Riddle of Lightning", 183, Rarity.COMMON, mage.cards.r.RiddleOfLightning.class)); + cards.add(new SetCardInfo("Ridged Kusite", 134, Rarity.COMMON, mage.cards.r.RidgedKusite.class)); + cards.add(new SetCardInfo("Rift Bolt", 184, Rarity.COMMON, mage.cards.r.RiftBolt.class)); + cards.add(new SetCardInfo("Rift Elemental", 185, Rarity.COMMON, mage.cards.r.RiftElemental.class)); + cards.add(new SetCardInfo("Riftmarked Knight", 38, Rarity.UNCOMMON, mage.cards.r.RiftmarkedKnight.class)); + cards.add(new SetCardInfo("Riftwing Cloudskate", 82, Rarity.UNCOMMON, mage.cards.r.RiftwingCloudskate.class)); + cards.add(new SetCardInfo("Riptide Pilferer", 83, Rarity.UNCOMMON, mage.cards.r.RiptidePilferer.class)); + cards.add(new SetCardInfo("Rough // Tumble", 186, Rarity.UNCOMMON, mage.cards.r.RoughTumble.class)); + cards.add(new SetCardInfo("Saffi Eriksdotter", 260, Rarity.RARE, mage.cards.s.SaffiEriksdotter.class)); + cards.add(new SetCardInfo("Saltblast", 39, Rarity.UNCOMMON, mage.cards.s.Saltblast.class)); + cards.add(new SetCardInfo("Saltcrusted Steppe", 283, Rarity.UNCOMMON, mage.cards.s.SaltcrustedSteppe.class)); + cards.add(new SetCardInfo("Saltfield Recluse", 40, Rarity.UNCOMMON, mage.cards.s.SaltfieldRecluse.class)); + cards.add(new SetCardInfo("Sangrophage", 135, Rarity.COMMON, mage.cards.s.Sangrophage.class)); + cards.add(new SetCardInfo("Sanguine Bond", 330, Rarity.SPECIAL, mage.cards.s.SanguineBond.class)); + cards.add(new SetCardInfo("Sarcomite Myr", 84, Rarity.COMMON, mage.cards.s.SarcomiteMyr.class)); + cards.add(new SetCardInfo("Scryb Ranger", 227, Rarity.UNCOMMON, mage.cards.s.ScrybRanger.class)); + cards.add(new SetCardInfo("Seal of Primordium", 228, Rarity.COMMON, mage.cards.s.SealOfPrimordium.class)); + cards.add(new SetCardInfo("Search for Tomorrow", 229, Rarity.COMMON, mage.cards.s.SearchForTomorrow.class)); + cards.add(new SetCardInfo("Secret Plans", 385, Rarity.SPECIAL, mage.cards.s.SecretPlans.class)); + cards.add(new SetCardInfo("Sedge Sliver", 187, Rarity.RARE, mage.cards.s.SedgeSliver.class)); + cards.add(new SetCardInfo("Sengir Nosferatu", 136, Rarity.RARE, mage.cards.s.SengirNosferatu.class)); + cards.add(new SetCardInfo("Serra Avenger", 41, Rarity.RARE, mage.cards.s.SerraAvenger.class)); + cards.add(new SetCardInfo("Shade of Trokair", 42, Rarity.COMMON, mage.cards.s.ShadeOfTrokair.class)); + cards.add(new SetCardInfo("Shaper Parasite", 85, Rarity.UNCOMMON, mage.cards.s.ShaperParasite.class)); + cards.add(new SetCardInfo("Shivan Meteor", 188, Rarity.UNCOMMON, mage.cards.s.ShivanMeteor.class)); + cards.add(new SetCardInfo("Shivan Sand-Mage", 189, Rarity.UNCOMMON, mage.cards.s.ShivanSandMage.class)); + cards.add(new SetCardInfo("Shriekmaw", 331, Rarity.SPECIAL, mage.cards.s.Shriekmaw.class)); + cards.add(new SetCardInfo("Sidewinder Sliver", 43, Rarity.COMMON, mage.cards.s.SidewinderSliver.class)); + cards.add(new SetCardInfo("Sigil of the Empty Throne", 301, Rarity.SPECIAL, mage.cards.s.SigilOfTheEmptyThrone.class)); + cards.add(new SetCardInfo("Silence", 302, Rarity.SPECIAL, mage.cards.s.Silence.class)); + cards.add(new SetCardInfo("Simian Spirit Guide", 190, Rarity.COMMON, mage.cards.s.SimianSpiritGuide.class)); + cards.add(new SetCardInfo("Sinew Sliver", 44, Rarity.COMMON, mage.cards.s.SinewSliver.class)); + cards.add(new SetCardInfo("Skirk Shaman", 191, Rarity.COMMON, mage.cards.s.SkirkShaman.class)); + cards.add(new SetCardInfo("Skittering Monstrosity", 137, Rarity.UNCOMMON, mage.cards.s.SkitteringMonstrosity.class)); + cards.add(new SetCardInfo("Slaughter Pact", 138, Rarity.RARE, mage.cards.s.SlaughterPact.class)); + cards.add(new SetCardInfo("Slimefoot, the Stowaway", 386, Rarity.SPECIAL, mage.cards.s.SlimefootTheStowaway.class)); + cards.add(new SetCardInfo("Slipstream Serpent", 86, Rarity.COMMON, mage.cards.s.SlipstreamSerpent.class)); + cards.add(new SetCardInfo("Sliver Legion", 261, Rarity.MYTHIC, mage.cards.s.SliverLegion.class)); + cards.add(new SetCardInfo("Sliversmith", 273, Rarity.UNCOMMON, mage.cards.s.Sliversmith.class)); + cards.add(new SetCardInfo("Smallpox", 139, Rarity.UNCOMMON, mage.cards.s.Smallpox.class)); + cards.add(new SetCardInfo("Snapback", 87, Rarity.COMMON, mage.cards.s.Snapback.class)); + cards.add(new SetCardInfo("Solemn Simulacrum", 400, Rarity.SPECIAL, mage.cards.s.SolemnSimulacrum.class)); + cards.add(new SetCardInfo("Sorcerous Spyglass", 401, Rarity.SPECIAL, mage.cards.s.SorcerousSpyglass.class)); + cards.add(new SetCardInfo("Spell Burst", 88, Rarity.UNCOMMON, mage.cards.s.SpellBurst.class)); + cards.add(new SetCardInfo("Spiketail Drakeling", 89, Rarity.COMMON, mage.cards.s.SpiketailDrakeling.class)); + cards.add(new SetCardInfo("Spinneret Sliver", 230, Rarity.COMMON, mage.cards.s.SpinneretSliver.class)); + cards.add(new SetCardInfo("Sporesower Thallid", 231, Rarity.UNCOMMON, mage.cards.s.SporesowerThallid.class)); + cards.add(new SetCardInfo("Sporoloth Ancient", 232, Rarity.COMMON, mage.cards.s.SporolothAncient.class)); + cards.add(new SetCardInfo("Sram, Senior Edificer", 303, Rarity.SPECIAL, mage.cards.s.SramSeniorEdificer.class)); + cards.add(new SetCardInfo("Stingscourger", 192, Rarity.COMMON, mage.cards.s.Stingscourger.class)); + cards.add(new SetCardInfo("Stinkweed Imp", 332, Rarity.SPECIAL, mage.cards.s.StinkweedImp.class)); + cards.add(new SetCardInfo("Stonecloaker", 45, Rarity.UNCOMMON, mage.cards.s.Stonecloaker.class)); + cards.add(new SetCardInfo("Stonehorn Dignitary", 304, Rarity.SPECIAL, mage.cards.s.StonehornDignitary.class)); + cards.add(new SetCardInfo("Storm Entity", 193, Rarity.UNCOMMON, mage.cards.s.StormEntity.class)); + cards.add(new SetCardInfo("Stormcloud Djinn", 90, Rarity.UNCOMMON, mage.cards.s.StormcloudDjinn.class)); + cards.add(new SetCardInfo("Stormfront Riders", 46, Rarity.UNCOMMON, mage.cards.s.StormfrontRiders.class)); + cards.add(new SetCardInfo("Strangling Soot", 140, Rarity.COMMON, mage.cards.s.StranglingSoot.class)); + cards.add(new SetCardInfo("Street Wraith", 141, Rarity.UNCOMMON, mage.cards.s.StreetWraith.class)); + cards.add(new SetCardInfo("Strength in Numbers", 233, Rarity.COMMON, mage.cards.s.StrengthInNumbers.class)); + cards.add(new SetCardInfo("Stronghold Rats", 142, Rarity.UNCOMMON, mage.cards.s.StrongholdRats.class)); + cards.add(new SetCardInfo("Stuffy Doll", 274, Rarity.RARE, mage.cards.s.StuffyDoll.class)); + cards.add(new SetCardInfo("Sudden Death", 143, Rarity.UNCOMMON, mage.cards.s.SuddenDeath.class)); + cards.add(new SetCardInfo("Sudden Shock", 194, Rarity.UNCOMMON, mage.cards.s.SuddenShock.class)); + cards.add(new SetCardInfo("Sudden Spoiling", 144, Rarity.RARE, mage.cards.s.SuddenSpoiling.class)); + cards.add(new SetCardInfo("Sulfur Elemental", 195, Rarity.UNCOMMON, mage.cards.s.SulfurElemental.class)); + cards.add(new SetCardInfo("Summoner's Pact", 234, Rarity.RARE, mage.cards.s.SummonersPact.class)); + cards.add(new SetCardInfo("Sunlance", 47, Rarity.COMMON, mage.cards.s.Sunlance.class)); + cards.add(new SetCardInfo("Swarmyard", 284, Rarity.RARE, mage.cards.s.Swarmyard.class)); + cards.add(new SetCardInfo("Sylvan Scrying", 367, Rarity.SPECIAL, mage.cards.s.SylvanScrying.class)); + cards.add(new SetCardInfo("Talrand, Sky Summoner", 318, Rarity.SPECIAL, mage.cards.t.TalrandSkySummoner.class)); + cards.add(new SetCardInfo("Tarmogoyf", 235, Rarity.MYTHIC, mage.cards.t.Tarmogoyf.class)); + cards.add(new SetCardInfo("Tasigur, the Golden Fang", 333, Rarity.SPECIAL, mage.cards.t.TasigurTheGoldenFang.class)); + cards.add(new SetCardInfo("Teferi, Mage of Zhalfir", 91, Rarity.MYTHIC, mage.cards.t.TeferiMageOfZhalfir.class)); + cards.add(new SetCardInfo("Temporal Isolation", 48, Rarity.COMMON, mage.cards.t.TemporalIsolation.class)); + cards.add(new SetCardInfo("Temur Ascendancy", 387, Rarity.SPECIAL, mage.cards.t.TemurAscendancy.class)); + cards.add(new SetCardInfo("Temur Battle Rage", 351, Rarity.SPECIAL, mage.cards.t.TemurBattleRage.class)); + cards.add(new SetCardInfo("Tendrils of Corruption", 145, Rarity.COMMON, mage.cards.t.TendrilsOfCorruption.class)); + cards.add(new SetCardInfo("Terramorphic Expanse", 285, Rarity.COMMON, mage.cards.t.TerramorphicExpanse.class)); + cards.add(new SetCardInfo("Thallid Germinator", 236, Rarity.COMMON, mage.cards.t.ThallidGerminator.class)); + cards.add(new SetCardInfo("Thallid Shell-Dweller", 237, Rarity.COMMON, mage.cards.t.ThallidShellDweller.class)); + cards.add(new SetCardInfo("Thelon of Havenwood", 238, Rarity.RARE, mage.cards.t.ThelonOfHavenwood.class)); + cards.add(new SetCardInfo("Thelonite Hermit", 239, Rarity.RARE, mage.cards.t.TheloniteHermit.class)); + cards.add(new SetCardInfo("Thick-Skinned Goblin", 196, Rarity.UNCOMMON, mage.cards.t.ThickSkinnedGoblin.class)); + cards.add(new SetCardInfo("Think Twice", 92, Rarity.COMMON, mage.cards.t.ThinkTwice.class)); + cards.add(new SetCardInfo("Thornweald Archer", 240, Rarity.COMMON, mage.cards.t.ThornwealdArcher.class)); + cards.add(new SetCardInfo("Thoughtseize", 334, Rarity.SPECIAL, mage.cards.t.Thoughtseize.class)); + cards.add(new SetCardInfo("Thraben Inspector", 305, Rarity.SPECIAL, mage.cards.t.ThrabenInspector.class)); + cards.add(new SetCardInfo("Thragtusk", 368, Rarity.SPECIAL, mage.cards.t.Thragtusk.class)); + cards.add(new SetCardInfo("Thrill of the Hunt", 241, Rarity.COMMON, mage.cards.t.ThrillOfTheHunt.class)); + cards.add(new SetCardInfo("Tidehollow Sculler", 388, Rarity.SPECIAL, mage.cards.t.TidehollowSculler.class)); + cards.add(new SetCardInfo("Time of Need", 369, Rarity.SPECIAL, mage.cards.t.TimeOfNeed.class)); + cards.add(new SetCardInfo("Timebender", 93, Rarity.UNCOMMON, mage.cards.t.Timebender.class)); + cards.add(new SetCardInfo("Tolaria West", 286, Rarity.RARE, mage.cards.t.TolariaWest.class)); + cards.add(new SetCardInfo("Tolarian Sentinel", 94, Rarity.COMMON, mage.cards.t.TolarianSentinel.class)); + cards.add(new SetCardInfo("Tombstalker", 146, Rarity.RARE, mage.cards.t.Tombstalker.class)); + cards.add(new SetCardInfo("Treasure Cruise", 319, Rarity.SPECIAL, mage.cards.t.TreasureCruise.class)); + cards.add(new SetCardInfo("Trespasser il-Vec", 147, Rarity.COMMON, mage.cards.t.TrespasserIlVec.class)); + cards.add(new SetCardInfo("Trinket Mage", 320, Rarity.SPECIAL, mage.cards.t.TrinketMage.class)); + cards.add(new SetCardInfo("Tromp the Domains", 242, Rarity.UNCOMMON, mage.cards.t.TrompTheDomains.class)); + cards.add(new SetCardInfo("True-Name Nemesis", 321, Rarity.SPECIAL, mage.cards.t.TrueNameNemesis.class)); + cards.add(new SetCardInfo("Trygon Predator", 389, Rarity.SPECIAL, mage.cards.t.TrygonPredator.class)); + cards.add(new SetCardInfo("Two-Headed Sliver", 197, Rarity.COMMON, mage.cards.t.TwoHeadedSliver.class)); + cards.add(new SetCardInfo("Uktabi Drake", 243, Rarity.COMMON, mage.cards.u.UktabiDrake.class)); + cards.add(new SetCardInfo("Urborg Syphon-Mage", 148, Rarity.COMMON, mage.cards.u.UrborgSyphonMage.class)); + cards.add(new SetCardInfo("Urborg, Tomb of Yawgmoth", 287, Rarity.RARE, mage.cards.u.UrborgTombOfYawgmoth.class)); + cards.add(new SetCardInfo("Urza's Factory", 288, Rarity.UNCOMMON, mage.cards.u.UrzasFactory.class)); + cards.add(new SetCardInfo("Utopia Mycon", 244, Rarity.UNCOMMON, mage.cards.u.UtopiaMycon.class)); + cards.add(new SetCardInfo("Utopia Vow", 245, Rarity.COMMON, mage.cards.u.UtopiaVow.class)); + cards.add(new SetCardInfo("Vampire Hexmage", 335, Rarity.SPECIAL, mage.cards.v.VampireHexmage.class)); + cards.add(new SetCardInfo("Vandalblast", 352, Rarity.SPECIAL, mage.cards.v.Vandalblast.class)); + cards.add(new SetCardInfo("Vanquisher's Banner", 402, Rarity.SPECIAL, mage.cards.v.VanquishersBanner.class)); + cards.add(new SetCardInfo("Veiling Oddity", 95, Rarity.COMMON, mage.cards.v.VeilingOddity.class)); + cards.add(new SetCardInfo("Venser, Shaper Savant", 96, Rarity.RARE, mage.cards.v.VenserShaperSavant.class)); + cards.add(new SetCardInfo("Vesuva", 289, Rarity.MYTHIC, mage.cards.v.Vesuva.class)); + cards.add(new SetCardInfo("Vesuvan Shapeshifter", 97, Rarity.RARE, mage.cards.v.VesuvanShapeshifter.class)); + cards.add(new SetCardInfo("Virulent Sliver", 246, Rarity.COMMON, mage.cards.v.VirulentSliver.class)); + cards.add(new SetCardInfo("Walk the Aeons", 98, Rarity.RARE, mage.cards.w.WalkTheAeons.class)); + cards.add(new SetCardInfo("Wastes", 410, Rarity.SPECIAL, mage.cards.w.Wastes.class)); + cards.add(new SetCardInfo("Watcher Sliver", 49, Rarity.COMMON, mage.cards.w.WatcherSliver.class)); + cards.add(new SetCardInfo("Wheel of Fate", 198, Rarity.MYTHIC, mage.cards.w.WheelOfFate.class)); + cards.add(new SetCardInfo("Whip-Spine Drake", 99, Rarity.UNCOMMON, mage.cards.w.WhipSpineDrake.class)); + cards.add(new SetCardInfo("Whitemane Lion", 50, Rarity.COMMON, mage.cards.w.WhitemaneLion.class)); + cards.add(new SetCardInfo("Wipe Away", 100, Rarity.UNCOMMON, mage.cards.w.WipeAway.class)); + cards.add(new SetCardInfo("Yavimaya Dryad", 247, Rarity.UNCOMMON, mage.cards.y.YavimayaDryad.class)); + cards.add(new SetCardInfo("Yawgmoth, Thran Physician", 336, Rarity.SPECIAL, mage.cards.y.YawgmothThranPhysician.class)); + cards.add(new SetCardInfo("Yixlid Jailer", 149, Rarity.UNCOMMON, mage.cards.y.YixlidJailer.class)); + cards.add(new SetCardInfo("Young Pyromancer", 353, Rarity.SPECIAL, mage.cards.y.YoungPyromancer.class)); + cards.add(new SetCardInfo("Zealous Conscripts", 354, Rarity.SPECIAL, mage.cards.z.ZealousConscripts.class)); + cards.add(new SetCardInfo("Zulaport Cutthroat", 337, Rarity.SPECIAL, mage.cards.z.ZulaportCutthroat.class)); + } + + @Override + public List getSpecialBonus() { + if (savedSpecialBonus.isEmpty()) { + savedSpecialBonus.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes(this.code))); + savedSpecialBonus.removeIf(cardInfo -> cardInfo.getCardNumberAsInt() > 410); + savedSpecialBonus.removeIf(cardInfo -> cardInfo.getCardNumberAsInt() <= 289); + } + + return new ArrayList<>(savedSpecialBonus); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/tsr.html +// Using USA collation for common/uncommon, rare and bonus sheet are standard +class TimeSpiralRemasteredCollator implements BoosterCollator { + + private static class TimeSpiralRemasteredRun extends CardRun { + private static final TimeSpiralRemasteredRun commonA = new TimeSpiralRemasteredRun(true, "176", "65", "177", "94", "192", "70", "86", "191", "84", "168", "80", "69", "173", "73", "154", "87", "197", "92", "166", "67", "183", "78", "171", "81", "155", "95", "190", "70", "191", "58", "168", "63", "162", "89", "157", "65", "173", "69", "159", "73", "176", "94", "192", "86", "197", "84", "166", "67", "177", "92", "171", "80", "162", "81", "155", "78", "154", "87", "157", "95", "190", "58", "159", "63", "183", "89"); + private static final TimeSpiralRemasteredRun commonB = new TimeSpiralRemasteredRun(true, "110", "245", "135", "241", "104", "226", "233", "108", "236", "107", "207", "229", "101", "201", "116", "199", "123", "246", "130", "240", "109", "245", "103", "202", "118", "221", "117", "230", "105", "212", "148", "243", "134", "237", "135", "226", "110", "236", "108", "233", "107", "241", "101", "229", "116", "201", "118", "199", "104", "246", "109", "240", "130", "207", "103", "202", "117", "221", "105", "230", "134", "243", "148", "212", "123", "237"); + private static final TimeSpiralRemasteredRun commonC1 = new TimeSpiralRemasteredRun(true, "44", "223", "20", "140", "53", "21", "181", "17", "184", "2", "205", "151", "42", "119", "74", "1", "161", "272", "10", "228", "12", "68", "6", "285", "7", "48", "53", "20", "133", "44", "223", "21", "140", "151", "2", "184", "17", "74", "42", "181", "228", "1", "10", "119", "12", "68", "285", "48", "272", "6", "205", "161", "7", "133", "185"); + private static final TimeSpiralRemasteredRun commonC2 = new TimeSpiralRemasteredRun(true, "25", "147", "31", "64", "269", "47", "206", "43", "178", "132", "263", "18", "200", "59", "26", "145", "9", "66", "50", "14", "147", "28", "232", "49", "122", "71", "22", "25", "165", "132", "31", "206", "43", "47", "269", "64", "18", "178", "200", "26", "263", "59", "9", "145", "14", "66", "50", "165", "28", "122", "232", "22", "49", "71", "185"); + private static final TimeSpiralRemasteredRun uncommonA = new TimeSpiralRemasteredRun(true, "217", "283", "196", "242", "126", "194", "13", "222", "248", "40", "128", "195", "204", "5", "189", "113", "56", "249", "218", "180", "149", "276", "16", "258", "224", "79", "163", "142", "19", "61", "273", "153", "85", "30", "111", "170", "57", "231", "24", "99", "143", "279", "100", "174", "213", "55", "131", "46", "244", "126", "196", "82", "219", "102", "188", "208", "33", "120", "60", "186", "217", "283", "169", "124", "242", "194", "61", "218", "16", "153", "142", "258", "13", "224", "249", "113", "189", "79", "248", "204", "19", "149", "163", "5", "276", "222", "195", "40", "56", "180", "128", "273", "85", "30", "111", "170", "231", "57", "24", "143", "99", "279", "174", "213", "100", "131", "46", "244", "124", "55", "208", "188", "120", "60", "186", "33", "219", "169", "102", "82"); + private static final TimeSpiralRemasteredRun uncommonB = new TimeSpiralRemasteredRun(true, "38", "152", "141", "29", "72", "225", "11", "158", "88", "271", "23", "76", "247", "160", "139", "250", "216", "288", "264", "45", "93", "252", "137", "275", "214", "39", "54", "129", "193", "227", "35", "112", "83", "282", "38", "164", "254", "115", "90", "37", "211", "152", "72", "141", "29", "247", "250", "158", "88", "11", "271", "225", "76", "139", "23", "160", "216", "288", "93", "45", "252", "137", "275", "214", "54", "264", "39", "227", "129", "193", "35", "282", "83", "112", "37", "254", "211", "38", "115", "90", "164", "29", "72", "152", "141", "225", "11", "88", "271", "158", "247", "23", "139", "76", "250", "160", "288", "252", "216", "93", "45", "137", "264", "214", "54", "275", "227", "129", "39", "193", "112", "35", "282", "83", "254", "211", "115", "37", "164", "90"); + private static final TimeSpiralRemasteredRun rare = new TimeSpiralRemasteredRun(false, "3", "4", "8", "27", "32", "34", "41", "51", "62", "75", "77", "96", "97", "98", "114", "125", "127", "136", "138", "144", "146", "156", "167", "172", "175", "179", "182", "187", "203", "209", "215", "220", "234", "238", "239", "251", "253", "255", "256", "257", "259", "260", "265", "266", "268", "270", "274", "277", "278", "281", "284", "286", "287", "3", "4", "8", "27", "32", "34", "41", "51", "62", "75", "77", "96", "97", "98", "114", "125", "127", "136", "138", "144", "146", "156", "167", "172", "175", "179", "182", "187", "203", "209", "215", "220", "234", "238", "239", "251", "253", "255", "256", "257", "259", "260", "265", "266", "268", "270", "274", "277", "278", "281", "284", "286", "287", "15", "36", "52", "91", "106", "121", "150", "198", "210", "235", "261", "262", "267", "280", "289"); + private static final TimeSpiralRemasteredRun special = new TimeSpiralRemasteredRun(false, "290", "291", "292", "293", "294", "295", "296", "297", "298", "299", "300", "301", "302", "303", "304", "305", "306", "307", "308", "309", "310", "311", "312", "313", "314", "315", "316", "317", "318", "319", "320", "321", "322", "323", "324", "325", "326", "327", "328", "329", "330", "331", "332", "333", "334", "335", "336", "337", "338", "339", "340", "341", "342", "343", "344", "345", "346", "347", "348", "349", "350", "351", "352", "353", "354", "355", "356", "357", "358", "359", "360", "361", "362", "363", "364", "365", "366", "367", "368", "369", "370", "371", "372", "373", "374", "375", "376", "377", "378", "379", "380", "381", "382", "383", "384", "385", "386", "387", "388", "389", "390", "391", "392", "393", "394", "395", "396", "397", "398", "399", "400", "401", "402", "403", "404", "405", "406", "407", "408", "409", "410"); + + private TimeSpiralRemasteredRun(boolean keepOrder, String... numbers) { + super(keepOrder, numbers); + } + } + + private static class TimeSpiralRemasteredStructure extends BoosterStructure { + private static final TimeSpiralRemasteredStructure C1 = new TimeSpiralRemasteredStructure( + TimeSpiralRemasteredRun.commonA, + TimeSpiralRemasteredRun.commonA, + TimeSpiralRemasteredRun.commonA, + TimeSpiralRemasteredRun.commonB, + TimeSpiralRemasteredRun.commonB, + TimeSpiralRemasteredRun.commonB, + TimeSpiralRemasteredRun.commonC1, + TimeSpiralRemasteredRun.commonC1, + TimeSpiralRemasteredRun.commonC1, + TimeSpiralRemasteredRun.commonC1 + ); + private static final TimeSpiralRemasteredStructure C2 = new TimeSpiralRemasteredStructure( + TimeSpiralRemasteredRun.commonA, + TimeSpiralRemasteredRun.commonA, + TimeSpiralRemasteredRun.commonA, + TimeSpiralRemasteredRun.commonB, + TimeSpiralRemasteredRun.commonB, + TimeSpiralRemasteredRun.commonC1, + TimeSpiralRemasteredRun.commonC1, + TimeSpiralRemasteredRun.commonC1, + TimeSpiralRemasteredRun.commonC1, + TimeSpiralRemasteredRun.commonC1 + ); + private static final TimeSpiralRemasteredStructure C3 = new TimeSpiralRemasteredStructure( + TimeSpiralRemasteredRun.commonA, + TimeSpiralRemasteredRun.commonA, + TimeSpiralRemasteredRun.commonB, + TimeSpiralRemasteredRun.commonB, + TimeSpiralRemasteredRun.commonB, + TimeSpiralRemasteredRun.commonC1, + TimeSpiralRemasteredRun.commonC1, + TimeSpiralRemasteredRun.commonC1, + TimeSpiralRemasteredRun.commonC1, + TimeSpiralRemasteredRun.commonC1 + ); + private static final TimeSpiralRemasteredStructure C4 = new TimeSpiralRemasteredStructure( + TimeSpiralRemasteredRun.commonA, + TimeSpiralRemasteredRun.commonA, + TimeSpiralRemasteredRun.commonA, + TimeSpiralRemasteredRun.commonB, + TimeSpiralRemasteredRun.commonB, + TimeSpiralRemasteredRun.commonB, + TimeSpiralRemasteredRun.commonC2, + TimeSpiralRemasteredRun.commonC2, + TimeSpiralRemasteredRun.commonC2, + TimeSpiralRemasteredRun.commonC2 + ); + private static final TimeSpiralRemasteredStructure C5 = new TimeSpiralRemasteredStructure( + TimeSpiralRemasteredRun.commonA, + TimeSpiralRemasteredRun.commonA, + TimeSpiralRemasteredRun.commonA, + TimeSpiralRemasteredRun.commonB, + TimeSpiralRemasteredRun.commonB, + TimeSpiralRemasteredRun.commonC2, + TimeSpiralRemasteredRun.commonC2, + TimeSpiralRemasteredRun.commonC2, + TimeSpiralRemasteredRun.commonC2, + TimeSpiralRemasteredRun.commonC2 + ); + private static final TimeSpiralRemasteredStructure C6 = new TimeSpiralRemasteredStructure( + TimeSpiralRemasteredRun.commonA, + TimeSpiralRemasteredRun.commonA, + TimeSpiralRemasteredRun.commonB, + TimeSpiralRemasteredRun.commonB, + TimeSpiralRemasteredRun.commonB, + TimeSpiralRemasteredRun.commonC2, + TimeSpiralRemasteredRun.commonC2, + TimeSpiralRemasteredRun.commonC2, + TimeSpiralRemasteredRun.commonC2, + TimeSpiralRemasteredRun.commonC2 + ); + private static final TimeSpiralRemasteredStructure U1 = new TimeSpiralRemasteredStructure( + TimeSpiralRemasteredRun.uncommonA, + TimeSpiralRemasteredRun.uncommonA, + TimeSpiralRemasteredRun.uncommonA + ); + private static final TimeSpiralRemasteredStructure U2 = new TimeSpiralRemasteredStructure( + TimeSpiralRemasteredRun.uncommonB, + TimeSpiralRemasteredRun.uncommonB, + TimeSpiralRemasteredRun.uncommonB + ); + private static final TimeSpiralRemasteredStructure R1 = new TimeSpiralRemasteredStructure( + TimeSpiralRemasteredRun.rare + ); + private static final TimeSpiralRemasteredStructure S1 = new TimeSpiralRemasteredStructure( + TimeSpiralRemasteredRun.special + ); + + private TimeSpiralRemasteredStructure(CardRun... runs) { + super(runs); + } + } + + private final RarityConfiguration commonRuns = new RarityConfiguration( + false, + TimeSpiralRemasteredStructure.C1, + TimeSpiralRemasteredStructure.C2, + TimeSpiralRemasteredStructure.C3, + TimeSpiralRemasteredStructure.C4, + TimeSpiralRemasteredStructure.C5, + TimeSpiralRemasteredStructure.C6 + ); + private final RarityConfiguration uncommonRuns = new RarityConfiguration( + false, + TimeSpiralRemasteredStructure.U1, + TimeSpiralRemasteredStructure.U1, + TimeSpiralRemasteredStructure.U1, + TimeSpiralRemasteredStructure.U2, + TimeSpiralRemasteredStructure.U2 + ); + private final RarityConfiguration rareRuns = new RarityConfiguration( + TimeSpiralRemasteredStructure.R1 + ); + private final RarityConfiguration specialRuns = new RarityConfiguration( + TimeSpiralRemasteredStructure.S1 + ); + + @Override + public void shuffle() { + commonRuns.shuffle(); + uncommonRuns.shuffle(); + rareRuns.shuffle(); + specialRuns.shuffle(); + } + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(specialRuns.getNext().makeRun()); + return booster; + } +} diff --git a/Mage.Sets/src/mage/sets/ZendikarRisingExpeditions.java b/Mage.Sets/src/mage/sets/ZendikarRisingExpeditions.java new file mode 100644 index 000000000000..8a1696346102 --- /dev/null +++ b/Mage.Sets/src/mage/sets/ZendikarRisingExpeditions.java @@ -0,0 +1,55 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * @author TheElk801 + */ +public final class ZendikarRisingExpeditions extends ExpansionSet { + + private static final ZendikarRisingExpeditions instance = new ZendikarRisingExpeditions(); + + public static ZendikarRisingExpeditions getInstance() { + return instance; + } + + private ZendikarRisingExpeditions() { + super("Zendikar Rising Expeditions", "ZNE", ExpansionSet.buildDate(2020, 9, 25), SetType.PROMOTIONAL); + this.blockName = "Masterpiece Series"; + this.hasBoosters = false; + this.hasBasicLands = false; + + cards.add(new SetCardInfo("Ancient Tomb", 21, Rarity.MYTHIC, mage.cards.a.AncientTomb.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Arid Mesa", 9, Rarity.MYTHIC, mage.cards.a.AridMesa.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Blackcleave Cliffs", 13, Rarity.MYTHIC, mage.cards.b.BlackcleaveCliffs.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Bloodstained Mire", 3, Rarity.MYTHIC, mage.cards.b.BloodstainedMire.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Bountiful Promenade", 20, Rarity.MYTHIC, mage.cards.b.BountifulPromenade.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Cavern of Souls", 22, Rarity.MYTHIC, mage.cards.c.CavernOfSouls.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Celestial Colonnade", 23, Rarity.MYTHIC, mage.cards.c.CelestialColonnade.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Copperline Gorge", 14, Rarity.MYTHIC, mage.cards.c.CopperlineGorge.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Creeping Tar Pit", 24, Rarity.MYTHIC, mage.cards.c.CreepingTarPit.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Darkslick Shores", 12, Rarity.MYTHIC, mage.cards.d.DarkslickShores.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Flooded Strand", 1, Rarity.MYTHIC, mage.cards.f.FloodedStrand.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Grove of the Burnwillows", 25, Rarity.MYTHIC, mage.cards.g.GroveOfTheBurnwillows.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Horizon Canopy", 26, Rarity.MYTHIC, mage.cards.h.HorizonCanopy.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Luxury Suite", 18, Rarity.MYTHIC, mage.cards.l.LuxurySuite.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Marsh Flats", 6, Rarity.MYTHIC, mage.cards.m.MarshFlats.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Misty Rainforest", 10, Rarity.MYTHIC, mage.cards.m.MistyRainforest.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Morphic Pool", 17, Rarity.MYTHIC, mage.cards.m.MorphicPool.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Polluted Delta", 2, Rarity.MYTHIC, mage.cards.p.PollutedDelta.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Prismatic Vista", 27, Rarity.MYTHIC, mage.cards.p.PrismaticVista.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Razorverge Thicket", 15, Rarity.MYTHIC, mage.cards.r.RazorvergeThicket.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Scalding Tarn", 7, Rarity.MYTHIC, mage.cards.s.ScaldingTarn.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Sea of Clouds", 16, Rarity.MYTHIC, mage.cards.s.SeaOfClouds.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Seachrome Coast", 11, Rarity.MYTHIC, mage.cards.s.SeachromeCoast.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Spire Garden", 19, Rarity.MYTHIC, mage.cards.s.SpireGarden.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Strip Mine", 28, Rarity.MYTHIC, mage.cards.s.StripMine.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Valakut, the Molten Pinnacle", 29, Rarity.MYTHIC, mage.cards.v.ValakutTheMoltenPinnacle.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Verdant Catacombs", 8, Rarity.MYTHIC, mage.cards.v.VerdantCatacombs.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Wasteland", 30, Rarity.MYTHIC, mage.cards.w.Wasteland.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Windswept Heath", 5, Rarity.MYTHIC, mage.cards.w.WindsweptHeath.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Wooded Foothills", 4, Rarity.MYTHIC, mage.cards.w.WoodedFoothills.class, FULL_ART_BFZ_VARIOUS)); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/AI/basic/CostModificationTest.java b/Mage.Tests/src/test/java/org/mage/test/AI/basic/CostModificationTest.java index a5a2bac25c01..490be237d945 100644 --- a/Mage.Tests/src/test/java/org/mage/test/AI/basic/CostModificationTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/AI/basic/CostModificationTest.java @@ -3,12 +3,10 @@ import mage.constants.PhaseStep; import mage.constants.Zone; -import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBaseAI; /** - * * @author LevelX2 */ public class CostModificationTest extends CardTestPlayerBaseAI { @@ -18,8 +16,6 @@ public class CostModificationTest extends CardTestPlayerBaseAI { * Once played, I am stuck at "Waiting for Computer" forever... */ @Test - @Ignore - // TODO: Check why sometimes Silvercoat Lion is not cast from AI public void testFluctuator() { addCard(Zone.HAND, playerA, "Silvercoat Lion"); // Destroy all artifacts, creatures, and enchantments. diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/PutOntoBattlefieldTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/PutOntoBattlefieldTest.java index 73aa73196909..e9a0c8e66e1b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/PutOntoBattlefieldTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/PutOntoBattlefieldTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.abilities.activated; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/BanisherPriestTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/BanisherPriestTest.java index 61853f90c9a7..e3e58819c329 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/BanisherPriestTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/BanisherPriestTest.java @@ -1,9 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - package org.mage.test.cards.abilities.enters; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/BloodMoonTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/BloodMoonTest.java index c7c39c5f6088..54167fcd163a 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/BloodMoonTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/BloodMoonTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.abilities.enters; import mage.constants.CardType; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/NayaSoulbeastTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/NayaSoulbeastTest.java index d7e82904fcf5..57cdbdac796a 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/NayaSoulbeastTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/NayaSoulbeastTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.abilities.enters; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/SkylineCascadeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/SkylineCascadeTest.java index fe5b0016b58e..15c9caa991ad 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/SkylineCascadeTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/SkylineCascadeTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.abilities.enters; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/equipped/LeoninShikariTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/equipped/LeoninShikariTest.java index 41eb9b274afa..7c84610eb62a 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/equipped/LeoninShikariTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/equipped/LeoninShikariTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.abilities.equipped; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DelveTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DelveTest.java index 854f9c9ada66..b657263388cb 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DelveTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DelveTest.java @@ -29,13 +29,13 @@ public void test_PlayDelve_Manual() { setChoice(playerA, "Blue"); // pay 1 setChoice(playerA, "Blue"); // pay 2 // delve can be payed in test only by one card - setChoice(playerA, "Exile cards"); + setChoice(playerA, "Exile a card"); setChoice(playerA, "Balduvian Bears"); // pay 3 as delve - setChoice(playerA, "Exile cards"); + setChoice(playerA, "Exile a card"); setChoice(playerA, "Balduvian Bears"); // pay 4 as delve - setChoice(playerA, "Exile cards"); + setChoice(playerA, "Exile a card"); setChoice(playerA, "Balduvian Bears"); // pay 5 as delve - setChoice(playerA, "Exile cards"); + setChoice(playerA, "Exile a card"); setChoice(playerA, "Balduvian Bears"); // pay 6 as delve setStrictChooseMode(true); @@ -105,7 +105,7 @@ public void test_CheatWithCancel() { activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treasure Cruise"); - setChoice(playerA, "Exile cards"); // delve activate (special button in UI) + setChoice(playerA, "Exile a card"); // delve activate (special button in UI) setChoice(playerA, TestPlayer.CHOICE_SKIP); // devle cost with nothing (done button in UI) setChoice(playerA, TestPlayer.MANA_CANCEL); // mana payment cancel (cancel button in UI) setChoice(playerA, TestPlayer.SKIP_FAILED_COMMAND); // delete cast/activate command from queue diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EpicTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EpicTest.java new file mode 100644 index 000000000000..95a61e6cc4a0 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EpicTest.java @@ -0,0 +1,47 @@ +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class EpicTest extends CardTestPlayerBase { + + @Test + public void testEndlessSwarm() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 8); + addCard(Zone.HAND, playerA, "Forest", 3); + addCard(Zone.HAND, playerA, "Endless Swarm"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Endless Swarm"); + + setStrictChooseMode(true); + setStopAt(7, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Snake", 3 + 3 + 4 + 5); + assertPermanentCount(playerA, "Forest", 8); + } + + @Test + public void testEndlessSwarmCopied() { + addCard(Zone.BATTLEFIELD, playerA, "Tropical Island", 10); + addCard(Zone.HAND, playerA, "Forest", 3); + addCard(Zone.HAND, playerA, "Endless Swarm"); + addCard(Zone.HAND, playerA, "Twincast"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Endless Swarm"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Twincast", "Endless Swarm"); + + setStopAt(7, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Snake", 2 * (3 + 3 + 4 + 5)); + assertPermanentCount(playerA, "Tropical Island", 10); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GoadTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GoadTest.java index 02ef1d7332de..e0729305b417 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GoadTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GoadTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.abilities.keywords; import java.io.FileNotFoundException; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MetalcraftTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MetalcraftTest.java index d37caa01a5f4..05a5ee33ed4b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MetalcraftTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MetalcraftTest.java @@ -31,7 +31,7 @@ public void testMetalcraftFromBlinkmoth() { // {1}, {T}: Target Blinkmoth creature gets +1/+1 until end of turn. addCard(Zone.BATTLEFIELD, playerA, "Blinkmoth Nexus", 1); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}: Until end of turn {this} becomes "); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}:"); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ModularTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ModularTest.java index 8191804c2f50..91502ecdeb71 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ModularTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ModularTest.java @@ -8,7 +8,6 @@ import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author BetaSteward */ public class ModularTest extends CardTestPlayerBase { @@ -33,7 +32,6 @@ public class ModularTest extends CardTestPlayerBase { * Arcbound Hybrid Artifact Creature — Beast 0/0, 4 (4) Haste Modular 2 * (This enters the battlefield with two +1/+1 counters on it. When it dies, * you may put its +1/+1 counters on target artifact creature.) - * */ @Test public void testModularEnters() { @@ -105,4 +103,71 @@ public void testInkmothNexus() { } + /** + * If a creature with modular dies due to -1/-1 counters, it still had those counters when it left + * and therefore will still transfer them to a creature on the battlefield + */ + @Test + public void testMinusCounters() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerA, "Arcbound Bruiser"); + addCard(Zone.BATTLEFIELD, playerA, "Memnite"); + addCard(Zone.HAND, playerA, "Puncture Blast"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Puncture Blast", "Arcbound Bruiser"); + setChoice(playerA, "Yes"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, "Arcbound Bruiser", 1); + assertGraveyardCount(playerA, "Puncture Blast", 1); + assertCounterCount("Memnite", CounterType.P1P1, 3); + } + + /** + * If a creature with modular dies and returns and dies again before any modular triggers resolve, + * the modular triggers should use that creature's counters from each time it died + * rather than the most recent time it died + */ + @Test + public void testReturnedAndKilledAgain() { + addCard(Zone.BATTLEFIELD, playerA, "Badlands", 11); + addCard(Zone.BATTLEFIELD, playerA, "Arcbound Lancer"); + addCard(Zone.BATTLEFIELD, playerA, "Memnite"); + addCard(Zone.HAND, playerA, "Makeshift Mannequin"); + addCard(Zone.HAND, playerA, "Puncture Blast"); + addCard(Zone.HAND, playerA, "Flame Slash"); + addCard(Zone.HAND, playerA, "Murder"); + + // put three -1/-1 counters on lancer, which leaves it with one +1/+1 + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Puncture Blast", "Arcbound Lancer"); + setChoice(playerA, "Yes", 2); + checkStackSize("stack1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 1); + + // kill lancer with one +1/+1 counter on it + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flame Slash", "Arcbound Lancer"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true); + checkStackSize("stack2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 1); + + // in response to modular trigger, return lancer to the battlefield + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Makeshift Mannequin", "Arcbound Lancer"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true); + checkStackSize("stack3", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2); + + // kill lancer again with original modular trigger on the stack + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Murder", "Arcbound Lancer"); + checkStackSize("stack4", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Arcbound Lancer", 1); + assertGraveyardCount(playerA, "Puncture Blast", 1); + assertGraveyardCount(playerA, "Flame Slash", 1); + assertGraveyardCount(playerA, "Murder", 1); + // Memnite should have 1 counter for the first trigger and 4 from the second + assertCounterCount("Memnite", CounterType.P1P1, 1 + 4); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/PersistTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/PersistTest.java index 6da6e7a15412..dc7b96b5d03b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/PersistTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/PersistTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.abilities.keywords; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ScavengeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ScavengeTest.java index 26ec57c58eb8..c2c571decb87 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ScavengeTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ScavengeTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.abilities.keywords; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/ConditionalOneShotEffectTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/ConditionalOneShotEffectTest.java index cdb534c3f3cd..5cc720287199 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/ConditionalOneShotEffectTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/ConditionalOneShotEffectTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.abilities.oneshot; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/CounterActivatedAbilityTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/CounterActivatedAbilityTest.java index f3e4f57864c5..107029e01594 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/CounterActivatedAbilityTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/CounterActivatedAbilityTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.abilities.oneshot.counterspell; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/ForceOfWillTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/ForceOfWillTest.java index 878c624f2a10..2468e8b68f83 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/ForceOfWillTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/ForceOfWillTest.java @@ -1,9 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - package org.mage.test.cards.abilities.oneshot.counterspell; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/destroy/HeatStrokeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/destroy/HeatStrokeTest.java index b46ac85e175d..0d8974ab65f7 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/destroy/HeatStrokeTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/destroy/HeatStrokeTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.abilities.oneshot.destroy; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/exile/SurgicalExtractionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/exile/SurgicalExtractionTest.java index a7cfc42d2b57..9a26b3fa8238 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/exile/SurgicalExtractionTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/exile/SurgicalExtractionTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.abilities.oneshot.exile; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/SoulfireGrandMasterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/SoulfireGrandMasterTest.java index fc3cf81945be..f7670572addb 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/SoulfireGrandMasterTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/SoulfireGrandMasterTest.java @@ -1,4 +1,3 @@ - package org.mage.test.cards.abilities.other; import mage.constants.PhaseStep; @@ -7,7 +6,6 @@ import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author BetaSteward */ public class SoulfireGrandMasterTest extends CardTestPlayerBase { @@ -17,7 +15,6 @@ public class SoulfireGrandMasterTest extends CardTestPlayerBase { * and sorcery spells you control have lifelink. {2}{U/R}{U/R}: The next * time you cast an instant or sorcery spell from your hand this turn, put * that card into your hand instead of into your graveyard as it resolves. - * */ @Test public void testSpellsGainLifelink() { @@ -120,19 +117,23 @@ public void testSearinBlood2() { /** * Test copied instant spell gives also life - * */ @Test - public void testCopySpell() { + public void test_CopiesMustHaveGainedLifelink() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); addCard(Zone.BATTLEFIELD, playerA, "Island", 1); addCard(Zone.HAND, playerA, "Lightning Bolt"); + // + // Instant and sorcery spells you control have lifelink. addCard(Zone.BATTLEFIELD, playerA, "Soulfire Grand Master", 1); + // // {2}{U}{R}: Copy target instant or sorcery spell you control. You may choose new targets for the copy. addCard(Zone.BATTLEFIELD, playerA, "Nivix Guildmage", 1); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{U}{R}:"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true); + checkStackSize("after copy", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2); // 2x bolts setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); @@ -143,12 +144,10 @@ public void testCopySpell() { assertLife(playerB, 14); assertLife(playerA, 26); - } /** * Test damage of activated ability of a permanent does not gain lifelink - * */ @Test public void testActivatedAbility() { @@ -235,7 +234,6 @@ public void testSoulfireStokeTheFlames() { /** * Check if second ability resolved, the next spell that is counterer won't * go to hand back because it did not resolve - * */ @Test public void testSoulfireCounteredSpellDontGoesBack() { @@ -269,7 +267,6 @@ public void testSoulfireCounteredSpellDontGoesBack() { * caster life. It should as it has lifelink, and it's Deflecting Palm (an * instant) dealing damage. I was playing against a human in Standard * Constructed. - * */ @Test public void testWithDeflectingPalm() { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/AsThoughManaAndAITest.java b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/AsThoughManaAndAITest.java new file mode 100644 index 000000000000..b9c376a7260d --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/AsThoughManaAndAITest.java @@ -0,0 +1,34 @@ +package org.mage.test.cards.asthough; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class AsThoughManaAndAITest extends CardTestPlayerBase { + + @Test + public void test_AutoPaymentMustUseAsThoughMana() { + // possible bug: AI auto-payment uses first mana ability from the mana source, so multi-colored lands can be broken + + // You may spend white mana as though it were red mana. + addCard(Zone.BATTLEFIELD, playerA, "Sunglasses of Urza", 1); + // + // {T}: Add {U} or {W}. + addCard(Zone.BATTLEFIELD, playerA, "Sea of Clouds", 1); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); // {R} + + checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerB, 20 - 3); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/DidNotHaveHexproofTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/DidNotHaveHexproofTest.java index 369faa3aecb3..475bae764448 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/DidNotHaveHexproofTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/DidNotHaveHexproofTest.java @@ -1,9 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - package org.mage.test.cards.asthough; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/LeylineOfAnticipationTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/LeylineOfAnticipationTest.java index e6031329b574..d7bed1b60ceb 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/LeylineOfAnticipationTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/LeylineOfAnticipationTest.java @@ -1,9 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - package org.mage.test.cards.asthough; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayTopCardFromLibraryTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayTopCardFromLibraryTest.java index db9cc79df873..47cb9d812374 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayTopCardFromLibraryTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayTopCardFromLibraryTest.java @@ -94,6 +94,7 @@ public void test_BolassCitadel_SplitRightPlay() { addCard(Zone.BATTLEFIELD, playerA, "Bolas's Citadel", 1); // Double your life total. Target opponent loses half their life, rounded up. + checkPlayableAbility("playable", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Revenge", true); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Revenge", playerB); // {4}{W}{B} = 6 life setStrictChooseMode(true); @@ -113,6 +114,7 @@ public void test_BolassCitadel_SplitLeftPlay() { addCard(Zone.GRAVEYARD, playerA, "Balduvian Bears", 1); // Return target creature card with converted mana cost 3 or less from your graveyard to the battlefield. + checkPlayableAbility("playable", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Revival", true); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Revival", "Balduvian Bears"); // {W/B}{W/B} = 2 life setStrictChooseMode(true); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/SpendOtherManaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/SpendOtherManaTest.java index 9b992062cd29..9894cc884c55 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/SpendOtherManaTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/SpendOtherManaTest.java @@ -190,6 +190,7 @@ public void test_QuicksilverElemental_Flicker() { checkPlayableAbility("must have new ability", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}, {T}:", true); // renew target + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {W}", 2); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flicker", "Anaba Shaman"); // use ability diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ChangelingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ChangelingTest.java index 807f29bd8ac7..4e60df5f3b01 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ChangelingTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ChangelingTest.java @@ -33,7 +33,7 @@ public void coatOfArmsTest() { addCard(Zone.BATTLEFIELD, playerA, copter); addCard(Zone.BATTLEFIELD, playerA, woodlandChangeling, 2); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}: Until end of turn {this} becomes"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}:"); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Crew"); setChoice(playerA, ultimus); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java index 690b387fd2df..c69a8f91726d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java @@ -311,7 +311,7 @@ public void test_ExileWithDelvePayAndReturn() { activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 5); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ethereal Forager"); setChoice(playerA, "Blue", 5); // pay normal - setChoice(playerA, "Exile cards"); // pay delve + setChoice(playerA, "Exile a card"); // pay delve setChoice(playerA, "Balduvian Bears"); setChoice(playerA, "Yes"); // move to command zone @@ -578,4 +578,37 @@ public void test_ModalDoubleFacesCard_CanReturnAfterKillAndCommanderControlCondi execute(); assertAllCommandsUsed(); } + + @Test + public void test_Escape_CantBeCastableFromCommandZone() { + // Player order: A -> D -> C -> B + + // When Uro enters the battlefield, sacrifice it unless it escaped. + // Whenever Uro enters the battlefield or attacks, you gain 3 life and draw a card, then you may put a land card from your hand onto the battlefield. + // Escape-{G}{G}{U}{U}, Exile five other cards from your graveyard. + addCard(Zone.COMMAND, playerA, "Uro, Titan of Nature's Wrath", 1); // {1}{G}{U} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + // + addCard(Zone.GRAVEYARD, playerA, "Grizzly Bears", 5); + addCard(Zone.HAND, playerA, "Swamp", 1); + + checkPlayableAbility("normal cast allowed", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Uro, Titan of Nature's Wrath", true); + checkPlayableAbility("escape cast not allowed", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Uro, Titan of Nature's Wrath with Escape", false); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Uro, Titan of Nature's Wrath"); + setChoice(playerA, "Whenever {this} enters the battlefield or attacks"); // gain life trigger first, sacrifice next + setChoice(playerA, "No"); // keep in graveyard + setChoice(playerA, "Yes"); // put land to battlefield + setChoice(playerA, "Swamp"); // put a Swamp + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 20 + 3); + assertPermanentCount(playerA, "Swamp", 1); + assertGraveyardCount(playerA, "Uro, Titan of Nature's Wrath", 1); // sacrificed + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ImprisonedInTheMoonTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ImprisonedInTheMoonTest.java index 78547484d5f1..dd82c303021e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ImprisonedInTheMoonTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ImprisonedInTheMoonTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.continuous; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LoosingAbilitiesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LoosingAbilitiesTest.java index a2dd3eb67612..6b6e50198e8f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LoosingAbilitiesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LoosingAbilitiesTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.continuous; import mage.abilities.keyword.SwampwalkAbility; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/MerfolkTricksterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/MerfolkTricksterTest.java index 1a9e2e6c5c17..49a0b6857beb 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/MerfolkTricksterTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/MerfolkTricksterTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.continuous; import mage.abilities.Abilities; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/SubTypeChangingEffectsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/SubTypeChangingEffectsTest.java index 57ba62045a9e..c7529222c84e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/SubTypeChangingEffectsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/SubTypeChangingEffectsTest.java @@ -472,4 +472,21 @@ public void testMaskwoodNexus2() { } } } + + @Test + public void testMaskwoodNexus3() { + addCard(Zone.BATTLEFIELD, playerB, "Sarkhan the Masterless"); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 3); + addCard(Zone.BATTLEFIELD, playerB, "Maskwood Nexus"); + addCard(Zone.BATTLEFIELD, playerA, "Bonebreaker Giant"); + + attack(1, playerA, "Bonebreaker Giant"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertDamageReceived(playerA, "Bonebreaker Giant", 3); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/control/GainControlTargetEffectTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/control/GainControlTargetEffectTest.java index 9fef6bc86262..02061624875c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/control/GainControlTargetEffectTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/control/GainControlTargetEffectTest.java @@ -93,7 +93,7 @@ public void testKeepControlOfMutavault() { // {1}: Mutavault becomes a 2/2 creature with all creature types until end of turn. It's still a land. addCard(Zone.BATTLEFIELD, playerB, "Mutavault", 1); - activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{1}: Until end of turn {this} becomes"); + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{1}:"); activateAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "{2}, {T}: Gain control", "Mutavault"); setChoice(playerA, "No"); // Don't untap the Shackles diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/AlteredEgoTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/AlteredEgoTest.java index 6c3196aecf81..9d8f74121f4d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/AlteredEgoTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/AlteredEgoTest.java @@ -1,4 +1,3 @@ - package org.mage.test.cards.copy; import mage.constants.PhaseStep; @@ -7,7 +6,6 @@ import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class AlteredEgoTest extends CardTestPlayerBase { @@ -24,9 +22,13 @@ public void testAddingTheCounters() { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Altered Ego"); setChoice(playerA, "X=3"); + setChoice(playerA, "Yes"); // use copy + setChoice(playerA, "Silvercoat Lion"); // copy target + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, "Silvercoat Lion", 1); assertPowerToughness(playerA, "Silvercoat Lion", 5, 5); @@ -42,9 +44,12 @@ public void testNoCreatureToCopyAvailable() { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Altered Ego"); setChoice(playerA, "X=3"); + setChoice(playerA, "Yes"); // use copy (but no targets for copy) + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, "Altered Ego", 0); assertGraveyardCount(playerA, "Altered Ego", 1); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CopyCreatureCardToTokenImplTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CopyCreatureCardToTokenImplTest.java index 7d04dff463f6..b70f35e2dd1f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CopyCreatureCardToTokenImplTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CopyCreatureCardToTokenImplTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.copy; import mage.constants.CardType; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CopySpellTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CopySpellTest.java index 643a3d5c978f..4dc44d1b3c8b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CopySpellTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CopySpellTest.java @@ -1,12 +1,23 @@ package org.mage.test.cards.copy; +import mage.abilities.MageSingleton; import mage.abilities.keyword.FlyingAbility; +import mage.cards.AdventureCard; +import mage.cards.Card; +import mage.cards.ModalDoubleFacesCard; +import mage.cards.SplitCard; +import mage.cards.repository.CardRepository; import mage.constants.PhaseStep; import mage.constants.Zone; +import mage.util.CardUtil; +import org.junit.Assert; import org.junit.Test; import org.mage.test.player.TestPlayer; import org.mage.test.serverside.base.CardTestPlayerBase; +import java.util.Set; +import java.util.UUID; + /** * @author LevelX2 */ @@ -55,24 +66,31 @@ public void ZadaHedronGrinderBoost() { // Target creature gets +3/+3 and gains flying until end of turn. addCard(Zone.HAND, playerA, "Angelic Blessing", 1); // {2}{W} addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + // + // Whenever you cast an instant or sorcery spell that targets only Zada, Hedron Grinder, + // copy that spell for each other creature you control that the spell could target. + // Each copy targets a different one of those creatures. + addCard(Zone.BATTLEFIELD, playerA, "Zada, Hedron Grinder", 1); // 3/3 + // + addCard(Zone.BATTLEFIELD, playerA, "Pillarfield Ox", 1); // 2/4 + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); // 2/2 - // Whenever you cast an instant or sorcery spell that targets only Zada, Hedron Grinder, copy that spell for each other creature you control that the spell could target. Each copy targets a different one of those creatures. - addCard(Zone.BATTLEFIELD, playerA, "Zada, Hedron Grinder", 1); - addCard(Zone.BATTLEFIELD, playerA, "Pillarfield Ox", 1); - - addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); - + // cast boost and copy it for another target (lion will not get boost cause can't be targeted) castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Angelic Blessing", "Zada, Hedron Grinder"); + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertGraveyardCount(playerA, "Angelic Blessing", 1); - assertPowerToughness(playerA, "Pillarfield Ox", 5, 7); - assertAbility(playerA, "Pillarfield Ox", FlyingAbility.getInstance(), true); - assertPowerToughness(playerA, "Zada, Hedron Grinder", 6, 6); + // original target + assertPowerToughness(playerA, "Zada, Hedron Grinder", 3 + 3, 3 + 3); assertAbility(playerA, "Zada, Hedron Grinder", FlyingAbility.getInstance(), true); - + // copied target + assertPowerToughness(playerA, "Pillarfield Ox", 2 + 3, 4 + 3); + assertAbility(playerA, "Pillarfield Ox", FlyingAbility.getInstance(), true); + // can't target lion, so no boost assertPowerToughness(playerB, "Silvercoat Lion", 2, 2); assertAbility(playerB, "Silvercoat Lion", FlyingAbility.getInstance(), false); } @@ -205,8 +223,8 @@ public void ZadaHedronSilverfurPartisan() { assertPermanentCount(playerA, "Wolf", 1); // created from Silverfur ability } */ - - + + @Test public void ZadaHedronGrinderBoostWithCharm() { // Choose two - @@ -404,4 +422,231 @@ public void testSevinnesReclamation() { assertPermanentCount(playerA, "Mountain", 1); assertPermanentCount(playerA, "Island", 1); } + + @Test + public void test_AllowsMultipleInstancesOfGainedTriggers() { + // bug: multiple copies of Imoti, Celebrant of Bounty only giving cascade once + // reason: gained ability used same id, so only one trigger were possible (now it uses new ids) + removeAllCardsFromHand(playerA); + removeAllCardsFromLibrary(playerA); + skipInitShuffling(); + + // Spells you cast with converted mana cost 6 or greater have cascade. + // Cascade + // (When you cast this spell exile cards from the top of your library until you exile a + // nonland card whose converted mana cost is less than this spell's converted mana cost. You may cast + // that spell without paying its mana cost if its converted mana cost is less than this spell's + // converted mana cost. Then put all cards exiled this way that weren't cast on the bottom of + // your library in a random order.) + addCard(Zone.BATTLEFIELD, playerA, "Imoti, Celebrant of Bounty", 1); // {3}{G}{U} + // + addCard(Zone.LIBRARY, playerA, "Swamp", 1); + addCard(Zone.LIBRARY, playerA, "Lightning Bolt", 1); + addCard(Zone.LIBRARY, playerA, "Swamp", 1); + addCard(Zone.LIBRARY, playerA, "Lightning Bolt", 1); + addCard(Zone.LIBRARY, playerA, "Swamp", 1); + // + // You may have Spark Double enter the battlefield as a copy of a creature or planeswalker you control, + // except it enters with an additional +1/+1 counter on it if it’s a creature, it enters with an + // additional loyalty counter on it if it’s a planeswalker, and it isn’t legendary if that + // permanent is legendary. + addCard(Zone.HAND, playerA, "Spark Double", 1); // {3}{U} + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + // + addCard(Zone.HAND, playerA, "Alpha Tyrranax", 1); // {4}{G}{G} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); + + // cast spark and make imoti's copy + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Spark Double"); + setChoice(playerA, "Yes"); // use copy + setChoice(playerA, "Imoti, Celebrant of Bounty"); // copy of imoti + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("after copy", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Imoti, Celebrant of Bounty", 2); + + // cast big spell and catch cascade 2x times (from two copies) + // possible bug: cascade activates only 1x times + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alpha Tyrranax"); + checkStackSize("afer big spell", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 3); + setChoice(playerA, "cascade"); // choice between 2x gained cascades + setChoice(playerA, "Yes"); // cast first bolt by first cascade + addTarget(playerA, playerB); // target for first bolt + setChoice(playerA, "Yes"); // cast second bold by second cascade + addTarget(playerA, playerB); // target for second bolt + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertLife(playerB, 20 - 3 * 2); // 2x bolts from 2x cascades + } + + @Test + public void test_CopiedSpellsMustUseIndependentCards() { + // possible bug: copied spell on stack depends on the original spell/card + // https://github.com/magefree/mage/issues/7634 + + // Return any number of cards with different converted mana costs from your graveyard to your hand. + // Put Seasons Past on the bottom of its owner's library. + addCard(Zone.HAND, playerA, "Seasons Past", 1); // {4}{G}{G} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); + // + addCard(Zone.GRAVEYARD, playerA, "Grizzly Bears", 5); // for return + // + // Copy target instant or sorcery spell, except that the copy is red. You may choose new targets for the copy. + addCard(Zone.HAND, playerA, "Fork", 1); // {R}{R} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + + // cast season and make copy of it on stack + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 6); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Seasons Past"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fork", "Seasons Past", "Cast Seasons Past"); + checkStackSize("after copy cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2); // season + fork + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true); + checkStackSize("after copy resolve", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2); // season + copied season + checkStackObject("after copy resolve", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Seasons Past", 2); + + // resolve copied season (possible bug: after copied resolve it will return an original card too, so original spell will be fizzled) + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA, true); + setChoice(playerA, "Grizzly Bears"); // return to hand + checkStackSize("after copied resolve", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 1); + + // resolve original season + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA, true); + setChoice(playerA, "Grizzly Bears"); // return to hand + checkStackSize("after original resolve", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 0); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_SimpleCopy_Card() { + Card sourceCard = CardRepository.instance.findCard("Grizzly Bears").getCard(); + Card originalCard = CardRepository.instance.findCard("Grizzly Bears").getCard(); + prepareZoneAndZCC(originalCard); + Card copiedCard = currentGame.copyCard(originalCard, null, playerA.getId()); + // main + Assert.assertNotEquals("main - id must be different", originalCard.getId(), copiedCard.getId()); + Assert.assertEquals("main - rules must be same", originalCard.getRules(), copiedCard.getRules()); + abilitySourceMustBeSame(sourceCard, "main source"); + abilitySourceMustBeSame(originalCard, "main original"); // original card can be broken after copyCard call + abilitySourceMustBeSame(copiedCard, "main copied"); + //cardsMustHaveSameZoneAndZCC(originalCard, copiedCard, "main"); + } + + @Test + public void test_SimpleCopy_SplitCard() { + SplitCard sourceCard = (SplitCard) CardRepository.instance.findCard("Alive // Well").getCard(); + SplitCard originalCard = (SplitCard) CardRepository.instance.findCard("Alive // Well").getCard(); + prepareZoneAndZCC(originalCard); + SplitCard copiedCard = (SplitCard) currentGame.copyCard(originalCard, null, playerA.getId()); + // main + Assert.assertNotEquals("main - id must be different", originalCard.getId(), copiedCard.getId()); + Assert.assertEquals("main - rules must be same", originalCard.getRules(), copiedCard.getRules()); + abilitySourceMustBeSame(sourceCard, "main source"); + abilitySourceMustBeSame(originalCard, "main original"); + abilitySourceMustBeSame(copiedCard, "main copied"); + //cardsMustHaveSameZoneAndZCC(originalCard, copiedCard, "main"); + // left + Assert.assertNotEquals("left - id must be different", originalCard.getLeftHalfCard().getId(), copiedCard.getLeftHalfCard().getId()); + Assert.assertEquals("left - rules must be same", originalCard.getLeftHalfCard().getRules(), copiedCard.getLeftHalfCard().getRules()); + Assert.assertEquals("left - parent ref", copiedCard.getLeftHalfCard().getParentCard().getId(), copiedCard.getId()); + abilitySourceMustBeSame(originalCard.getLeftHalfCard(), "left original"); + abilitySourceMustBeSame(copiedCard.getLeftHalfCard(), "left copied"); + //cardsMustHaveSameZoneAndZCC(originalCard.getLeftHalfCard(), copiedCard.getLeftHalfCard(), "left"); + // right + Assert.assertNotEquals("right - id must be different", originalCard.getRightHalfCard().getId(), copiedCard.getRightHalfCard().getId()); + Assert.assertEquals("right - rules must be same", originalCard.getRightHalfCard().getRules(), copiedCard.getRightHalfCard().getRules()); + Assert.assertEquals("right - parent ref", copiedCard.getRightHalfCard().getParentCard().getId(), copiedCard.getId()); + abilitySourceMustBeSame(originalCard.getRightHalfCard(), "right original"); + abilitySourceMustBeSame(copiedCard.getRightHalfCard(), "right copied"); + //cardsMustHaveSameZoneAndZCC(originalCard.getRightHalfCard(), copiedCard.getRightHalfCard(), "right"); + } + + @Test + public void test_SimpleCopy_AdventureCard() { + AdventureCard sourceCard = (AdventureCard) CardRepository.instance.findCard("Animating Faerie").getCard(); + AdventureCard originalCard = (AdventureCard) CardRepository.instance.findCard("Animating Faerie").getCard(); + prepareZoneAndZCC(originalCard); + AdventureCard copiedCard = (AdventureCard) currentGame.copyCard(originalCard, null, playerA.getId()); + // main + Assert.assertNotEquals("main - id must be different", originalCard.getId(), copiedCard.getId()); + Assert.assertEquals("main - rules must be same", originalCard.getRules(), copiedCard.getRules()); + abilitySourceMustBeSame(sourceCard, "main source"); + abilitySourceMustBeSame(originalCard, "main original"); + abilitySourceMustBeSame(copiedCard, "main copied"); + //cardsMustHaveSameZoneAndZCC(originalCard, copiedCard, "main"); + // right (spell) + Assert.assertNotEquals("right - id must be different", originalCard.getSpellCard().getId(), copiedCard.getSpellCard().getId()); + Assert.assertEquals("right - rules must be same", originalCard.getSpellCard().getRules(), copiedCard.getSpellCard().getRules()); + Assert.assertEquals("right - parent ref", copiedCard.getSpellCard().getParentCard().getId(), copiedCard.getId()); + abilitySourceMustBeSame(originalCard.getSpellCard(), "right original"); + abilitySourceMustBeSame(copiedCard.getSpellCard(), "right copied"); + //cardsMustHaveSameZoneAndZCC(originalCard.getSpellCard(), copiedCard.getSpellCard(), "right"); + } + + @Test + public void test_SimpleCopy_MDFC() { + ModalDoubleFacesCard sourceCard = (ModalDoubleFacesCard) CardRepository.instance.findCard("Agadeem's Awakening").getCard(); + ModalDoubleFacesCard originalCard = (ModalDoubleFacesCard) CardRepository.instance.findCard("Agadeem's Awakening").getCard(); + prepareZoneAndZCC(originalCard); + ModalDoubleFacesCard copiedCard = (ModalDoubleFacesCard) currentGame.copyCard(originalCard, null, playerA.getId()); + // main + Assert.assertNotEquals("main - id must be different", originalCard.getId(), copiedCard.getId()); + Assert.assertEquals("main - rules must be same", originalCard.getRules(), copiedCard.getRules()); + abilitySourceMustBeSame(sourceCard, "main source"); + abilitySourceMustBeSame(originalCard, "main original"); + abilitySourceMustBeSame(copiedCard, "main copied"); + //cardsMustHaveSameZoneAndZCC(originalCard, copiedCard, "main"); + // left + Assert.assertNotEquals("left - id must be different", originalCard.getLeftHalfCard().getId(), copiedCard.getLeftHalfCard().getId()); + Assert.assertEquals("left - rules must be same", originalCard.getLeftHalfCard().getRules(), copiedCard.getLeftHalfCard().getRules()); + Assert.assertEquals("left - parent ref", copiedCard.getLeftHalfCard().getParentCard().getId(), copiedCard.getId()); + abilitySourceMustBeSame(originalCard.getLeftHalfCard(), "left original"); + abilitySourceMustBeSame(copiedCard.getLeftHalfCard(), "left copied"); + //cardsMustHaveSameZoneAndZCC(originalCard.getLeftHalfCard(), copiedCard.getLeftHalfCard(), "left"); + // right + Assert.assertNotEquals("right - id must be different", originalCard.getRightHalfCard().getId(), copiedCard.getRightHalfCard().getId()); + Assert.assertEquals("right - rules must be same", originalCard.getRightHalfCard().getRules(), copiedCard.getRightHalfCard().getRules()); + Assert.assertEquals("right - parent ref", copiedCard.getRightHalfCard().getParentCard().getId(), copiedCard.getId()); + abilitySourceMustBeSame(originalCard.getRightHalfCard(), "right original"); + abilitySourceMustBeSame(copiedCard.getRightHalfCard(), "right copied"); + //cardsMustHaveSameZoneAndZCC(originalCard.getRightHalfCard(), copiedCard.getRightHalfCard(), "right"); + } + + private void abilitySourceMustBeSame(Card card, String infoPrefix) { + Set partIds = CardUtil.getObjectParts(card); + + card.getAbilities(currentGame).forEach(ability -> { + // ability can refs to part or main card only + if (!partIds.contains(ability.getSourceId())) { + if (ability instanceof MageSingleton) { + // sourceId don't work with MageSingleton abilities + return; + } + + Assert.fail(infoPrefix + " - " + "ability source must be same: " + ability.toString()); + } + }); + } + + private void prepareZoneAndZCC(Card originalCard) { + // prepare custom zcc and zone for copy testing + originalCard.setZoneChangeCounter(5, currentGame); + originalCard.setZone(Zone.STACK, currentGame); + } + + private void cardsMustHaveSameZoneAndZCC(Card originalCard, Card copiedCard, String infoPrefix) { + // zcc and zone are not copied, so you don't need it here yet + Zone zone1 = currentGame.getState().getZone(originalCard.getId()); + Zone zone2 = currentGame.getState().getZone(copiedCard.getId()); + int zcc1 = currentGame.getState().getZoneChangeCounter(originalCard.getId()); + int zcc2 = currentGame.getState().getZoneChangeCounter(copiedCard.getId()); + if (zone1 != zone2 || zcc1 != zcc2) { + Assert.fail(infoPrefix + " - " + "cards must have same zone and zcc: " + zcc1 + " - " + zone1 + " != " + zcc2 + " - " + zone2); + } + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java index 12e66f80fb14..a38051841fa3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java @@ -292,7 +292,7 @@ public void testCopiedSteelHellkite() { attack(4, playerB, "Steel Hellkite"); - activateAbility(4, PhaseStep.POSTCOMBAT_MAIN, playerB, "{X}: Destroy each nonland permanent with converted mana cost X whose controller was dealt combat damage by {this} this turn. Activate this ability only once each turn."); + activateAbility(4, PhaseStep.POSTCOMBAT_MAIN, playerB, "{X}:"); setChoice(playerB, "X=0"); setStopAt(4, PhaseStep.END_TURN); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/SparkDoubleTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/SparkDoubleTest.java index c624bb7fb1d7..7164314b83ad 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/SparkDoubleTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/SparkDoubleTest.java @@ -244,4 +244,46 @@ public void test_CopyOfSparksCopy_ByAbility() { assertAllCommandsUsed(); } + @Test + public void test_SparkCopyEachOther() { + // rules: + // 706.9e Some replacement effects that generate copy effects include an exception that’s an + // additional effect rather than a modification of the affected object’s characteristics. + // If another copy effect is applied to that object after applying the copy effect with that + // exception, the exception’s effect doesn’t happen. + // Example: Altered Ego reads, “You may have Altered Ego enter the battlefield as a copy of any + // creature on the battlefield, except it enters with X additional +1/+1 counters on it.” You + // choose for it to enter the battlefield as a copy of Clone, which reads “You may have Clone + // enter the battlefield as a copy of any creature on the battlefield,” for which no creature + // was chosen as it entered the battlefield. If you then choose a creature to copy as you apply + // the replacement effect Altered Ego gains by copying Clone, Altered Ego’s replacement effect + // won’t cause it to enter the battlefield with any +1/+1 counters on it. + + addCard(Zone.HAND, playerA, "Spark Double", 2); // {3}{U} + addCard(Zone.BATTLEFIELD, playerA, "Island", 4 * 2); + // + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); + + // cast first spark + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Spark Double"); + setChoice(playerA, "Yes"); + setChoice(playerA, "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("after 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 2); + + // cast second spark + // rules 706.9e affected, so must get only 1 counter + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Spark Double"); + setChoice(playerA, "Yes"); + setChoice(playerA, "Grizzly Bears[only copy]"); + //setChoice(playerA, "Grizzly Bears"); // possible bug: two etb effects + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("after 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 3); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/VolrathsShapshifterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/VolrathsShapshifterTest.java index 904dfefaf49d..f80194319074 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/VolrathsShapshifterTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/VolrathsShapshifterTest.java @@ -116,7 +116,7 @@ private boolean hasShapeshiftersOriginalAbility(Permanent shapeshifter) { SimpleActivatedAbility simpleActivatedAbility = (SimpleActivatedAbility) ability; return simpleActivatedAbility.getZone() == Zone.BATTLEFIELD && simpleActivatedAbility.getEffects().size() == 1 && simpleActivatedAbility.getEffects().get(0) instanceof DiscardControllerEffect && simpleActivatedAbility.getManaCosts().size() == 1 - && simpleActivatedAbility.getManaCosts().get(0) instanceof GenericManaCost && simpleActivatedAbility.getManaCosts().get(0).convertedManaCost() == 2; + && simpleActivatedAbility.getManaCosts().get(0) instanceof GenericManaCost && simpleActivatedAbility.getManaCosts().get(0).manaValue() == 2; } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/additional/RevealedOrControlledDragonTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/additional/RevealedOrControlledDragonTest.java new file mode 100644 index 000000000000..04a5a806da8f --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/additional/RevealedOrControlledDragonTest.java @@ -0,0 +1,181 @@ +package org.mage.test.cards.cost.additional; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class RevealedOrControlledDragonTest extends CardTestPlayerBase { + private static final String dragon = "Shivan Dragon"; + private static final String roar = "Draconic Roar"; + private static final String lion = "Silvercoat Lion"; + private static final String orator = "Orator of Ojutai"; + private static final String sentinels = "Scaleguard Sentinels"; + + public void addDragonToHand(String cardName) { + addCard(Zone.HAND, playerA, dragon); + addCard(Zone.HAND, playerA, cardName); + } + + public void addDragonToBattlefield(String cardName) { + addCard(Zone.BATTLEFIELD, playerA, dragon); + addCard(Zone.HAND, playerA, cardName); + } + + @Test + public void testRoarHand() { + addDragonToHand(roar); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, lion); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, roar, lion); + setChoice(playerA, dragon); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, lion, 1); + assertGraveyardCount(playerA, roar, 1); + assertLife(playerA, 20 - 3); + } + + @Test + public void testRoarBattlefield() { + addDragonToBattlefield(roar); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, lion); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, roar, lion); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, lion, 1); + assertGraveyardCount(playerA, roar, 1); + assertLife(playerA, 20 - 3); + } + + @Test + public void testRoarFalse() { + addCard(Zone.HAND, playerA, roar); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, lion); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, roar, lion); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, lion, 1); + assertGraveyardCount(playerA, roar, 1); + assertLife(playerA, 20); + } + + @Test + public void testOratorHand() { + addDragonToHand(orator); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, orator); + setChoice(playerA, dragon); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, orator, 1); + assertHandCount(playerA, 1 + 1); + } + + @Test + public void testOratorBattlefield() { + addDragonToBattlefield(orator); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, orator); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, orator, 1); + assertHandCount(playerA, 1); + } + + @Test + public void testOratorFalse() { + addCard(Zone.HAND, playerA, orator); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, orator); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, orator, 1); + assertHandCount(playerA, 0); + } + + @Test + public void testSentinelsHand() { + addDragonToHand(sentinels); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sentinels); + setChoice(playerA, dragon); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, sentinels, 1); + assertCounterCount(playerA, sentinels, CounterType.P1P1, 1); + } + + @Test + public void testSentinelsBattlefield() { + addDragonToBattlefield(sentinels); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sentinels); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, sentinels, 1); + assertCounterCount(playerA, sentinels, CounterType.P1P1, 1); + } + + @Test + public void testSentinelsFalse() { + addCard(Zone.HAND, playerA, sentinels); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sentinels); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, sentinels, 1); + assertCounterCount(playerA, sentinels, CounterType.P1P1, 0); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modaldoublefaces/ModalDoubleFacesCardsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modaldoublefaces/ModalDoubleFacesCardsTest.java index 9aaa2e77fde2..989d2d2ed835 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modaldoublefaces/ModalDoubleFacesCardsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modaldoublefaces/ModalDoubleFacesCardsTest.java @@ -10,6 +10,7 @@ import mage.util.ManaUtil; import org.junit.Assert; import org.junit.Test; +import org.mage.test.player.TestPlayer; import org.mage.test.serverside.base.CardTestPlayerBase; /** @@ -797,4 +798,69 @@ public void test_ETB_OnlyActualSideCardCanRaiseTriggers() { execute(); assertAllCommandsUsed(); } + + @Test + public void test_Cascade_ValkiGodOfLies() { + // https://magic.wizards.com/en/articles/archive/news/february-15-2021-banned-and-restricted-announcement + // For example, if you cast Bloodbraid Elf and exile Valki, God of Lies from your library, + // you'll be able to cast Valki but not Tibalt, Cosmic Impostor. On the other hand, if you + // exile Cosima, God of the Voyage, you may cast either Cosima or The Omenkeel, as each face + // has a lesser converted mana cost than Bloodbraid Elf. + removeAllCardsFromLibrary(playerA); + skipInitShuffling(); + + // Cascade + addCard(Zone.HAND, playerA, "Bloodbraid Elf"); // {2}{R}{G} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + // + addCard(Zone.LIBRARY, playerA, "Swamp", 2); + addCard(Zone.LIBRARY, playerA, "Valki, God of Lies", 1); + addCard(Zone.LIBRARY, playerA, "Island", 2); + + // play elf with cascade + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bloodbraid Elf"); + setChoice(playerA, "Yes"); // use free cast + //setChoice(playerA, "Cast Valki, God of Lies"); possible bug: you can see two spell abilities to choose, but only one allows here + setChoice(playerA, TestPlayer.CHOICE_SKIP); // no choices for valki's etb exile + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Valki, God of Lies", 1); + } + + @Test + public void test_Cascade_CosimaGodOfTheVoyage() { + // https://magic.wizards.com/en/articles/archive/news/february-15-2021-banned-and-restricted-announcement + // For example, if you cast Bloodbraid Elf and exile Valki, God of Lies from your library, + // you'll be able to cast Valki but not Tibalt, Cosmic Impostor. On the other hand, if you + // exile Cosima, God of the Voyage, you may cast either Cosima or The Omenkeel, as each face + // has a lesser converted mana cost than Bloodbraid Elf. + removeAllCardsFromLibrary(playerA); + skipInitShuffling(); + + // Cascade + addCard(Zone.HAND, playerA, "Bloodbraid Elf"); // {2}{R}{G} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + // + addCard(Zone.LIBRARY, playerA, "Swamp", 2); + addCard(Zone.LIBRARY, playerA, "Cosima, God of the Voyage", 1); + addCard(Zone.LIBRARY, playerA, "Island", 2); + + // play elf with cascade + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bloodbraid Elf"); + setChoice(playerA, "Yes"); // use free cast + setChoice(playerA, "Cast The Omenkeel"); // can cast any side here + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "The Omenkeel", 1); + } } \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceTest.java index ee506ea8387d..f87e17b1d5e7 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceTest.java @@ -37,6 +37,8 @@ public void test_Monohybrid() { Assert.assertEquals("test mono hybrid have variant generic", "{1/R}", testCost.getText()); testReduce("{5/R}", 0, "{5/R}"); // ensure that mono hybrid in test mode + // mana order must be same (e.g. cost {R}{2}{G} must be reduced to {R}{1}{G}) + // DECREASE COST // colorless is not reduce @@ -73,16 +75,29 @@ public void test_Monohybrid() { testReduce("{R}{3}{G}", 2, "{R}{1}{G}"); testReduce("{R}{G}{3}", 2, "{R}{G}{1}"); + // multi generics, decrease cost by 2 (you can't get multigeneric in real game example) + testReduce("{2}{2}", 2, "{2}"); + testReduce("{3}{3}", 2, "{1}{3}"); + testReduce("{3}{R}{3}", 2, "{1}{R}{3}"); + testReduce("{3}{R}{3}{G}", 2, "{1}{R}{3}{G}"); + testReduce("{R}{3}{G}{3}", 2, "{R}{1}{G}{3}"); + // + testReduce("{2}{2}", 3, "{1}"); + testReduce("{3}{3}", 3, "{3}"); + testReduce("{3}{R}{3}", 5, "{R}{1}"); + testReduce("{3}{R}{3}{G}", 5, "{R}{1}{G}"); + testReduce("{R}{3}{G}{3}", 5, "{R}{G}{1}"); + // INCREASE COST // colorless, increase cost by 1 - testReduce("{C}", -1, "{C}{1}"); - testReduce("{C}{G}", -1, "{C}{G}{1}"); + testReduce("{C}", -1, "{1}{C}"); + testReduce("{C}{G}", -1, "{1}{C}{G}"); // 0 generic, increase cost by 1 testReduce("", -1, "{1}"); - testReduce("{R}", -1, "{R}{1}"); - testReduce("{R}{G}", -1, "{R}{G}{1}"); + testReduce("{R}", -1, "{1}{R}"); + testReduce("{R}{G}", -1, "{1}{R}{G}"); // 1 generic, increase cost by 1 testReduce("{1}", -1, "{2}"); @@ -123,8 +138,8 @@ public void test_Monohybrid() { testReduce("{2/R}", 1, "{1/R}"); testReduce("{2/R}{2/G}", 1, "{1/R}{2/G}"); // TODO: add or/or reduction? (see https://github.com/magefree/mage/issues/6130 ) // mono hybrid, increase cost by 1 - testReduce("{2/R}", -1, "{2/R}{1}"); - testReduce("{2/R}{2/G}", -1, "{2/R}{2/G}{1}"); + testReduce("{2/R}", -1, "{1}{2/R}"); + testReduce("{2/R}{2/G}", -1, "{1}{2/R}{2/G}"); // generic, normal amount // mono hybrid + 1 generic, decrease cost by 1 diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/FerventChampionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/FerventChampionTest.java index 974e818df228..2370ee55c171 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/FerventChampionTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/FerventChampionTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.cost.modification; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/splitcards/SplitCardsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/splitcards/SplitCardsTest.java index 7f6ec618afbd..5566dc54f4c6 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/splitcards/SplitCardsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/splitcards/SplitCardsTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.cost.splitcards; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/damage/ExcessDamageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/damage/ExcessDamageTest.java new file mode 100644 index 000000000000..b0ab32122394 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/damage/ExcessDamageTest.java @@ -0,0 +1,139 @@ +package org.mage.test.cards.damage; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class ExcessDamageTest extends CardTestPlayerBase { + + private static final String spill = "Flame Spill"; + private static final String bear = "Grizzly Bears"; + private static final String jab = "Flame Jab"; + private static final String spirit = "Pestilent Spirit"; + private static final String myr = "Darksteel Myr"; + private static final String gideon = "Gideon Jura"; + private static final String leyline = "Leyline of Punishment"; + private static final String bolt = "Lightning Bolt"; + private static final String aegar = "Aegar, the Freezing Flame"; + + @Test + public void testExcessDamageRegular() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerA, bear); + addCard(Zone.HAND, playerA, spill); + + setStrictChooseMode(true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, spill, bear); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, bear, 1); + assertPermanentCount(playerA, bear, 0); + assertLife(playerA, 20 - 2); + } + + @Test + public void testExcessDamageAlreadyDamaged() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.BATTLEFIELD, playerA, bear); + addCard(Zone.HAND, playerA, spill); + addCard(Zone.HAND, playerA, jab); + + setStrictChooseMode(true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, jab, bear); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, spill, bear); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, bear, 1); + assertPermanentCount(playerA, bear, 0); + assertLife(playerA, 20 - 3); + } + + @Test + public void testExcessDamageDeathtouch() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerA, bear); + addCard(Zone.BATTLEFIELD, playerA, spirit); + addCard(Zone.HAND, playerA, spill); + + setStrictChooseMode(true); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, spill, bear); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, bear, 1); + assertPermanentCount(playerA, bear, 0); + assertLife(playerA, 20 - 3); + } + + @Test + public void testExcessDamageIndestructible() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerA, myr); + addCard(Zone.HAND, playerA, spill); + + setStrictChooseMode(true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, spill, myr); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, myr, 0); + assertPermanentCount(playerA, myr, 1); + assertLife(playerA, 20 - 3); + } + + @Test + public void testExcessDamagePlaneswalker() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.BATTLEFIELD, playerA, gideon); + addCard(Zone.BATTLEFIELD, playerA, leyline); + addCard(Zone.HAND, playerA, spill); + addCard(Zone.HAND, playerA, bolt); + + setStrictChooseMode(true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, gideon); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "0:"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, spill, gideon); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, gideon, 1); + assertPermanentCount(playerA, gideon, 0); + assertLife(playerA, 20 - 1); + } + + @Test + public void testAegarTheFreezingFlame() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, aegar); + addCard(Zone.HAND, playerA, bolt); + addCard(Zone.BATTLEFIELD, playerB, bear); + + setStrictChooseMode(true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, bear); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertHandCount(playerA, 1); + assertGraveyardCount(playerA, bolt, 1); + assertGraveyardCount(playerB, bear, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/AuraMovingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/AuraMovingTest.java index ac4fc5a64dea..4aaf1bb6820f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/AuraMovingTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/AuraMovingTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.enchantments; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/AuraTargetRemovedTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/AuraTargetRemovedTest.java index 4e46099f9b80..b3f15e734464 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/AuraTargetRemovedTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/AuraTargetRemovedTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.enchantments; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/MindsDilationTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/MindsDilationTest.java index 5088a35ffdd9..269329ff6696 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/MindsDilationTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/MindsDilationTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.enchantments; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/facedown/BaneAlleyBrokerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/facedown/BaneAlleyBrokerTest.java index d8f254f899e9..d02e05846099 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/facedown/BaneAlleyBrokerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/facedown/BaneAlleyBrokerTest.java @@ -17,7 +17,6 @@ public class BaneAlleyBrokerTest extends CardTestPlayerBase { * then exile a card from your hand face down. You may look at cards exiled * with Bane Alley Broker. {U}{B}, {T}: Return a card exiled with Bane Alley * Broker to its owner's hand. - * */ // test that cards exiled using Bane Alley Broker are face down @Test @@ -28,11 +27,13 @@ public void testBaneAlleyBroker() { addCard(Zone.BATTLEFIELD, playerA, "Island"); addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Draw a card, then exile a card from your hand face down."); + setStrictChooseMode(true); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}"); addTarget(playerA, "Goblin Roughrider"); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertHandCount(playerA, 2); assertHandCount(playerA, "Sejiri Merfolk", 1); @@ -45,7 +46,32 @@ public void testBaneAlleyBroker() { Assert.assertTrue("Exiled card is not face down", card.isFaceDown(currentGame)); } } + } + + @Test + public void testBaneAlleyBrokerReturn() { + addCard(Zone.BATTLEFIELD, playerA, "Bane Alley Broker"); + addCard(Zone.HAND, playerA, "Goblin Roughrider"); + addCard(Zone.HAND, playerA, "Sejiri Merfolk"); + addCard(Zone.BATTLEFIELD, playerA, "Island"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + + setStrictChooseMode(true); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}"); + addTarget(playerA, "Goblin Roughrider"); + + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{U}{B}"); + addTarget(playerA, "Goblin Roughrider"); + + setStopAt(3, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertHandCount(playerA, 4); + assertHandCount(playerA, "Sejiri Merfolk", 1); + assertHandCount(playerA, "Goblin Roughrider", 1); + assertExileCount("Goblin Roughrider", 0); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/HarvestMageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/HarvestMageTest.java index e4036fd280f2..b310add09c4c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/mana/HarvestMageTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/HarvestMageTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.mana; import mage.abilities.mana.ManaOptions; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/HarvesterDruidTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/HarvesterDruidTest.java index 95cbd92c19c6..cf083a2fbd6a 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/mana/HarvesterDruidTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/HarvesterDruidTest.java @@ -4,19 +4,18 @@ import mage.constants.PhaseStep; import mage.constants.Zone; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; -import static org.mage.test.utils.ManaOptionsTestUtils.*; + +import static org.mage.test.utils.ManaOptionsTestUtils.assertDuplicatedManaOptions; +import static org.mage.test.utils.ManaOptionsTestUtils.assertManaOptions; /** - * * @author LevelX2, JayDi85 */ public class HarvesterDruidTest extends CardTestPlayerBase { @Test - @Ignore public void testOneInstance() { addCard(Zone.BATTLEFIELD, playerA, "Island", 1); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); @@ -35,7 +34,6 @@ public void testOneInstance() { } @Test - @Ignore public void testTwoInstances() { addCard(Zone.BATTLEFIELD, playerA, "Island", 1); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/SylvokExplorerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/SylvokExplorerTest.java index e48aeed31221..14029b17036e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/mana/SylvokExplorerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/SylvokExplorerTest.java @@ -4,18 +4,15 @@ import mage.constants.PhaseStep; import mage.constants.Zone; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class SylvokExplorerTest extends CardTestPlayerBase { @Test - @Ignore public void testOneInstance() { addCard(Zone.BATTLEFIELD, playerB, "Island", 1); addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); @@ -44,11 +41,11 @@ public void testTwoInstances() { addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); setStopAt(1, PhaseStep.PRECOMBAT_MAIN); - + setStrictChooseMode(true); execute(); assertAllCommandsUsed(); - + ManaOptions options = playerA.getAvailableManaTest(currentGame); Assert.assertEquals("Player should be able to create 3 white mana", "{W}{W}{W}", options.get(0).toString()); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/VorinclexVoiceOfHungerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/VorinclexVoiceOfHungerTest.java index 8a2250605898..b3487e42f0f6 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/mana/VorinclexVoiceOfHungerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/VorinclexVoiceOfHungerTest.java @@ -4,12 +4,10 @@ import mage.constants.PhaseStep; import mage.constants.Zone; import mage.counters.CounterType; -import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class VorinclexVoiceOfHungerTest extends CardTestPlayerBase { @@ -91,7 +89,6 @@ public void testGemstoneCavern() { } @Test - @Ignore public void testCastWithGemstoneCavern() { // Trample // Whenever you tap a land for mana, add one mana of any type that land produced. diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/modal/OneOrMoreTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/modal/OneOrMoreTest.java index 2160bc9bc5f5..e8160237bf81 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/modal/OneOrMoreTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/modal/OneOrMoreTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.modal; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/GideonTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/GideonTest.java index a8302a41b644..bf4b85a803fa 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/GideonTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/GideonTest.java @@ -1,6 +1,7 @@ package org.mage.test.cards.planeswalker; import mage.abilities.keyword.IndestructibleAbility; +import mage.constants.CardType; import mage.constants.PhaseStep; import mage.constants.Zone; import mage.counters.CounterType; @@ -139,4 +140,45 @@ public void testGideonBattleForgedSacrifice() { assertPermanentCount(playerB, "Gideon, Battle-Forged", 0); assertGraveyardCount(playerB, "Kytheon, Hero of Akros", 1); } + + @Test + public void testGideonJuraNoPrevention() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Gideon Jura"); + addCard(Zone.BATTLEFIELD, playerA, "Leyline of Punishment"); + addCard(Zone.HAND, playerA, "Lightning Bolt"); + + setStrictChooseMode(true); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "0:"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Gideon Jura"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertType("Gideon Jura", CardType.CREATURE, true); + assertDamageReceived(playerA, "Gideon Jura", 3); + assertCounterCount(playerA, "Gideon Jura", CounterType.LOYALTY, 3); + } + + @Test + public void testGideonJuraNoPreventionCombat() { + addCard(Zone.BATTLEFIELD, playerA, "Gideon Jura"); + addCard(Zone.BATTLEFIELD, playerA, "Leyline of Punishment"); + addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears"); + + setStrictChooseMode(true); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "0:"); + attack(1, playerA, "Gideon Jura", playerB); + block(1, playerB, "Grizzly Bears", "Gideon Jura"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertType("Gideon Jura", CardType.CREATURE, true); + assertDamageReceived(playerA, "Gideon Jura", 2); + assertCounterCount(playerA, "Gideon Jura", CounterType.LOYALTY, 4); + assertGraveyardCount(playerB, "Grizzly Bears", 1); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/LilianaDefiantNecromancerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/LilianaDefiantNecromancerTest.java index 80fd554a8e80..00ce1f60febd 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/LilianaDefiantNecromancerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/LilianaDefiantNecromancerTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.planeswalker; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/RedirectDamageToPlaneswalkerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/RedirectDamageToPlaneswalkerTest.java deleted file mode 100644 index e38da1cadec4..000000000000 --- a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/RedirectDamageToPlaneswalkerTest.java +++ /dev/null @@ -1,46 +0,0 @@ - -package org.mage.test.cards.planeswalker; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import mage.counters.CounterType; -import org.junit.Ignore; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class RedirectDamageToPlaneswalkerTest extends CardTestPlayerBase { - - @Ignore - @Test - public void testDirectDamage() { - // +2: Look at the top card of target player's library. You may put that card on the bottom of that player's library. - // 0: Draw three cards, then put two cards from your hand on top of your library in any order. - // −1: Return target creature to its owner's hand. - addCard(Zone.BATTLEFIELD, playerA, "Jace, the Mind Sculptor"); // starts with 3 Loyality counters - - addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); - addCard(Zone.HAND, playerB, "Lightning Bolt"); - - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+2:", playerB); - - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", playerA); - setChoice(playerB, "Yes"); - - setStopAt(1, PhaseStep.END_TURN); - execute(); - - assertPermanentCount(playerA, "Jace, the Mind Sculptor", 1); - assertCounterCount("Jace, the Mind Sculptor", CounterType.LOYALTY, 2); // 3 + 2 - 3 = 2 - - assertGraveyardCount(playerB, "Lightning Bolt", 1); - - assertLife(playerA, 20); - assertLife(playerB, 20); - - } - -} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/UginTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/UginTest.java index f4ada32f0161..9bfa63818a7f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/UginTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/UginTest.java @@ -41,7 +41,7 @@ public void testCard() { attack(3, playerA, "Silvercoat Lion"); block(3, playerB, "Ashaya, the Awoken World", "Silvercoat Lion"); - activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "-X: Exile each permanent with converted mana cost X or less that's one or more colors"); + activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "-X: Exile each permanent with mana value X or less that's one or more colors"); setChoice(playerA, "X=3"); setStopAt(3, PhaseStep.END_TURN); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/VivienTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/VivienTest.java index dccf3a7ee10b..46a261638bb1 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/VivienTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/VivienTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.planeswalker; import mage.abilities.keyword.TrampleAbility; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/protection/EightAndAHalfTailsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/protection/EightAndAHalfTailsTest.java index c5f83d2096ee..4fd955ec87fe 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/protection/EightAndAHalfTailsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/protection/EightAndAHalfTailsTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.protection; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/ClergyOfTheHolyNimbusTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/ClergyOfTheHolyNimbusTest.java index ad2ee133e42d..e5c6c2950f26 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/ClergyOfTheHolyNimbusTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/ClergyOfTheHolyNimbusTest.java @@ -2,12 +2,10 @@ import mage.constants.PhaseStep; import mage.constants.Zone; -import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ public class ClergyOfTheHolyNimbusTest extends CardTestPlayerBase { @@ -20,15 +18,15 @@ public void testBasicRegeneration() { addCard(Zone.BATTLEFIELD, playerB, "Clergy of the Holy Nimbus"); // {W} 1/1 addCard(Zone.HAND, playerA, "Doom Blade"); // {1}{B} destroy target non-black creature addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); - + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Doom Blade", "Clergy of the Holy Nimbus"); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); - + assertGraveyardCount(playerA, "Doom Blade", 1); assertPermanentCount(playerB, "Clergy of the Holy Nimbus", 1); } - + @Test public void testCannotBeRegeneratedSpell() { @@ -37,31 +35,30 @@ public void testCannotBeRegeneratedSpell() { addCard(Zone.BATTLEFIELD, playerB, "Clergy of the Holy Nimbus"); // {W} 1/1 addCard(Zone.HAND, playerA, "Wrath of God"); // {2}{W}{W} destroy all creatures, they cannot be regenerated addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); - + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wrath of God"); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); - + assertGraveyardCount(playerA, "Wrath of God", 1); assertGraveyardCount(playerB, "Clergy of the Holy Nimbus", 1); } - + // in game testing works correctly - not sure if the ability is being activated or not here. - @Ignore @Test public void testOpponentPaysOneToNotAllowRegeneration() { - + // If Clergy of the Holy Nimbus would be destroyed, regenerate it. // {1}: Clergy of the Holy Nimbus can't be regenerated this turn. Only any opponent may activate this ability. addCard(Zone.BATTLEFIELD, playerB, "Clergy of the Holy Nimbus"); // {W} 1/1 addCard(Zone.HAND, playerA, "Doom Blade"); // {1}{B} destroy target non-black creature addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); - + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}:"); castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Doom Blade", "Clergy of the Holy Nimbus"); setStopAt(1, PhaseStep.END_TURN); execute(); - + assertGraveyardCount(playerA, "Doom Blade", 1); assertGraveyardCount(playerB, "Clergy of the Holy Nimbus", 1); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DamageEffectsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DamageEffectsTest.java index d6a9b1226f08..134f2d15f71f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DamageEffectsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DamageEffectsTest.java @@ -3,12 +3,10 @@ import mage.constants.PhaseStep; import mage.constants.Zone; -import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class DamageEffectsTest extends CardTestPlayerBase { @@ -70,49 +68,4 @@ public void testDamageToPlayer() { assertLife(playerA, 32); } - - @Ignore - @Test - public void vexingDevilFurnaceRathRedirectToPlaneswalker() { - - /* - Vexing Devil {R} - Creature — Devil - When Vexing Devil enters the battlefield, any opponent may have it deal 4 damage to them. If a player does, sacrifice Vexing Devil. - */ - String vDevil = "Vexing Devil"; - - /* - Nissa, Worldwaker {3}{G}{G} - Planeswalker — Nissa - +1: Target land you control becomes a 4/4 Elemental creature with trample. It's still a land. - +1: Untap up to four target Forests. - −7: Search your library for any number of basic land cards, put them onto the battlefield, then shuffle your library. Those lands become 4/4 Elemental creatures with trample. They're still lands. - */ - String nissa = "Nissa, Worldwaker"; - - /* - Furnace of Rath {1}{R}{R}{R} - Enchantment - If a source would deal damage to a creature or player, it deals double that damage to that creature or player instead. - */ - addCard(Zone.BATTLEFIELD, playerB, "Furnace of Rath"); - addCard(Zone.HAND, playerB, vDevil); - addCard(Zone.HAND, playerA, nissa); - addCard(Zone.BATTLEFIELD, playerA, "Forest", 5); - addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, nissa); - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, vDevil); - setChoice(playerA, "Yes"); // deal 8 damage to playerA and sac vexing devil (8 due to furnace) - setChoice(playerB, "Yes"); // redirect to planeswalker - addTarget(playerB, nissa); - - setStopAt(2, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerB, vDevil, 1); - assertLife(playerA, 20); - assertGraveyardCount(playerA, nissa, 1); - } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DiscardEffectsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DiscardEffectsTest.java index 233bb083c21c..dfe9286bb400 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DiscardEffectsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DiscardEffectsTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.replacement; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/PulmonicSliverTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/PulmonicSliverTest.java index fe2db68a8a43..b674465be3d5 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/PulmonicSliverTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/PulmonicSliverTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.replacement; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/prevent/ForcefieldTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/prevent/ForcefieldTest.java new file mode 100644 index 000000000000..2839c48d490d --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/prevent/ForcefieldTest.java @@ -0,0 +1,38 @@ +package org.mage.test.cards.replacement.prevent; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class ForcefieldTest extends CardTestPlayerBase { + + private static final String giant = "Bonebreaker Giant"; + private static final String lion = "Silvercoat Lion"; + + @Test + public void testForcefield() { + addCard(Zone.BATTLEFIELD, playerB, "Wastes", 2); + addCard(Zone.BATTLEFIELD, playerB, "Forcefield"); + addCard(Zone.BATTLEFIELD, playerA, giant); + addCard(Zone.BATTLEFIELD, playerA, lion); + + attack(1, playerA, giant); + attack(1, playerA, lion); + + setChoice(playerB, giant); + setChoice(playerB, lion); + activateAbility(1, PhaseStep.DECLARE_BLOCKERS, playerB, "{1}"); + activateAbility(1, PhaseStep.DECLARE_BLOCKERS, playerB, "{1}"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerB, 20 - 1 - 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/redirect/PalisadeGiantTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/redirect/PalisadeGiantTest.java index c7e67c0c05ad..3a0ed57363d0 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/redirect/PalisadeGiantTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/redirect/PalisadeGiantTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.replacement.redirect; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/CavernOfSoulsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/CavernOfSoulsTest.java index 4bbe5029b3d9..34db9c4181ee 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/CavernOfSoulsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/CavernOfSoulsTest.java @@ -2,12 +2,10 @@ import mage.constants.PhaseStep; import mage.constants.Zone; -import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author noxx */ public class CavernOfSoulsTest extends CardTestPlayerBase { @@ -195,10 +193,8 @@ public void testConditionlManaWorksIfCavernIsReplayed() { * Return to the Ranks cannot be countered if mana produced by Cavern of * Souls was used to pay X. Can be bug also for all other spells with X in * their cost, not sure. - * */ @Test - @Ignore public void testCastWithColorlessManaCanBeCountered() { addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); addCard(Zone.HAND, playerA, "Cavern of Souls"); @@ -214,8 +210,9 @@ public void testCastWithColorlessManaCanBeCountered() { playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cavern of Souls"); setChoice(playerA, "Drake"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Return to the Ranks", "Silvercoat Lion"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Return to the Ranks"); setChoice(playerA, "X=1"); + addTarget(playerA, "Silvercoat Lion"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Counterspell", "Return to the Ranks"); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/c18/GeodeGolemTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/c18/GeodeGolemTest.java new file mode 100644 index 000000000000..3c4cce83e0f3 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/c18/GeodeGolemTest.java @@ -0,0 +1,205 @@ +package org.mage.test.cards.single.c18; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommanderDuelBase; + +/** + * @author JayDi85 + */ +public class GeodeGolemTest extends CardTestCommanderDuelBase { + + @Test + public void test_Normal() { + // Whenever Geode Golem deals combat damage to a player, you may + // cast your commander from the command zone without paying its mana cost. + addCard(Zone.BATTLEFIELD, playerA, "Geode Golem"); + // + addCard(Zone.COMMAND, playerA, "Grizzly Bears"); // {1}{G} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 10); + // + addCustomEffect_TargetDamage(playerA, 2); + + checkCommandCardCount("before 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + // turn 1 - first cast + + // attack and cast first time (free) + attack(1, playerA, "Geode Golem"); + setChoice(playerA, "Yes"); // cast commander + setChoice(playerA, "Grizzly Bears"); // commander choice + waitStackResolved(1, PhaseStep.COMBAT_DAMAGE); + checkPermanentCount("after 1", 1, PhaseStep.COMBAT_DAMAGE, playerA, "Grizzly Bears", 1); + checkPermanentTapped("after 1", 1, PhaseStep.COMBAT_DAMAGE, playerA, "Forest", true, 0); + // + // remove to command zone (0x tax) + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "target damage 2", "Grizzly Bears"); + setChoice(playerA, "Yes"); // move to command zone + + // turn 3 - second cast (1x tax) + + attack(3, playerA, "Geode Golem"); + setChoice(playerA, "Yes"); // cast commander + setChoice(playerA, "Grizzly Bears"); // commander choice + waitStackResolved(3, PhaseStep.COMBAT_DAMAGE); + checkPermanentCount("after 2", 3, PhaseStep.COMBAT_DAMAGE, playerA, "Grizzly Bears", 1); + checkPermanentTapped("after 2", 3, PhaseStep.COMBAT_DAMAGE, playerA, "Forest", true, 2); // 1x tax + // + // remove to command zone + activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "target damage 2", "Grizzly Bears"); + setChoice(playerA, "Yes"); // move to command zone + + // turn 5 - third cast (2x tax) + + attack(5, playerA, "Geode Golem"); + setChoice(playerA, "Yes"); // cast commander + setChoice(playerA, "Grizzly Bears"); // commander choice + waitStackResolved(5, PhaseStep.COMBAT_DAMAGE); + checkPermanentCount("after 3", 5, PhaseStep.COMBAT_DAMAGE, playerA, "Grizzly Bears", 1); + checkPermanentTapped("after 3", 5, PhaseStep.COMBAT_DAMAGE, playerA, "Forest", true, 2 * 2); // 2x tax + + setStrictChooseMode(true); + setStopAt(5, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Grizzly Bears", 1); + } + + @Test + public void test_MDF_SingleSide() { + // Whenever Geode Golem deals combat damage to a player, you may + // cast your commander from the command zone without paying its mana cost. + addCard(Zone.BATTLEFIELD, playerA, "Geode Golem"); + // + // Akoum Warrior {5}{R} - creature 4/5 + // Akoum Teeth - land + addCard(Zone.COMMAND, playerA, "Akoum Warrior"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 10); + // + addCustomEffect_TargetDamage(playerA, 5); + + checkCommandCardCount("before 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 1); + + // turn 1 - first cast + + // attack and cast first time (free) + attack(1, playerA, "Geode Golem"); + setChoice(playerA, "Yes"); // cast commander + setChoice(playerA, "Akoum Warrior"); // commander choice + waitStackResolved(1, PhaseStep.COMBAT_DAMAGE); + checkPermanentCount("after 1", 1, PhaseStep.COMBAT_DAMAGE, playerA, "Akoum Warrior", 1); + checkPermanentTapped("after 1", 1, PhaseStep.COMBAT_DAMAGE, playerA, "Mountain", true, 0); + // + // remove to command zone (0x tax) + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "target damage 5", "Akoum Warrior"); + setChoice(playerA, "Yes"); // move to command zone + + // turn 3 - second cast (1x tax) + + attack(3, playerA, "Geode Golem"); + setChoice(playerA, "Yes"); // cast commander + setChoice(playerA, "Akoum Warrior"); // commander choice + waitStackResolved(3, PhaseStep.COMBAT_DAMAGE); + checkPermanentCount("after 2", 3, PhaseStep.COMBAT_DAMAGE, playerA, "Akoum Warrior", 1); + checkPermanentTapped("after 2", 3, PhaseStep.COMBAT_DAMAGE, playerA, "Mountain", true, 2); // 1x tax + // + // remove to command zone + activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "target damage 5", "Akoum Warrior"); + setChoice(playerA, "Yes"); // move to command zone + + // turn 5 - third cast (2x tax) + + attack(5, playerA, "Geode Golem"); + setChoice(playerA, "Yes"); // cast commander + setChoice(playerA, "Akoum Warrior"); // commander choice + waitStackResolved(5, PhaseStep.COMBAT_DAMAGE); + checkPermanentCount("after 3", 5, PhaseStep.COMBAT_DAMAGE, playerA, "Akoum Warrior", 1); + checkPermanentTapped("after 3", 5, PhaseStep.COMBAT_DAMAGE, playerA, "Mountain", true, 2 * 2); // 2x tax + + setStrictChooseMode(true); + setStopAt(5, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Akoum Warrior", 1); + } + + @Test + public void test_MDF_BothSides() { + // Whenever Geode Golem deals combat damage to a player, you may + // cast your commander from the command zone without paying its mana cost. + addCard(Zone.BATTLEFIELD, playerA, "Geode Golem"); + // + // Birgi, God of Storytelling {2}{R} - creature 3/3 + // Harnfel, Horn of Bounty {4}{R} - artifact + addCard(Zone.COMMAND, playerA, "Birgi, God of Storytelling"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 10); + // + addCustomEffect_TargetDamage(playerA, 3); + addCustomEffect_DestroyTarget(playerA); + + checkCommandCardCount("before 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Birgi, God of Storytelling", 1); + + // turn 1 - first cast, LEFT side + + // attack and cast first time (free) + attack(1, playerA, "Geode Golem"); + setChoice(playerA, "Yes"); // cast commander + setChoice(playerA, "Birgi, God of Storytelling"); // commander choice + setChoice(playerA, "Cast Birgi, God of Storytelling"); // spell choice + waitStackResolved(1, PhaseStep.COMBAT_DAMAGE); + checkPermanentCount("after 1", 1, PhaseStep.COMBAT_DAMAGE, playerA, "Birgi, God of Storytelling", 1); + checkPermanentTapped("after 1", 1, PhaseStep.COMBAT_DAMAGE, playerA, "Mountain", true, 0); + // + // remove to command zone (0x tax) + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "target damage 3", "Birgi, God of Storytelling"); + setChoice(playerA, "Yes"); // move to command zone + + // turn 3 - second cast, LEFT side (1x tax) + + attack(3, playerA, "Geode Golem"); + setChoice(playerA, "Yes"); // cast commander + setChoice(playerA, "Birgi, God of Storytelling"); // commander choice + setChoice(playerA, "Cast Birgi, God of Storytelling"); // spell choice + waitStackResolved(3, PhaseStep.COMBAT_DAMAGE); + checkPermanentCount("after 2", 3, PhaseStep.COMBAT_DAMAGE, playerA, "Birgi, God of Storytelling", 1); + checkPermanentTapped("after 2", 3, PhaseStep.COMBAT_DAMAGE, playerA, "Mountain", true, 2); // 1x tax + // + // remove to command zone + activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "target damage 3", "Birgi, God of Storytelling"); + setChoice(playerA, "Yes"); // move to command zone + + // turn 5 - third cast, RIGHT side (2x tax) + + attack(5, playerA, "Geode Golem"); + setChoice(playerA, "Yes"); // cast commander + setChoice(playerA, "Birgi, God of Storytelling"); // commander choice + setChoice(playerA, "Cast Harnfel, Horn of Bounty"); // spell choice + waitStackResolved(5, PhaseStep.COMBAT_DAMAGE); + checkPermanentCount("after 3", 5, PhaseStep.COMBAT_DAMAGE, playerA, "Harnfel, Horn of Bounty", 1); + checkPermanentTapped("after 3", 5, PhaseStep.COMBAT_DAMAGE, playerA, "Mountain", true, 2 * 2); // 2x tax + // + // remove to command zone + activateAbility(5, PhaseStep.POSTCOMBAT_MAIN, playerA, "target destroy", "Harnfel, Horn of Bounty"); + setChoice(playerA, "Yes"); // move to command zone + + // turn 7 - fourth cast, RIGHT side (3x tax) + + attack(7, playerA, "Geode Golem"); + setChoice(playerA, "Yes"); // cast commander + setChoice(playerA, "Birgi, God of Storytelling"); // commander choice + setChoice(playerA, "Cast Harnfel, Horn of Bounty"); // spell choice + waitStackResolved(7, PhaseStep.COMBAT_DAMAGE); + checkPermanentCount("after 4", 7, PhaseStep.COMBAT_DAMAGE, playerA, "Harnfel, Horn of Bounty", 1); + checkPermanentTapped("after 4", 7, PhaseStep.COMBAT_DAMAGE, playerA, "Mountain", true, 2 * 3); // 3x tax + + setStrictChooseMode(true); + setStopAt(7, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Harnfel, Horn of Bounty", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/c19/ElshaOfTheInfiniteTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/c19/ElshaOfTheInfiniteTest.java new file mode 100644 index 000000000000..1703df289fa5 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/c19/ElshaOfTheInfiniteTest.java @@ -0,0 +1,55 @@ +package org.mage.test.cards.single.c19; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class ElshaOfTheInfiniteTest extends CardTestPlayerBase { + + @Test + public void test_MustApplyToTopCardOnly() { + // bug: flash ability can be applied to all cards in hand + // https://github.com/magefree/mage/issues/7605 + + removeAllCardsFromLibrary(playerA); + removeAllCardsFromHand(playerA); + skipInitShuffling(); + + // You may look at the top card of your library any time. + // You may cast noncreature spells from the top of your library. If you cast a spell this way, you may cast it as though it had flash. + addCard(Zone.BATTLEFIELD, playerA, "Elsha of the Infinite"); + // + // Sorcery + // Bolt of Keranos deals 3 damage to any target. Scry 1. + addCard(Zone.HAND, playerA, "Bolt of Keranos", 1);// {1}{R}{R} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // + // Birgi, God of Storytelling - creature, {2}{R} + // Harnfel, Horn of Bounty - artifact, {4}{R} + addCard(Zone.LIBRARY, playerA, "Birgi, God of Storytelling", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + + // possible bug: sorcery card in hand got flash and can be playable + checkPlayableAbility("hand on upkeep, can't cast", 1, PhaseStep.UPKEEP, playerA, "Cast Bolt of Keranos", false); + checkPlayableAbility("lib on upkeep, can't cast left", 1, PhaseStep.UPKEEP, playerA, "Cast Birgi, God of Storytelling", false); + checkPlayableAbility("lib on upkeep, can cast right", 1, PhaseStep.UPKEEP, playerA, "Cast Harnfel, Horn of Bounty", true); + // + checkPlayableAbility("hand on main", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Bolt of Keranos", true); + checkPlayableAbility("lib on main, can't cast left", 1, PhaseStep.UPKEEP, playerA, "Cast Birgi, God of Storytelling", false); + checkPlayableAbility("lib on main, can cast right", 1, PhaseStep.UPKEEP, playerA, "Cast Harnfel, Horn of Bounty", true); + + checkLibraryCount("before cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Birgi, God of Storytelling", 1); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Harnfel, Horn of Bounty"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Harnfel, Horn of Bounty", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/c20/TwinningStaffTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/c20/TwinningStaffTest.java new file mode 100644 index 000000000000..61a697488be4 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/c20/TwinningStaffTest.java @@ -0,0 +1,91 @@ +package org.mage.test.cards.single.c20; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.player.TestPlayer; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class TwinningStaffTest extends CardTestPlayerBase { + private static final String staff = "Twinning Staff"; + private static final String zada = "Zada, Hedron Grinder"; + private static final String bear = "Grizzly Bears"; + private static final String lion = "Silvercoat Lion"; + private static final String growth = "Giant Growth"; + private static final String disfigure = "Disfigure"; + private static final String fork = "Fork"; + private static final String elite = "Scalebane's Elite"; + + @Test + public void testWithZada() { + addCard(Zone.BATTLEFIELD, playerA, "Forest"); + addCard(Zone.BATTLEFIELD, playerA, staff); + addCard(Zone.BATTLEFIELD, playerA, zada); + addCard(Zone.BATTLEFIELD, playerA, bear); + addCard(Zone.BATTLEFIELD, playerA, lion); + addCard(Zone.HAND, playerA, growth); + + setChoice(playerA, TestPlayer.CHOICE_SKIP); // skip stack order + setChoice(playerA, "Yes"); + addTarget(playerA, bear); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, growth, zada); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, zada, 3 + 3, 3 + 3); + assertPowerToughness(playerA, bear, 2 + 3 + 3, 2 + 3 + 3); + assertPowerToughness(playerA, lion, 2 + 3, 2 + 3); + } + + @Test + public void testWithZadaTwice() { + addCard(Zone.BATTLEFIELD, playerA, "Forest"); + addCard(Zone.BATTLEFIELD, playerA, staff, 2); + addCard(Zone.BATTLEFIELD, playerA, zada); + addCard(Zone.BATTLEFIELD, playerA, bear); + addCard(Zone.BATTLEFIELD, playerA, lion); + addCard(Zone.HAND, playerA, growth); + + addTarget(playerA, bear, 2); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, growth, zada); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, zada, 3 + 3, 3 + 3); + assertPowerToughness(playerA, bear, 2 + 3 + 3 + 3, 2 + 3 + 3 + 3); + assertPowerToughness(playerA, lion, 2 + 3, 2 + 3); + } + + @Test + public void testFork() { + addCard(Zone.BATTLEFIELD, playerA, "Badlands", 3); + addCard(Zone.BATTLEFIELD, playerA, staff); + addCard(Zone.BATTLEFIELD, playerA, bear); + addCard(Zone.BATTLEFIELD, playerA, elite); // protection from black + addCard(Zone.HAND, playerA, disfigure); + addCard(Zone.HAND, playerA, fork); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, disfigure, bear); + setChoice(playerA, "Yes", 2); + addTarget(playerA, elite, 2); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, fork, disfigure, disfigure); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, disfigure, 1); + assertGraveyardCount(playerA, fork, 1); + assertGraveyardCount(playerA, bear, 1); + assertGraveyardCount(playerA, elite, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/chk/Test_HorobiDeathsWail.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/chk/Test_HorobiDeathsWail.java new file mode 100644 index 000000000000..5279f5be79fb --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/chk/Test_HorobiDeathsWail.java @@ -0,0 +1,28 @@ +package org.mage.test.cards.single.chk; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class Test_HorobiDeathsWail extends CardTestPlayerBase { + + // issue 7772 + @Test + @Ignore + public void animateDeadOnHorobi(){ + // Animate Dead + addCard(Zone.HAND, playerA, "Animate Dead"); + // Whenever a creature becomes the target of a spell or ability, destroy that creature. + addCard(Zone.GRAVEYARD, playerA, "Horobi, Death's Wail"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Animate Dead", "Horobi, Death's Wail"); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + assertPermanentCount(playerA, "Horobi, Death's Wail", 1); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/cmd/AcornCatapultTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/cmd/AcornCatapultTest.java index ec15bbb2b0e2..9bed69cc0fcc 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/cmd/AcornCatapultTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/cmd/AcornCatapultTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.single.cmd; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/cmr/ProfaneTransfusionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/cmr/ProfaneTransfusionTest.java new file mode 100644 index 000000000000..74bf1b3060c8 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/cmr/ProfaneTransfusionTest.java @@ -0,0 +1,117 @@ +package org.mage.test.cards.single.cmr; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class ProfaneTransfusionTest extends CardTestPlayerBase { + + private static final String transfusion = "Profane Transfusion"; + private static final String emperion = "Platinum Emperion"; + private static final String reflection = "Boon Reflection"; + private static final String skullcrack = "Skullcrack"; + + @Test + public void testRegular() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 9); + addCard(Zone.HAND, playerA, transfusion); + + setLife(playerA, 24); + setLife(playerB, 16); + + addTarget(playerA, playerA); + addTarget(playerA, playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, transfusion); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 16); + assertLife(playerB, 24); + assertPermanentCount(playerA, "Horror", 1); + assertPowerToughness(playerA, "Horror", 24 - 16, 24 - 16); + assertGraveyardCount(playerA, transfusion, 1); + } + + @Test + public void testCantChange() { + // Platinum Emperion stops life totals from changing but token is still created + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 9); + addCard(Zone.BATTLEFIELD, playerA, emperion); + addCard(Zone.HAND, playerA, transfusion); + + setLife(playerA, 24); + setLife(playerB, 16); + + addTarget(playerA, playerA); + addTarget(playerA, playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, transfusion); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 24); + assertLife(playerB, 16); + assertPermanentCount(playerA, "Horror", 1); + assertPowerToughness(playerA, "Horror", 24 - 16, 24 - 16); + assertGraveyardCount(playerA, transfusion, 1); + } + + @Test + public void testDoubleLife() { + // Boon Reflection doubles life gain, which affects final difference + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 9); + addCard(Zone.BATTLEFIELD, playerB, reflection); + addCard(Zone.HAND, playerA, transfusion); + + setLife(playerA, 24); + setLife(playerB, 16); + + addTarget(playerA, playerA); + addTarget(playerA, playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, transfusion); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 16); + assertLife(playerB, 32); + assertPermanentCount(playerA, "Horror", 1); + assertPowerToughness(playerA, "Horror", 32 - 16, 32 - 16); + assertGraveyardCount(playerA, transfusion, 1); + } + + @Test + public void testCantGainLife() { + // Skullcrack prevents life gain, but final difference should still be 3 + addCard(Zone.BATTLEFIELD, playerA, "Badlands", 11); + addCard(Zone.HAND, playerA, skullcrack); + addCard(Zone.HAND, playerA, transfusion); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, skullcrack, playerB); + addTarget(playerA, playerA); + addTarget(playerA, playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, transfusion); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 20); + assertLife(playerB, 17); + assertPermanentCount(playerA, "Horror", 1); + assertPowerToughness(playerA, "Horror", 20 - 17, 20 - 17); + assertGraveyardCount(playerA, transfusion, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/dgm/MorgueBurstTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dgm/MorgueBurstTest.java index 1f6eb2670188..e2315fe10830 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/dgm/MorgueBurstTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dgm/MorgueBurstTest.java @@ -1,9 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - package org.mage.test.cards.single.dgm; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/AltarOfTheLostTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/AltarOfTheLostTest.java index d16a35781725..f9f0fc11ffbe 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/AltarOfTheLostTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/AltarOfTheLostTest.java @@ -3,6 +3,7 @@ import mage.constants.PhaseStep; import mage.constants.Zone; import org.junit.Test; +import org.mage.test.player.TestPlayer; import org.mage.test.serverside.base.CardTestPlayerBase; /** @@ -20,8 +21,11 @@ public void testCard() { // Flashback {1}{B} addCard(Zone.GRAVEYARD, playerA, "Lingering Souls"); - setChoice(playerA, "Black"); - setChoice(playerA, "Black"); + // Add 2 black mana (mana choice in WUBRG order) + setChoice(playerA, "X=0"); + setChoice(playerA, "X=0"); + setChoice(playerA, "X=2"); + setChoice(playerA, TestPlayer.CHOICE_SKIP); activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Flashback {1}{B}"); setStopAt(3, PhaseStep.BEGIN_COMBAT); @@ -38,8 +42,10 @@ public void testCard1() { addCard(Zone.BATTLEFIELD, playerA, "Altar of the Lost"); addCard(Zone.HAND, playerA, "Lingering Souls"); - setChoice(playerA, "Black"); - setChoice(playerA, "Black"); + setChoice(playerA, "X=0"); + setChoice(playerA, "X=0"); + setChoice(playerA, "X=2"); + setChoice(playerA, TestPlayer.CHOICE_SKIP); castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Lingering Souls"); setStopAt(3, PhaseStep.BEGIN_COMBAT); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/FiendOfTheShadowsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/FiendOfTheShadowsTest.java index ec64fa99ebeb..fa943a3d259b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/FiendOfTheShadowsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/FiendOfTheShadowsTest.java @@ -45,7 +45,7 @@ public void testCardExile1() { addCard(Zone.HAND, playerB, "Swamp"); attack(1, playerA, "Fiend of the Shadows"); - addTarget(playerB, "Swamp"); + setChoice(playerB, "Swamp"); playLand(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Swamp"); setStrictChooseMode(true); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/dst/DemonsHornTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dst/DemonsHornTest.java index 7f41496aa1f7..7a055b797495 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/dst/DemonsHornTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dst/DemonsHornTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.single.dst; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/eld/RobberOfTheRichTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/eld/RobberOfTheRichTest.java new file mode 100644 index 000000000000..ebb23c2dba12 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/eld/RobberOfTheRichTest.java @@ -0,0 +1,60 @@ +package org.mage.test.cards.single.eld; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class RobberOfTheRichTest extends CardTestPlayerBase { + + private static final String robber = "Robber of the Rich"; + private static final String passage = "Whitesun's Passage"; + private static final String blackguard = "Bane Alley Blackguard"; + + @Test + public void testRobber() { + removeAllCardsFromLibrary(playerB); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, robber); + addCard(Zone.LIBRARY, playerB, passage); + addCard(Zone.HAND, playerB, "Mountain", 3); + + attack(1, playerA, robber, playerB); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, passage); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 20 + 5); + assertGraveyardCount(playerB, passage, 1); + } + + @Test + public void testRobberWithOtherRogue() { + removeAllCardsFromLibrary(playerB); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, robber); + addCard(Zone.BATTLEFIELD, playerA, blackguard); + addCard(Zone.LIBRARY, playerB, passage, 5); + addCard(Zone.HAND, playerB, "Mountain"); + + attack(1, playerA, robber, playerB); + attack(3, playerA, blackguard, playerB); + + castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, passage); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 20 + 5); + assertGraveyardCount(playerB, passage, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/eld/SyrGwynHeroOfAshvaleTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/eld/SyrGwynHeroOfAshvaleTest.java index bfeab03b9166..d00136c897ac 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/eld/SyrGwynHeroOfAshvaleTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/eld/SyrGwynHeroOfAshvaleTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.single.eld; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/emn/PermeatingMassTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/emn/PermeatingMassTest.java index 4c7038c7e710..4dfc5bb2656f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/emn/PermeatingMassTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/emn/PermeatingMassTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.single.emn; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/emn/SpellQuellerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/emn/SpellQuellerTest.java index d22b896bb53d..61e18f2d29dc 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/emn/SpellQuellerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/emn/SpellQuellerTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.single.emn; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/emn/TreeOfPerditionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/emn/TreeOfPerditionTest.java index 3075bf0f8d15..6edabffb06e2 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/emn/TreeOfPerditionTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/emn/TreeOfPerditionTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.single.emn; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/fdn/RiteOfPassageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/fdn/RiteOfPassageTest.java new file mode 100644 index 000000000000..b58ab70d2ef7 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/fdn/RiteOfPassageTest.java @@ -0,0 +1,49 @@ +package org.mage.test.cards.single.fdn; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class RiteOfPassageTest extends CardTestPlayerBase { + + + @Test + public void addCounterNonLethal(){ + // Watchwolf 3/3 + addCard(Zone.BATTLEFIELD, playerA, "Watchwolf", 1); + // Whenever a creature you control is dealt damage, put a +1/+1 counter on it. + addCard(Zone.BATTLEFIELD, playerA, "Rite of Passage"); + // Shock - deal 2 damage to any target + addCard(Zone.HAND, playerA, "Shock"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA,"Shock","Watchwolf"); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + assertCounterCount("Watchwolf", CounterType.P1P1, 1); + } + + @Test + public void addCounterNonLethalOppControlsVorinclex(){ + // Watchwolf 3/3 + addCard(Zone.BATTLEFIELD, playerA, "Watchwolf", 1); + // Whenever a creature you control is dealt damage, put a +1/+1 counter on it. + addCard(Zone.BATTLEFIELD, playerA, "Rite of Passage"); + // Shock - deal 2 damage to any target + addCard(Zone.HAND, playerA, "Shock"); + // Vorinclex + //If an opponent would put one or more counters on a permanent or player, + // they put half that many of each of those kinds of counters on that permanent or player instead, rounded down. + addCard(Zone.BATTLEFIELD, playerB, "Vorinclex, Monstrous Raider"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA,"Shock","Watchwolf"); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + assertCounterCount("Watchwolf", CounterType.P1P1, 0); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/fut/MuragandaPetroglyphsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/fut/MuragandaPetroglyphsTest.java index 4345d9408618..1cbd3ad1a08c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/fut/MuragandaPetroglyphsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/fut/MuragandaPetroglyphsTest.java @@ -212,4 +212,28 @@ public void cipherTest() { assertPowerToughness(playerA, "Grizzly Bears", 2, 2); } + + /** + * Quicksilver Gargantuan overwrites Tarmogoyf's P/T and therefore removes its characteristic-definining abilities + */ + @Test + public void cdaTest() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 7); + addCard(Zone.BATTLEFIELD, playerA, "Muraganda Petroglyphs", 1); + addCard(Zone.BATTLEFIELD, playerA, "Tarmogoyf", 1); + addCard(Zone.GRAVEYARD, playerA, "Darksteel Relic"); + addCard(Zone.GRAVEYARD, playerA, "Bitterblossom"); + addCard(Zone.HAND, playerA, "Quicksilver Gargantuan", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Quicksilver Gargantuan"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + // Graveyard has artifact, enchantment and tribal + assertPowerToughness(playerA, "Tarmogoyf", 3, 4); + // Gargantuan copy overwrites and gets a boost from Petroglyphs + assertPowerToughness(playerA, "Tarmogoyf", 7 + 2, 7 + 2); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/grn/BeamsplitterMageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/grn/BeamsplitterMageTest.java new file mode 100644 index 000000000000..2e4c4b68ad10 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/grn/BeamsplitterMageTest.java @@ -0,0 +1,58 @@ +package org.mage.test.cards.single.grn; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class BeamsplitterMageTest extends CardTestPlayerBase { + private static final String bsm = "Beamsplitter Mage"; + private static final String lion = "Silvercoat Lion"; + private static final String duelist = "Deft Duelist"; + private static final String bolt = "Lightning Bolt"; + private static final String seeds = "Seeds of Strength"; + + @Test + public void testLightningBolt() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, lion); + addCard(Zone.BATTLEFIELD, playerA, duelist); + addCard(Zone.BATTLEFIELD, playerA, bsm); + addCard(Zone.HAND, playerA, bolt); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, bsm); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, bsm, 0); + assertPermanentCount(playerA, lion, 0); + assertPermanentCount(playerA, duelist, 1); + assertGraveyardCount(playerA, bsm, 1); + assertGraveyardCount(playerA, lion, 1); + assertGraveyardCount(playerA, duelist, 0); + assertGraveyardCount(playerA, bolt, 1); + } + + @Test + public void testSeedsOfStrength() { + addCard(Zone.BATTLEFIELD, playerA, "Savannah", 2); + addCard(Zone.BATTLEFIELD, playerA, lion); + addCard(Zone.BATTLEFIELD, playerA, duelist); + addCard(Zone.BATTLEFIELD, playerA, bsm); + addCard(Zone.HAND, playerA, seeds); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, seeds, bsm); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, bsm, 5, 5); + assertPowerToughness(playerA, lion, 5, 5); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/iko/TheOzolithTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/iko/TheOzolithTest.java index 2a7d7873bfe6..fb0f77c6bc1e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/iko/TheOzolithTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/iko/TheOzolithTest.java @@ -1,9 +1,9 @@ package org.mage.test.cards.single.iko; +import mage.constants.CardType; import mage.constants.PhaseStep; import mage.constants.Zone; import mage.counters.CounterType; -import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -12,48 +12,50 @@ */ public class TheOzolithTest extends CardTestPlayerBase { - private static final String ozlth = "The Ozolith"; - private static final String frtld = "Fertilid"; - private static final String wlkncrps = "Walking Corpse"; - private static final String flgrn = "Fully Grown"; - private static final String mrdr = "Murder"; - private static final String ttrkt = "Tatterkite"; - private static final String bltbtl = "Blightbeetle"; - private static final String pnctrblst = "Puncture Blast"; + private static final String THE_OZOLITH = "The Ozolith"; + private static final String FERTILID = "Fertilid"; + private static final String WALKING_CORPSE = "Walking Corpse"; + private static final String FULLY_GROWN = "Fully Grown"; + private static final String MURDER = "Murder"; + private static final String TATTERKITE = "Tatterkite"; + private static final String BLIGHTBEETLE = "Blightbeetle"; + private static final String PUNCTURE_BLAST = "Puncture Blast"; + private static final String BRANCHING_EVOLUTION = "Branching Evolution"; + private static final String MARCH_OF_THE_MACHINES = "March of the Machines"; @Test public void testTheOzolithGetCounters() { addCard(Zone.BATTLEFIELD, playerA, "Bayou", 6); - addCard(Zone.BATTLEFIELD, playerA, ozlth); - addCard(Zone.BATTLEFIELD, playerA, frtld); - addCard(Zone.HAND, playerA, flgrn); - addCard(Zone.HAND, playerA, mrdr); + addCard(Zone.BATTLEFIELD, playerA, THE_OZOLITH); + addCard(Zone.BATTLEFIELD, playerA, FERTILID); + addCard(Zone.HAND, playerA, FULLY_GROWN); + addCard(Zone.HAND, playerA, MURDER); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, flgrn, frtld); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, FULLY_GROWN, FERTILID); waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, mrdr, frtld); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, MURDER, FERTILID); setStopAt(1, PhaseStep.END_TURN); execute(); assertAllCommandsUsed(); - assertPermanentCount(playerA, frtld, 0); - assertCounterCount(ozlth, CounterType.P1P1, 2); - assertCounterCount(ozlth, CounterType.TRAMPLE, 1); + assertPermanentCount(playerA, FERTILID, 0); + assertCounterCount(THE_OZOLITH, CounterType.P1P1, 2); + assertCounterCount(THE_OZOLITH, CounterType.TRAMPLE, 1); } @Test public void testTheOzolithGiveCounters() { addCard(Zone.BATTLEFIELD, playerA, "Bayou", 6); - addCard(Zone.BATTLEFIELD, playerA, ozlth); - addCard(Zone.BATTLEFIELD, playerA, frtld); - addCard(Zone.BATTLEFIELD, playerA, wlkncrps); - addCard(Zone.HAND, playerA, flgrn); - addCard(Zone.HAND, playerA, mrdr); + addCard(Zone.BATTLEFIELD, playerA, THE_OZOLITH); + addCard(Zone.BATTLEFIELD, playerA, FERTILID); + addCard(Zone.BATTLEFIELD, playerA, WALKING_CORPSE); + addCard(Zone.HAND, playerA, FULLY_GROWN); + addCard(Zone.HAND, playerA, MURDER); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, flgrn, frtld); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, FULLY_GROWN, FERTILID); waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, mrdr, frtld); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, MURDER, FERTILID); setChoice(playerA, "Yes"); // Move counters at beginning of combat @@ -61,22 +63,22 @@ public void testTheOzolithGiveCounters() { execute(); assertAllCommandsUsed(); - assertPermanentCount(playerA, frtld, 0); - assertCounterCount(wlkncrps, CounterType.P1P1, 2); - assertCounterCount(wlkncrps, CounterType.TRAMPLE, 1); - assertCounterCount(ozlth, CounterType.P1P1, 0); - assertCounterCount(ozlth, CounterType.TRAMPLE, 0); + assertPermanentCount(playerA, FERTILID, 0); + assertCounterCount(WALKING_CORPSE, CounterType.P1P1, 2); + assertCounterCount(WALKING_CORPSE, CounterType.TRAMPLE, 1); + assertCounterCount(THE_OZOLITH, CounterType.P1P1, 0); + assertCounterCount(THE_OZOLITH, CounterType.TRAMPLE, 0); } @Test public void testTheOzolithCantGiveAnyCounters() { addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); - addCard(Zone.BATTLEFIELD, playerA, ozlth); - addCard(Zone.BATTLEFIELD, playerA, frtld); - addCard(Zone.BATTLEFIELD, playerA, ttrkt); - addCard(Zone.HAND, playerA, mrdr); + addCard(Zone.BATTLEFIELD, playerA, THE_OZOLITH); + addCard(Zone.BATTLEFIELD, playerA, FERTILID); + addCard(Zone.BATTLEFIELD, playerA, TATTERKITE); + addCard(Zone.HAND, playerA, MURDER); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, mrdr, frtld); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, MURDER, FERTILID); setChoice(playerA, "Yes"); // Move counters at beginning of combat @@ -84,24 +86,24 @@ public void testTheOzolithCantGiveAnyCounters() { execute(); assertAllCommandsUsed(); - assertPermanentCount(playerA, frtld, 0); - assertCounterCount(ttrkt, CounterType.P1P1, 0); - assertCounterCount(ozlth, CounterType.P1P1, 2); + assertPermanentCount(playerA, FERTILID, 0); + assertCounterCount(TATTERKITE, CounterType.P1P1, 0); + assertCounterCount(THE_OZOLITH, CounterType.P1P1, 2); } @Test public void testTheOzolithCantGiveSomeCounters() { addCard(Zone.BATTLEFIELD, playerA, "Bayou", 6); - addCard(Zone.BATTLEFIELD, playerA, ozlth); - addCard(Zone.BATTLEFIELD, playerA, frtld); - addCard(Zone.BATTLEFIELD, playerA, wlkncrps); - addCard(Zone.HAND, playerA, flgrn); - addCard(Zone.HAND, playerA, mrdr); - addCard(Zone.BATTLEFIELD, playerB, bltbtl); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, flgrn, frtld); + addCard(Zone.BATTLEFIELD, playerA, THE_OZOLITH); + addCard(Zone.BATTLEFIELD, playerA, FERTILID); + addCard(Zone.BATTLEFIELD, playerA, WALKING_CORPSE); + addCard(Zone.HAND, playerA, FULLY_GROWN); + addCard(Zone.HAND, playerA, MURDER); + addCard(Zone.BATTLEFIELD, playerB, BLIGHTBEETLE); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, FULLY_GROWN, FERTILID); waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, mrdr, frtld); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, MURDER, FERTILID); setChoice(playerA, "Yes"); // Move counters at beginning of combat @@ -109,26 +111,26 @@ public void testTheOzolithCantGiveSomeCounters() { execute(); assertAllCommandsUsed(); - assertPermanentCount(playerA, frtld, 0); - assertCounterCount(wlkncrps, CounterType.P1P1, 0); - assertCounterCount(wlkncrps, CounterType.TRAMPLE, 1); - assertCounterCount(ozlth, CounterType.P1P1, 2); - assertCounterCount(ozlth, CounterType.TRAMPLE, 0); + assertPermanentCount(playerA, FERTILID, 0); + assertCounterCount(WALKING_CORPSE, CounterType.P1P1, 0); + assertCounterCount(WALKING_CORPSE, CounterType.TRAMPLE, 1); + assertCounterCount(THE_OZOLITH, CounterType.P1P1, 2); + assertCounterCount(THE_OZOLITH, CounterType.TRAMPLE, 0); } @Test public void testTheOzolithMultiples() { addCard(Zone.BATTLEFIELD, playerA, "Bayou", 6); - addCard(Zone.BATTLEFIELD, playerA, ozlth, 2); + addCard(Zone.BATTLEFIELD, playerA, THE_OZOLITH, 2); addCard(Zone.BATTLEFIELD, playerA, "Mirror Gallery"); - addCard(Zone.BATTLEFIELD, playerA, frtld); - addCard(Zone.BATTLEFIELD, playerA, wlkncrps); - addCard(Zone.HAND, playerA, flgrn); - addCard(Zone.HAND, playerA, mrdr); + addCard(Zone.BATTLEFIELD, playerA, FERTILID); + addCard(Zone.BATTLEFIELD, playerA, WALKING_CORPSE); + addCard(Zone.HAND, playerA, FULLY_GROWN); + addCard(Zone.HAND, playerA, MURDER); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, flgrn, frtld); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, FULLY_GROWN, FERTILID); waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, mrdr, frtld); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, MURDER, FERTILID); setChoice(playerA, "Yes", 2); // Move counters at beginning of combat @@ -136,30 +138,36 @@ public void testTheOzolithMultiples() { execute(); assertAllCommandsUsed(); - assertPermanentCount(playerA, frtld, 0); - assertCounterCount(wlkncrps, CounterType.P1P1, 4); - assertCounterCount(wlkncrps, CounterType.TRAMPLE, 2); - assertCounterCount(ozlth, CounterType.P1P1, 0); - assertCounterCount(ozlth, CounterType.TRAMPLE, 0); + assertPermanentCount(playerA, FERTILID, 0); + assertCounterCount(WALKING_CORPSE, CounterType.P1P1, 4); + assertCounterCount(WALKING_CORPSE, CounterType.TRAMPLE, 2); + assertCounterCount(THE_OZOLITH, CounterType.P1P1, 0); + assertCounterCount(THE_OZOLITH, CounterType.TRAMPLE, 0); } - @Ignore @Test public void testTheOzolithMinusPlus() { - // TODO: this test fails because of incorrect last known information handling - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); - addCard(Zone.BATTLEFIELD, playerA, ozlth); - addCard(Zone.BATTLEFIELD, playerA, frtld); - addCard(Zone.HAND, playerA, pnctrblst); + addCard(Zone.BATTLEFIELD, playerA, "Taiga", 9); + addCard(Zone.BATTLEFIELD, playerA, THE_OZOLITH); + addCard(Zone.BATTLEFIELD, playerA, MARCH_OF_THE_MACHINES); + addCard(Zone.HAND, playerA, FERTILID); + addCard(Zone.HAND, playerA, BRANCHING_EVOLUTION); + addCard(Zone.HAND, playerA, PUNCTURE_BLAST); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, pnctrblst, frtld); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, FERTILID); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, BRANCHING_EVOLUTION); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, PUNCTURE_BLAST, FERTILID); setStopAt(1, PhaseStep.END_TURN); execute(); assertAllCommandsUsed(); - assertPermanentCount(playerA, frtld, 0); - assertCounterCount(ozlth, CounterType.P1P1, 2); - assertCounterCount(ozlth, CounterType.M1M1, 3); + assertPermanentCount(playerA, FERTILID, 0); + // The Ozolith should be a creature which received 4 +1/+1 counters and 3 -1/-1 counters + // (2 +1/+1 counters from Fertilid doubled by Branching Evolution) + assertType(THE_OZOLITH, CardType.CREATURE, true); + // The counters then cancel out and there should only be a single +1/+1 counter left + assertCounterCount(THE_OZOLITH, CounterType.P1P1, 1); + assertCounterCount(THE_OZOLITH, CounterType.M1M1, 0); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/iko/ZirdaTheDawnwakerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/iko/ZirdaTheDawnwakerTest.java new file mode 100644 index 000000000000..200fd7ecd95b --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/iko/ZirdaTheDawnwakerTest.java @@ -0,0 +1,104 @@ +package org.mage.test.cards.single.iko; + +import mage.abilities.keyword.TrampleAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class ZirdaTheDawnwakerTest extends CardTestPlayerBase { + + private static final String zirda = "Zirda, the Dawnwaker"; + private static final String golem = "Igneous Golem"; + private static final String mauler = "Barkhide Mauler"; + private static final String geth = "Geth, Lord of the Vault"; + private static final String lion = "Silvercoat Lion"; + + @Test + public void testReduceToOne() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, zirda); + addCard(Zone.BATTLEFIELD, playerA, golem); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertTapped("Swamp", true); + assertAbility(playerA, golem, TrampleAbility.getInstance(), true); + } + + @Test + public void testCycling() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, zirda); + addCard(Zone.HAND, playerA, mauler); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cycling"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertTapped("Swamp", true); + assertGraveyardCount(playerA, mauler, 1); + } + + @Test + public void testWithGeth() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, zirda); + addCard(Zone.BATTLEFIELD, playerA, geth); + addCard(Zone.GRAVEYARD, playerB, lion); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{X}{B}"); + setChoice(playerA, "X=2"); + addTarget(playerA, lion); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, lion, 1); + assertTapped(lion, true); + assertGraveyardCount(playerB, 2); + assertGraveyardCount(playerB, lion, 0); + } + + @Test + public void testWithGeth2() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); + addCard(Zone.BATTLEFIELD, playerA, geth); + addCard(Zone.GRAVEYARD, playerB, zirda); + addCard(Zone.GRAVEYARD, playerB, lion); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{X}{B}"); + setChoice(playerA, "X=3"); + addTarget(playerA, zirda); + + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{X}{B}"); + setChoice(playerA, "X=2"); + addTarget(playerA, lion); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, zirda, 1); + assertPermanentCount(playerA, lion, 1); + assertTapped(zirda, true); + assertTapped(lion, true); + assertGraveyardCount(playerB, 2 + 3); + assertGraveyardCount(playerB, zirda, 0); + assertGraveyardCount(playerB, lion, 0); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/jou/DeicideTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/jou/DeicideTest.java new file mode 100644 index 000000000000..28fdebe3354a --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/jou/DeicideTest.java @@ -0,0 +1,33 @@ +package org.mage.test.cards.single.jou; + +import mage.constants.PhaseStep; +import mage.constants.SubType; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class DeicideTest extends CardTestPlayerBase { + + // #7661 + @Test + public void targetNonCreatureeGod() { + // Exile target enchantment. If the exiled card is a God card, search its controller's graveyard, hand, + // and library for any number of cards with the same name as that card and exile them, then that player shuffles their library. + addCard(Zone.HAND, playerA, "Deicide"); + + addCard(Zone.BATTLEFIELD, playerB, "Heliod, Sun-Crowned"); + // add for Devotion {W}{W} x 3 + addCard(Zone.BATTLEFIELD, playerB, "Crusade", 3); + + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Deicide", "Heliod, Sun-Crowned"); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + assertExiledCardSubtype("Heliod, Sun-Crowned", SubType.GOD); + + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/jou/KruphixGodOfHorizonsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/jou/KruphixGodOfHorizonsTest.java new file mode 100644 index 000000000000..73359b77e97e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/jou/KruphixGodOfHorizonsTest.java @@ -0,0 +1,62 @@ +package org.mage.test.cards.single.jou; + +import mage.constants.ManaType; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class KruphixGodOfHorizonsTest extends CardTestPlayerBase { + + private static final String kruphix = "Kruphix, God of Horizons"; + private static final String sliver = "Metallic Sliver"; + private static final String repeal = "Mystic Repeal"; + + @Test + public void testKruphixNormal() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + addCard(Zone.BATTLEFIELD, playerA, kruphix); + + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}"); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertManaPool(playerA, ManaType.COLORLESS, 3); + assertManaPool(playerA, ManaType.GREEN, 0); + assertPermanentCount(playerA, kruphix, 1); + } + + @Test + public void testKruphixRemoved() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + addCard(Zone.BATTLEFIELD, playerA, kruphix); + addCard(Zone.HAND, playerA, sliver); + addCard(Zone.HAND, playerA, repeal); + + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}"); + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, sliver); + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, repeal, kruphix); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertManaPool(playerA, ManaType.COLORLESS, 0); + assertManaPool(playerA, ManaType.GREEN, 0); + assertPermanentCount(playerA, sliver, 1); + assertPermanentCount(playerA, kruphix, 0); + assertTappedCount("Forest", true, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/khm/DraugrNecromancerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/khm/DraugrNecromancerTest.java new file mode 100644 index 000000000000..4c188d7bcb6d --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/khm/DraugrNecromancerTest.java @@ -0,0 +1,282 @@ +package org.mage.test.cards.single.khm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class DraugrNecromancerTest extends CardTestPlayerBase { + + private static final String necromancer = "Draugr Necromancer"; + private static final String bolt = "Lightning Bolt"; + private static final String bear = "Grizzly Bears"; + private static final String murder = "Murder"; + private static final String pair = "Curious Pair"; + private static final String birgi = "Birgi, God of Storytelling"; + private static final String harnfel = "Harnfel, Horn of Bounty"; + + @Test + public void testExilesWithCounter() { + addCard(Zone.BATTLEFIELD, playerA, necromancer); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.HAND, playerA, bolt); + addCard(Zone.BATTLEFIELD, playerB, bear); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, bear); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertExileCount(playerB, bear, 1); + assertCounterOnExiledCardCount(bear, CounterType.ICE, 1); + } + + @Test + public void testCastFromExile() { + addCard(Zone.BATTLEFIELD, playerA, necromancer); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, "Forest"); + addCard(Zone.HAND, playerA, bolt); + addCard(Zone.BATTLEFIELD, playerB, bear); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, bear); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, bear); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertExileCount(playerB, bear, 0); + assertPermanentCount(playerA, bear, 1); + } + + @Test + public void testCastFromExileWithSnow() { + addCard(Zone.BATTLEFIELD, playerA, necromancer); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, "Snow-Covered Swamp"); + addCard(Zone.HAND, playerA, bolt); + addCard(Zone.BATTLEFIELD, playerB, bear); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, bear); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, bear); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertExileCount(playerB, bear, 0); + assertPermanentCount(playerA, bear, 1); + } + + @Test + public void testCastFromExileWithoutSnow() { + addCard(Zone.BATTLEFIELD, playerA, necromancer); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.HAND, playerA, bolt); + addCard(Zone.BATTLEFIELD, playerB, bear); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, bear); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, bear); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertExileCount(playerB, bear, 1); + assertCounterOnExiledCardCount(bear, CounterType.ICE, 1); + assertPermanentCount(playerA, bear, 0); + } + + @Test + public void testCastAdventureCreatureFromExile() { + addCard(Zone.BATTLEFIELD, playerA, necromancer); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.HAND, playerA, bolt); + addCard(Zone.BATTLEFIELD, playerB, pair); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, pair); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, pair); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertExileCount(playerB, pair, 0); + assertPermanentCount(playerA, pair, 1); + } + + @Test + public void testCastAdventureSpellFromExile() { + addCard(Zone.BATTLEFIELD, playerA, necromancer); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Forest"); + addCard(Zone.HAND, playerA, bolt); + addCard(Zone.BATTLEFIELD, playerB, pair); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, pair); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Treats to Share"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertExileCount(playerB, pair, 1); + assertGraveyardCount(playerB, pair, 0); + assertPermanentCount(playerA, "Food", 1); + } + + @Test + public void testCastAdventureCreatureFromExileWithSnow() { + addCard(Zone.BATTLEFIELD, playerA, necromancer); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Snow-Covered Swamp", 2); + addCard(Zone.HAND, playerA, bolt); + addCard(Zone.BATTLEFIELD, playerB, pair); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, pair); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, pair); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertExileCount(playerB, pair, 0); + assertPermanentCount(playerA, pair, 1); + } + + @Test + public void testCastAdventureSpellFromExileWithSnow() { + addCard(Zone.BATTLEFIELD, playerA, necromancer); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Snow-Covered Swamp"); + addCard(Zone.HAND, playerA, bolt); + addCard(Zone.BATTLEFIELD, playerB, pair); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, pair); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Treats to Share"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertExileCount(playerB, pair, 1); + assertGraveyardCount(playerB, pair, 0); + assertPermanentCount(playerA, "Food", 1); + } + + @Test + public void testCastMDFCFrontFromExile() { + addCard(Zone.BATTLEFIELD, playerA, necromancer); + addCard(Zone.BATTLEFIELD, playerA, "Badlands", 6); // {B} or {R} + addCard(Zone.HAND, playerA, murder); + addCard(Zone.HAND, playerB, birgi); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, birgi); + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, murder, birgi); // {1}{B}{B} + + castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, birgi); // {2}{R} + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, birgi, 1); + assertGraveyardCount(playerB, birgi, 0); + assertExileCount(playerB, birgi, 0); + } + + @Test + public void testCastMDFCBackFromExile() { + addCard(Zone.BATTLEFIELD, playerA, necromancer); + addCard(Zone.BATTLEFIELD, playerA, "Badlands", 8); + addCard(Zone.HAND, playerA, murder); + addCard(Zone.HAND, playerB, birgi); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, birgi); + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, murder, birgi); + + castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, harnfel); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, harnfel, 1); + assertPermanentCount(playerA, birgi, 0); + assertGraveyardCount(playerB, birgi, 0); + assertExileCount(playerB, birgi, 0); + } + + @Test + public void testCastMDFCFrontFromExileWithSnow() { + addCard(Zone.BATTLEFIELD, playerA, necromancer); + addCard(Zone.BATTLEFIELD, playerA, "Snow-Covered Swamp", 6); + addCard(Zone.HAND, playerA, murder); + addCard(Zone.HAND, playerB, birgi); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, birgi); + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, murder, birgi); + + castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, birgi); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, birgi, 1); + assertGraveyardCount(playerB, birgi, 0); + assertExileCount(playerB, birgi, 0); + } + + @Test + public void testCastMDFCBackFromExileWithSnow() { + addCard(Zone.BATTLEFIELD, playerA, necromancer); + addCard(Zone.BATTLEFIELD, playerA, "Snow-Covered Swamp", 8); + addCard(Zone.HAND, playerA, murder); + addCard(Zone.HAND, playerB, birgi); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, birgi); + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, murder, birgi); + + castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, harnfel); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, harnfel, 1); + assertPermanentCount(playerA, birgi, 0); + assertGraveyardCount(playerB, birgi, 0); + assertExileCount(playerB, birgi, 0); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/khm/MoritteOfTheFrostTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/khm/MoritteOfTheFrostTest.java new file mode 100644 index 000000000000..cf2426167aa4 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/khm/MoritteOfTheFrostTest.java @@ -0,0 +1,112 @@ +package org.mage.test.cards.single.khm; + +import mage.cards.Card; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class MoritteOfTheFrostTest extends CardTestPlayerBase { + + @Test + public void test_MustBeAnyTypeInHand() { + // bug: can't cast by mana from Myr Reservoir + + // {T}: Add {C}{C}. Spend this mana only to cast Myr spells or activate abilities of Myr. + addCard(Zone.BATTLEFIELD, playerA, "Myr Reservoir"); + // + // Changeling (This card is every creature type.) + addCard(Zone.HAND, playerA, "Moritte of the Frost");// {2}{G}{U}{U} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + + // prepare mana + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {C}{C}"); + + // cast myr and remove to to graveyard due 0/0 + checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Moritte of the Frost", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Moritte of the Frost"); + setChoice(playerA, "No"); // no copy + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Moritte of the Frost", 1); // removed to graveyard due 0/0 + } + + @Test + public void test_MustBeAnyTypeOnBattlefield() { + // {T}: Add {C}{C}. Spend this mana only to cast Myr spells or activate abilities of Myr. + addCard(Zone.BATTLEFIELD, playerA, "Myr Reservoir"); + // + // Changeling (This card is every creature type.) + addCard(Zone.HAND, playerA, "Moritte of the Frost");// {2}{G}{U}{U} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + // + // Minion creatures get +1/+1. + addCard(Zone.BATTLEFIELD, playerA, "Balthor the Defiled", 1); + + // prepare mana + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {C}{C}"); + + // cast myr and keep on battlefield due boost + checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Moritte of the Frost", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Moritte of the Frost"); + setChoice(playerA, "No"); // no copy + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Moritte of the Frost", 1); // boosted +1/+1 + } + + @Test + public void test_MustBeAnyTypeAfterCopy() { + // {T}: Add {C}{C}. Spend this mana only to cast Myr spells or activate abilities of Myr. + addCard(Zone.BATTLEFIELD, playerA, "Myr Reservoir"); + // + // Changeling (This card is every creature type.) + // You may have Moritte of the Frost enter the battlefield as a copy of a permanent you control, + // except it's legendary and snow in addition to its other types and, if it's a creature, it enters + // with two additional +1/+1 counters on it and has changeling. + addCard(Zone.HAND, playerA, "Moritte of the Frost");// {2}{G}{U}{U} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + // + // Minion creatures get +1/+1. + addCard(Zone.BATTLEFIELD, playerA, "Balthor the Defiled", 1); + // + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); // 2/2 + + // prepare mana + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {C}{C}"); + + // cast myr and copy + checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Moritte of the Frost", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Moritte of the Frost"); + setChoice(playerA, "Yes"); // use copy + setChoice(playerA, "Grizzly Bears"); // copy target + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Grizzly Bears", 2); + + // boosted by copy and balthor + Card card = currentGame.getBattlefield().getAllActivePermanents().stream().filter(p -> p.isCopy()).findFirst().orElse(null); + Assert.assertNotNull("Can't find copy", card); + Assert.assertEquals("Copy power", 2 + 2 + 1, card.getPower().getValue()); + Assert.assertEquals("Copy Toughness", 2 + 2 + 1, card.getToughness().getValue()); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/khm/ValkiGodOfLiesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/khm/ValkiGodOfLiesTest.java new file mode 100644 index 000000000000..77ed48941e8c --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/khm/ValkiGodOfLiesTest.java @@ -0,0 +1,35 @@ +package org.mage.test.cards.single.khm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class ValkiGodOfLiesTest extends CardTestPlayerBase { + + @Test + public void ephmerateTest() { + removeAllCardsFromLibrary(playerB); + + addCard(Zone.BATTLEFIELD, playerA, "Badlands", 7); + addCard(Zone.HAND, playerA, "Plains"); + addCard(Zone.HAND, playerA, "Valki, God of Lies"); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); + addCard(Zone.LIBRARY, playerB, "Ephemerate"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tibalt, Cosmic Impostor"); + setChoice(playerA, "Tibalt, Cosmic Impostor"); // two etb effects + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+2: Exile the top card of each player's library."); + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Plains"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ephemerate", "Grizzly Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Tibalt, Cosmic Impostor", 1); + assertPermanentCount(playerA, "Grizzly Bears", 1); + assertGraveyardCount(playerB, "Ephemerate", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/AquitectsWillTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/AquitectsWillTest.java new file mode 100644 index 000000000000..a22f25f4787e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/AquitectsWillTest.java @@ -0,0 +1,91 @@ +package org.mage.test.cards.single.lrw; + +import mage.constants.PhaseStep; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class AquitectsWillTest extends CardTestPlayerBase { + + private static final String mountain = "Mountain"; + private static final String will = "Aquitect's Will"; + private static final String recall = "Ancestral Recall"; + private static final String hexmage = "Vampire Hexmage"; + + @Test + public void testProduceBlueDuringCast() { + addCard(Zone.BATTLEFIELD, playerA, "Island"); + addCard(Zone.BATTLEFIELD, playerA, mountain); + addCard(Zone.HAND, playerA, will); + addCard(Zone.HAND, playerA, recall); + + setStrictChooseMode(true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, will, mountain); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, recall, playerA); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertHandCount(playerA, 3); + assertGraveyardCount(playerA, will, 1); + assertGraveyardCount(playerA, recall, 1); + assertCounterCount(playerA, mountain, CounterType.FLOOD, 1); + assertSubtype(mountain, SubType.MOUNTAIN); + assertSubtype(mountain, SubType.ISLAND); + } + + @Test + public void testProduceBlueOutsideCast() { + addCard(Zone.BATTLEFIELD, playerA, "Island"); + addCard(Zone.BATTLEFIELD, playerA, mountain); + addCard(Zone.HAND, playerA, will); + addCard(Zone.HAND, playerA, recall); + + setStrictChooseMode(true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, will, mountain); + + activateManaAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: Add {U}"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, recall, playerA); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertHandCount(playerA, 3); + assertGraveyardCount(playerA, will, 1); + assertGraveyardCount(playerA, recall, 1); + assertCounterCount(playerA, mountain, CounterType.FLOOD, 1); + assertSubtype(mountain, SubType.MOUNTAIN); + assertSubtype(mountain, SubType.ISLAND); + } + + @Test + public void testEffectTiedToCounter() { + addCard(Zone.BATTLEFIELD, playerA, "Island"); + addCard(Zone.BATTLEFIELD, playerA, mountain); + addCard(Zone.BATTLEFIELD, playerA, hexmage); + addCard(Zone.HAND, playerA, will); + + setStrictChooseMode(true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, will, mountain); + + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Sacrifice", mountain); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, will, 1); + assertGraveyardCount(playerA, hexmage, 1); + assertCounterCount(playerA, mountain, CounterType.FLOOD, 0); + assertSubtype(mountain, SubType.MOUNTAIN); + assertNotSubtype(mountain, SubType.ISLAND); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/MulldrifterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/MulldrifterTest.java index 70d3a391f872..c8b63068bf56 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/MulldrifterTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/MulldrifterTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.single.lrw; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m10/MindShatterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m10/MindShatterTest.java index 1de37d2ddd9c..fbe83c5c7d67 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/m10/MindShatterTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m10/MindShatterTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.single.m10; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m11/NecroticPlagueTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m11/NecroticPlagueTest.java index 4908839d0cf3..1a8cb041f2eb 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/m11/NecroticPlagueTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m11/NecroticPlagueTest.java @@ -22,8 +22,10 @@ public void testCard1() { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Necrotic Plague", "Sejiri Merfolk"); + setStrictChooseMode(true); setStopAt(2, PhaseStep.PRECOMBAT_MAIN); execute(); + assertAllCommandsUsed(); assertLife(playerA, 20); assertLife(playerB, 20); @@ -38,7 +40,7 @@ public void testCard2() { addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4); /** - * Goblin Deathraiders English + * Goblin Deathraiders * Creature — Goblin Warrior 3/1, BR * Trample */ @@ -63,9 +65,12 @@ public void testCard2() { addCard(Zone.BATTLEFIELD, playerB, "Sejiri Merfolk"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Necrotic Plague", "Sejiri Merfolk"); + addTarget(playerB, "Goblin Deathraiders"); // target for new necro attach + setStrictChooseMode(true); setStopAt(3, PhaseStep.PRECOMBAT_MAIN); execute(); + assertAllCommandsUsed(); assertLife(playerA, 20); assertLife(playerB, 20); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m14/ChandraPyromasterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m14/ChandraPyromasterTest.java index 33db189545b4..63ea393f6739 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/m14/ChandraPyromasterTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m14/ChandraPyromasterTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.single.m14; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/PyromancersGauntletTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m14/PyromancersGauntletTest.java similarity index 99% rename from Mage.Tests/src/test/java/PyromancersGauntletTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/m14/PyromancersGauntletTest.java index 6e0c6569b806..1d6d865d3f01 100644 --- a/Mage.Tests/src/test/java/PyromancersGauntletTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m14/PyromancersGauntletTest.java @@ -1,3 +1,4 @@ +package org.mage.test.cards.single.m14; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ArchfiendsVesselTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ArchfiendsVesselTest.java index cb1f0923b049..d151dfd5f753 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ArchfiendsVesselTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ArchfiendsVesselTest.java @@ -26,8 +26,9 @@ public void enterFromGraveyard(){ assertAllCommandsUsed(); assertPermanentCount(playerA, "Demon", 1); + assertPermanentCount(playerA, archfiendsVessel, 0); assertExileCount(playerA, archfiendsVessel, 1); - + assertGraveyardCount(playerA, archfiendsVessel, 0); } @Test @@ -48,7 +49,33 @@ public void playFromGraveyard(){ assertAllCommandsUsed(); assertPermanentCount(playerA, "Demon", 1); + assertPermanentCount(playerA, archfiendsVessel, 0); assertExileCount(playerA, archfiendsVessel, 1); + assertGraveyardCount(playerA, archfiendsVessel, 0); + } + + @Test + public void diesOnStack() { + // If Archfiend’s Vessel leaves the battlefield while its triggered ability is on the stack, + // you can’t exile it from the zone it’s put into, so you won’t create a Demon. + + addCard(Zone.GRAVEYARD, playerA, archfiendsVessel); + addCard(Zone.HAND, playerA, "Exhume"); + addCard(Zone.HAND, playerA, "Fatal Push"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Exhume"); + addTarget(playerA, archfiendsVessel); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fatal Push"); + addTarget(playerA, archfiendsVessel); + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + assertPermanentCount(playerA, "Demon", 0); + assertPermanentCount(playerA, archfiendsVessel, 0); + assertExileCount(playerA, archfiendsVessel, 0); + assertGraveyardCount(playerA, archfiendsVessel, 1); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AzusaLostButSeekingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AzusaLostButSeekingTest.java index bd7ac3587239..d768912473a1 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AzusaLostButSeekingTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AzusaLostButSeekingTest.java @@ -2,7 +2,6 @@ import mage.constants.PhaseStep; import mage.constants.Zone; -import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -11,8 +10,7 @@ public class AzusaLostButSeekingTest extends CardTestPlayerBase { private static final String azusa = "Azusa, Lost but Seeking"; @Test - @Ignore - public void playAdditionalLands(){ + public void playAdditionalLands() { addCard(Zone.BATTLEFIELD, playerA, azusa); addCard(Zone.HAND, playerA, "Forest", 4); @@ -21,10 +19,8 @@ public void playAdditionalLands(){ playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Forest"); playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Forest"); - setStrictChooseMode(true); setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); execute(); - assertAllCommandsUsed(); assertPermanentCount(playerA, "Forest", 3); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/DemonicEmbraceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/DemonicEmbraceTest.java index 84b1b8cea292..5e4a5ca29b51 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/DemonicEmbraceTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/DemonicEmbraceTest.java @@ -2,14 +2,12 @@ import mage.constants.PhaseStep; import mage.constants.Zone; -import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; public class DemonicEmbraceTest extends CardTestPlayerBase { @Test - @Ignore public void playFromGraveyard() { // Enchanted creature gets +3/+1, has flying, and is a Demon in addition to its other types. // You may cast Demonic Embrace from your graveyard by paying 3 life and discarding a card in addition to paying its other costs. @@ -19,11 +17,14 @@ public void playFromGraveyard() { addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Demonic Embrace", "Grizzly Bears"); - //addTarget(playerA, ); addTarget(playerA, "Mountain"); - setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); execute(); - assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Demonic Embrace", 1); + assertPowerToughness(playerA, "Grizzly Bears", 5, 3); + assertGraveyardCount(playerA, "Mountain", 1); + assertLife(playerA, 20 - 3); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mmq/CallerOfTheHuntTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mmq/CallerOfTheHuntTest.java new file mode 100644 index 000000000000..acf689217e3a --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mmq/CallerOfTheHuntTest.java @@ -0,0 +1,60 @@ +package org.mage.test.cards.single.mmq; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps; + +/** + * @author JayDi85 + */ + +public class CallerOfTheHuntTest extends CardTestPlayerBaseWithAIHelps { + + @Test + public void test_Play_Manual() { + // As an additional cost to cast Caller of the Hunt, choose a creature type. + // Caller of the Hunt's power and toughness are each equal to the number of creatures of the chosen type on the battlefield. + addCard(Zone.HAND, playerA, "Caller of the Hunt"); // {2}{G} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + // + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); + addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears", 2); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Archaeologist", 2); + + // cast Caller of the Hunt and choose bear as a type (+3 boost) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Caller of the Hunt"); + setChoice(playerA, "Bear"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Caller of the Hunt", 1); + assertPowerToughness(playerA, "Caller of the Hunt", 3, 3); // +3 boost + } + + @Test + public void test_Play_AI() { + // As an additional cost to cast Caller of the Hunt, choose a creature type. + // Caller of the Hunt's power and toughness are each equal to the number of creatures of the chosen type on the battlefield. + addCard(Zone.HAND, playerA, "Caller of the Hunt"); // {2}{G} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + // + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); + addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears", 2); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Archaeologist", 2); + + // ai must cast Caller of the Hunt and choose bear as a type (+3 boost) + aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Caller of the Hunt", 1); + assertPowerToughness(playerA, "Caller of the Hunt", 3, 3); // +3 boost + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mor/StinkdrinkerBanditTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mor/StinkdrinkerBanditTest.java index db83ca8ae48e..e6b5ddf96f63 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/mor/StinkdrinkerBanditTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mor/StinkdrinkerBanditTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.single.mor; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ogw/EldraziObligatorTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ogw/EldraziObligatorTest.java index e4a4d057edc9..796917290143 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ogw/EldraziObligatorTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ogw/EldraziObligatorTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.single.ogw; import mage.abilities.keyword.HasteAbility; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ogw/RealitySmasherTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ogw/RealitySmasherTest.java index 835b644f1ff9..246c932a9a39 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ogw/RealitySmasherTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ogw/RealitySmasherTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.single.ogw; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ogw/ThoughtKnotSeerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ogw/ThoughtKnotSeerTest.java index 3e5165327500..3181e2de7e98 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ogw/ThoughtKnotSeerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ogw/ThoughtKnotSeerTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.single.ogw; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/rtr/NivmagusElementalTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/rtr/NivmagusElementalTest.java new file mode 100644 index 000000000000..00f14b7ca39f --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/rtr/NivmagusElementalTest.java @@ -0,0 +1,59 @@ +package org.mage.test.cards.single.rtr; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class NivmagusElementalTest extends CardTestPlayerBase { + + private static final String nivmagus = "Nivmagus Elemental"; + private static final String bolt = "Lightning Bolt"; + private static final String grapeshot = "Grapeshot"; + + @Test + public void testNivmagusElemental() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, nivmagus); + addCard(Zone.HAND, playerA, bolt); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, playerB); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Exile"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, nivmagus, CounterType.P1P1, 2); + assertLife(playerB, 20); + assertExileCount(playerA, bolt, 1); + } + + @Test + public void testNivmagusElementalStorm() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerA, nivmagus); + addCard(Zone.HAND, playerA, bolt); + addCard(Zone.HAND, playerA, grapeshot); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, playerB); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Exile"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, grapeshot, playerB); + setChoice(playerA, "No"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Exile"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Exile"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, nivmagus, CounterType.P1P1, 6); + assertLife(playerB, 20); + assertExileCount(playerA, bolt, 1); + assertExileCount(playerA, grapeshot, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/soi/AsylumVisitorTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/soi/AsylumVisitorTest.java index 78b165753b00..d7363b7632a2 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/soi/AsylumVisitorTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/soi/AsylumVisitorTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.single.soi; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/soi/KindlyStrangerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/soi/KindlyStrangerTest.java index cd4b3256d126..6d54d05dd561 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/soi/KindlyStrangerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/soi/KindlyStrangerTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.single.soi; import mage.constants.PhaseStep; @@ -30,7 +25,7 @@ public void transformDestroyCreature() { addCard(Zone.GRAVEYARD, playerA, "Bitterblossom", 1); addCard(Zone.BATTLEFIELD, playerB, "Hill Giant", 1); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{B}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Delirium"); addTarget(playerA, "Hill Giant"); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/soi/NightSideCMCTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/soi/NightSideCMCTest.java index d57124bf60c7..826b6c7a6775 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/soi/NightSideCMCTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/soi/NightSideCMCTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.single.soi; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/soi/SilverfurPartisanTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/soi/SilverfurPartisanTest.java index 723ce43dc198..93cf91f4ebbc 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/soi/SilverfurPartisanTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/soi/SilverfurPartisanTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.single.soi; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/som/NimDeathmantleTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/som/NimDeathmantleTest.java index f12c796289e8..4452683c25cc 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/som/NimDeathmantleTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/som/NimDeathmantleTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.single.som; import mage.abilities.keyword.IntimidateAbility; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/stx/BalefulMasteryTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/stx/BalefulMasteryTest.java new file mode 100644 index 000000000000..155b7fd98f46 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/stx/BalefulMasteryTest.java @@ -0,0 +1,182 @@ +package org.mage.test.cards.single.stx; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author htrajan, JayDi85 + */ +public class BalefulMasteryTest extends CardTestPlayerBase { + + @Test + public void test_BalefulMastery_NormalCost() { + // You may pay {1}{B} rather than pay this spell's mana cost. + // If the {1}{B} cost was paid, an opponent draws a card. + // Exile target creature or planeswalker. + addCard(Zone.HAND, playerA, "Baleful Mastery"); // {3}{B} + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4); + // + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker"); + addCard(Zone.BATTLEFIELD, playerB, "Witchbane Orb"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Baleful Mastery", "Goblin Piker"); + setChoice(playerA, "No"); // use normal cost + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertHandCount(playerA, 0); + assertHandCount(playerB, 0); + assertExileCount(playerB, "Goblin Piker", 1); + } + + @Test + public void test_BalefulMastery_AlternativeCost() { + // You may pay {1}{B} rather than pay this spell's mana cost. + // If the {1}{B} cost was paid, an opponent draws a card. + // Exile target creature or planeswalker. + addCard(Zone.HAND, playerA, "Baleful Mastery"); // {3}{B} + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker"); + addCard(Zone.BATTLEFIELD, playerB, "Witchbane Orb"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Baleful Mastery", "Goblin Piker"); + setChoice(playerA, "Yes"); // use alternative cost + addTarget(playerA, playerB); // select opponent + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertHandCount(playerA, 0); + assertHandCount(playerB, 1); // +1 from cost's draw + assertExileCount(playerB, "Goblin Piker", 1); + } + + @Test + public void test_BalefulMastery_DoubleCast() { + // You may pay {1}{B} rather than pay this spell's mana cost. + // If the {1}{B} cost was paid, an opponent draws a card. + // Exile target creature or planeswalker. + addCard(Zone.HAND, playerA, "Baleful Mastery", 2); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2 + 4); // 1x normal, 1x alternative + // + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker"); + addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears"); + + // cast 1 - alternative + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Baleful Mastery", "Goblin Piker"); + setChoice(playerA, "Yes"); // use alternative cost + addTarget(playerA, playerB); // select opponent + + // cast 2 - normal + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Baleful Mastery", "Grizzly Bears"); + setChoice(playerA, "No"); // normal cast + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertHandCount(playerA, 0); + assertHandCount(playerB, 1); + assertExileCount(playerB, "Goblin Piker", 1); + assertExileCount(playerB, "Grizzly Bears", 1); + } + + @Test + public void test_BalefulMastery_BlinkMustResetAlternativeCost() { + addCustomEffect_ReturnFromAnyToHand(playerA); + + // You may pay {1}{B} rather than pay this spell's mana cost. + // If the {1}{B} cost was paid, an opponent draws a card. + // Exile target creature or planeswalker. + addCard(Zone.HAND, playerA, "Baleful Mastery"); // {3}{B} + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2 + 4); // 1x normal, 1x alternative + // + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker"); + addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears"); + + // cast 1 - with alternative + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Baleful Mastery", "Goblin Piker"); + setChoice(playerA, "Yes"); // use alternative cost + addTarget(playerA, playerB); // select opponent + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("after cast 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Baleful Mastery", 1); + checkHandCount("after cast 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 0); + checkHandCount("after cast 1", 1, PhaseStep.PRECOMBAT_MAIN, playerB, 1); // +1 from cost's draw + checkExileCount("after cast 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Goblin Piker", 1); + checkExileCount("after cast 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 0); + + // return to hand + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "return from graveyard"); + addTarget(playerA, "Baleful Mastery"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkHandCardCount("after return", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Baleful Mastery", 1); + + // cast 2 - without alternative + // possible bug: cost status can be found from previous object (e.g. it ask about opponent select, but must not) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Baleful Mastery", "Grizzly Bears"); + setChoice(playerA, "No"); // do not use alternative cost + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("after cast 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Baleful Mastery", 1); + checkHandCount("after cast 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 0); + checkHandCount("after cast 2", 1, PhaseStep.PRECOMBAT_MAIN, playerB, 1); // no draws on cast 2 + checkExileCount("after cast 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Goblin Piker", 1); + checkExileCount("after cast 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_BalefulMastery_CopyMustKeepAlternativeCost() { + // You may pay {1}{B} rather than pay this spell's mana cost. + // If the {1}{B} cost was paid, an opponent draws a card. + // Exile target creature or planeswalker. + addCard(Zone.HAND, playerA, "Baleful Mastery"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // + // Copy target instant or sorcery spell. You may choose new targets for the copy. + addCard(Zone.HAND, playerA, "Twincast"); // {U}{U} + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + // + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker"); + addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears"); + + // cast with alternative + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {B}", 2); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Baleful Mastery", "Goblin Piker"); + setChoice(playerA, "Yes"); // use alternative cost + // copy spell + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Twincast", "Cast Baleful Mastery", "Cast Baleful Mastery"); + setChoice(playerA, "Yes"); // change target + addTarget(playerA, "Grizzly Bears"); // new target + checkStackSize("before copy", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA, true); + checkStackSize("after copy", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2); + // + // resolve copied spell + // possible bug: alternative cost will be lost for copied spell, so no opponent selections + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA, true); + addTarget(playerA, playerB); // select opponent + checkStackSize("after copy resolve", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 1); + // resolve original spell + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA, true); + addTarget(playerA, playerB); // select opponent + checkStackSize("after original resolve", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 0); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/stx/PlumbTheForbiddenTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/stx/PlumbTheForbiddenTest.java new file mode 100644 index 000000000000..323b578d4862 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/stx/PlumbTheForbiddenTest.java @@ -0,0 +1,40 @@ +package org.mage.test.cards.single.stx; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class PlumbTheForbiddenTest extends CardTestPlayerBase { + + private static final String plumb = "Plumb the Forbidden"; + private static final String bear = "Grizzly Bears"; + private static final String lion = "Silvercoat Lion"; + private static final String corpse = "Walking Corpse"; + + @Test + public void testPlumbTheForbidden() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + addCard(Zone.BATTLEFIELD, playerA, bear); + addCard(Zone.BATTLEFIELD, playerA, lion); + addCard(Zone.BATTLEFIELD, playerA, corpse); + addCard(Zone.HAND, playerA, plumb); + + setChoice(playerA, String.join("^", bear, lion, corpse)); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, plumb); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, bear, 1); + assertGraveyardCount(playerA, lion, 1); + assertGraveyardCount(playerA, corpse, 1); + assertGraveyardCount(playerA, plumb, 1); + assertLife(playerA, 20 - 4); + assertHandCount(playerA, 4); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/stx/RadiantScrollwielderTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/stx/RadiantScrollwielderTest.java new file mode 100644 index 000000000000..60990fafb7d9 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/stx/RadiantScrollwielderTest.java @@ -0,0 +1,33 @@ +package org.mage.test.cards.single.stx; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class RadiantScrollwielderTest extends CardTestPlayerBase { + + private static final String wielder = "Radiant Scrollwielder"; + private static final String bolt = "Lightning Bolt"; + + @Test + public void testExileCastExile() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, wielder); + addCard(Zone.GRAVEYARD, playerA, bolt); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, playerB); + +// setStrictChooseMode(true); currently doesn't work as computer player doesn't allow random targeting + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 20 + 3); + assertLife(playerB, 20 - 3); + assertExileCount(playerA, bolt, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/stx/StarPupilTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/stx/StarPupilTest.java new file mode 100644 index 000000000000..807e93782c49 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/stx/StarPupilTest.java @@ -0,0 +1,34 @@ +package org.mage.test.cards.single.stx; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class StarPupilTest extends CardTestPlayerBase { + + private static final String pupil = "Star Pupil"; + private static final String murder = "Murder"; + private static final String lion = "Silvercoat Lion"; + + @Test + public void testPupil() { + addCard(Zone.BATTLEFIELD, playerA, "Scrubland", 4); + addCard(Zone.BATTLEFIELD, playerA, lion); + addCard(Zone.HAND, playerA, pupil); + addCard(Zone.HAND, playerA, murder); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, pupil); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, murder, pupil); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(lion, CounterType.P1P1, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ths/AshiokNightmareWaeverTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ths/AshiokNightmareWaeverTest.java index 3e668eb6f398..4ad535a8301b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ths/AshiokNightmareWaeverTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ths/AshiokNightmareWaeverTest.java @@ -51,7 +51,7 @@ public void testSecondAbility() { skipInitShuffling(); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+2: Exile the top three cards of target opponent's library.", playerB); - activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "-X: Put a creature card with converted mana cost X exiled with {this} onto the battlefield under your control. That creature is a Nightmare in addition to its other types."); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "-X: Put a creature card with mana value X exiled with {this} onto the battlefield under your control. That creature is a Nightmare in addition to its other types."); setChoice(playerA, "X=5"); addTarget(playerA, "Prophet of Kruphix"); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/tor/RadiateTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/tor/RadiateTest.java index b733059c789c..feb3555b2d66 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/tor/RadiateTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/tor/RadiateTest.java @@ -26,13 +26,13 @@ public void test_Play_Manual() { addCard(Zone.BATTLEFIELD, playerB, "Kitesail Corsair", 2); // cast bolt and copy spell for each another target + setChoice(playerA, TestPlayer.CHOICE_SKIP); // skip stack order castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Radiate", "Lightning Bolt", "Lightning Bolt"); checkStackSize("before radiate", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2); waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true); // must have: 2x for corsairs, 2x for bears, 1x for A checkStackSize("after radiate", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 1 + 5); - addTarget(playerA, TestPlayer.TARGET_SKIP); setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); @@ -47,7 +47,7 @@ public void test_Play_Manual() { @Test public void test_Play_AI() { - // possible bug: game freeze or Target wasn't handled... TargetWithAdditionalFilter + // This test has trouble now but the manual version works // Choose target instant or sorcery spell that targets only a single permanent or player. Copy that spell // for each other permanent or player the spell could target. Each copy targets a different one of those diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/usg/NoRestForTheWickedTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/usg/NoRestForTheWickedTest.java index 3d5848c27017..9c35131c8f3b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/usg/NoRestForTheWickedTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/usg/NoRestForTheWickedTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.single.usg; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/vis/DesertionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/vis/DesertionTest.java new file mode 100644 index 000000000000..55f8ac926707 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/vis/DesertionTest.java @@ -0,0 +1,38 @@ +package org.mage.test.cards.single.vis; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class DesertionTest extends CardTestPlayerBase { + + @Test + public void test_MultipleCounter() { + // possible bug: error NPE if target spell already countered before resolve + + // Counter target spell. + // If an artifact or creature spell is countered this way, put that card onto the battlefield under your + // control instead of into its owner's graveyard. + addCard(Zone.HAND, playerA, "Desertion", 2); // {3}{U}{U} + addCard(Zone.BATTLEFIELD, playerA, "Island", 2 * 5); + // + addCard(Zone.HAND, playerA, "Grizzly Bears"); // {1}{G} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + + // counter same spell 2x times + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Desertion", "Grizzly Bears", "Cast Grizzly Bears"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Desertion", "Grizzly Bears", "Cast Grizzly Bears"); + checkStackObject("before resolve", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grizzly Bears", 1); + checkStackObject("before resolve", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Desertion", 2); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Grizzly Bears", 1); + assertGraveyardCount(playerA, "Desertion", 2); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/war/KioraTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/war/KioraTest.java index 18ba5479c256..e33a064f4ada 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/war/KioraTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/war/KioraTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.single.war; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/targets/TargetMultiAmountTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/targets/TargetMultiAmountTest.java new file mode 100644 index 000000000000..e2661e4c35fc --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/targets/TargetMultiAmountTest.java @@ -0,0 +1,268 @@ +package org.mage.test.cards.targets; + +import mage.constants.MultiAmountType; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.player.TestPlayer; +import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author JayDi85 + */ + +public class TargetMultiAmountTest extends CardTestPlayerBaseWithAIHelps { + + @Test + public void test_DefaultValues() { + // default values must be assigned from first to last by minimum values + assertDefaultValues("", 0, 0, 0); + // + assertDefaultValues("0", 1, 0, 0); + assertDefaultValues("0 0", 2, 0, 0); + assertDefaultValues("0 0 0", 3, 0, 0); + // + assertDefaultValues("1", 1, 1, 1); + assertDefaultValues("1 0", 2, 1, 1); + assertDefaultValues("1 0 0", 3, 1, 1); + // + assertDefaultValues("1", 1, 1, 2); + assertDefaultValues("1 0", 2, 1, 2); + assertDefaultValues("1 0 0", 3, 1, 2); + // + assertDefaultValues("2", 1, 2, 2); + assertDefaultValues("2 0", 2, 2, 2); + assertDefaultValues("2 0 0", 3, 2, 2); + // + assertDefaultValues("2", 1, 2, 10); + assertDefaultValues("2 0", 2, 2, 10); + assertDefaultValues("2 0 0", 3, 2, 10); + // + // performance test + assertDefaultValues("2 0 0", 3, 2, Integer.MAX_VALUE); + } + + private void assertDefaultValues(String need, int count, int min, int max) { + List defaultValues = MultiAmountType.prepareDefaltValues(count, min, max); + String current = defaultValues + .stream() + .map(String::valueOf) + .collect(Collectors.joining(" ")); + Assert.assertEquals("default values", need, current); + Assert.assertTrue("default values must be good", MultiAmountType.isGoodValues(defaultValues, count, min, max)); + } + + @Test + public void test_MaxValues() { + // max possible values must be assigned from first to last by max possible values + assertMaxValues("", 0, 0, 0); + // + assertMaxValues("0", 1, 0, 0); + assertMaxValues("0 0", 2, 0, 0); + assertMaxValues("0 0 0", 3, 0, 0); + // + assertMaxValues("1", 1, 1, 1); + assertMaxValues("1 0", 2, 1, 1); + assertMaxValues("1 0 0", 3, 1, 1); + // + assertMaxValues("2", 1, 1, 2); + assertMaxValues("1 1", 2, 1, 2); + assertMaxValues("1 1 0", 3, 1, 2); + // + assertMaxValues("2", 1, 2, 2); + assertMaxValues("1 1", 2, 2, 2); + assertMaxValues("1 1 0", 3, 2, 2); + // + assertMaxValues("10", 1, 2, 10); + assertMaxValues("5 5", 2, 2, 10); + assertMaxValues("4 3 3", 3, 2, 10); + // + assertMaxValues("1 1 1 1 1 0 0 0 0 0", 10, 2, 5); + // + // performance test + assertMaxValues(String.valueOf(Integer.MAX_VALUE), 1, 2, Integer.MAX_VALUE); + int part = Integer.MAX_VALUE / 3; + String need = String.format("%d %d %d", part + 1, part, part); + assertMaxValues(need, 3, 2, Integer.MAX_VALUE); + } + + private void assertMaxValues(String need, int count, int min, int max) { + List maxValues = MultiAmountType.prepareMaxValues(count, min, max); + String current = maxValues + .stream() + .map(String::valueOf) + .collect(Collectors.joining(" ")); + Assert.assertEquals("max values", need, current); + Assert.assertTrue("max values must be good", MultiAmountType.isGoodValues(maxValues, count, min, max)); + } + + @Test + public void test_GoodValues() { + // good values are checking in test_DefaultValues, it's an additional + List list = MultiAmountType.prepareDefaltValues(3, 0, 0); + + // count (0, 0, 0) + Assert.assertFalse("count", MultiAmountType.isGoodValues(list, 0, 0, 0)); + Assert.assertFalse("count", MultiAmountType.isGoodValues(list, 1, 0, 0)); + Assert.assertFalse("count", MultiAmountType.isGoodValues(list, 2, 0, 0)); + Assert.assertTrue("count", MultiAmountType.isGoodValues(list, 3, 0, 0)); + Assert.assertFalse("count", MultiAmountType.isGoodValues(list, 4, 0, 0)); + + // min (0, 1, 1) + list.set(0, 0); + list.set(1, 1); + list.set(2, 1); + Assert.assertTrue("min", MultiAmountType.isGoodValues(list, 3, 0, 100)); + Assert.assertTrue("min", MultiAmountType.isGoodValues(list, 3, 1, 100)); + Assert.assertTrue("min", MultiAmountType.isGoodValues(list, 3, 2, 100)); + Assert.assertFalse("min", MultiAmountType.isGoodValues(list, 3, 3, 100)); + Assert.assertFalse("min", MultiAmountType.isGoodValues(list, 3, 4, 100)); + + // max (0, 1, 1) + list.set(0, 0); + list.set(1, 1); + list.set(2, 1); + Assert.assertFalse("max", MultiAmountType.isGoodValues(list, 3, 0, 0)); + Assert.assertFalse("max", MultiAmountType.isGoodValues(list, 3, 0, 1)); + Assert.assertTrue("max", MultiAmountType.isGoodValues(list, 3, 0, 2)); + Assert.assertTrue("max", MultiAmountType.isGoodValues(list, 3, 0, 3)); + Assert.assertTrue("max", MultiAmountType.isGoodValues(list, 3, 0, 4)); + } + + @Test + public void test_Parse() { + // parse must use correct values on good data or default values on broken data + + // simple parse without data check + assertParse("", 3, 1, 3, "", false); + assertParse("1", 3, 1, 3, "1", false); + assertParse("0 0 0", 3, 1, 3, "0 0 0", false); + assertParse("1 0 3", 3, 1, 3, "1 0 3", false); + assertParse("0 5 0 6", 3, 1, 3, "1,text 5 4. 6", false); + + // parse with data check - good data + assertParse("1 0 2", 3, 0, 3, "1 0 2", true); + + // parse with data check - broken data (must return defalt - 1 0 0) + assertParse("1 0 0", 3, 1, 3, "", true); + assertParse("1 0 0", 3, 1, 3, "1", true); + assertParse("1 0 0", 3, 1, 3, "0 0 0", true); + assertParse("1 0 0", 3, 1, 3, "1 0 3", true); + assertParse("1 0 0", 3, 1, 3, "1,text 4.", true); + } + + private void assertParse(String need, int count, int min, int max, String answerToParse, Boolean returnDefaultOnError) { + List parsedValues = MultiAmountType.parseAnswer(answerToParse, count, min, max, returnDefaultOnError); + String current = parsedValues + .stream() + .map(String::valueOf) + .collect(Collectors.joining(" ")); + Assert.assertEquals("parsed values", need, current); + if (returnDefaultOnError) { + Assert.assertTrue("parsed values must be good", MultiAmountType.isGoodValues(parsedValues, count, min, max)); + } + } + + @Test + public void test_Mana_Manamorphose_Manual() { + removeAllCardsFromHand(playerA); + + // Add two mana in any combination of colors. + // Draw a card. + addCard(Zone.HAND, playerA, "Manamorphose", 2); // {1}{R/G} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2 * 2); + + // cast and select {B}{B} + // one type of choices: wubrg order + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Manamorphose"); + setChoiceAmount(playerA, 0); // W + setChoiceAmount(playerA, 0); // U + setChoiceAmount(playerA, 2); // B + setChoice(playerA, TestPlayer.CHOICE_SKIP); // skip RG + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkManaPool("after first cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "B", 2); + + // cast and select {R}{G} + // another type of choices + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Manamorphose"); + setChoiceAmount(playerA, 0, 0, 0, 1, 1); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + checkManaPool("after second cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 1); + checkManaPool("after second cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "G", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Mana_Manamorphose_AI() { + removeAllCardsFromHand(playerA); + + // Add two mana in any combination of colors. + // Draw a card. + addCard(Zone.HAND, playerA, "Manamorphose", 1); // {1}{R/G} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + + // cast, but AI must select first manas (WU) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Manamorphose"); + aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkManaPool("after ai cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "W", 1); + checkManaPool("after ai cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "U", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Damage_Boulderfall_Manual() { + // Boulderfall deals 5 damage divided as you choose among any number of target creatures and/or players. + addCard(Zone.HAND, playerA, "Boulderfall", 1); // {6}{R}{R} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 8); + // + addCard(Zone.BATTLEFIELD, playerA, "Kitesail Corsair@bear", 3); // 2/1 + + // distribute 4x + 1x damage (kill two creatures) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Boulderfall"); + addTargetAmount(playerA, "@bear.1", 4); + addTargetAmount(playerA, "@bear.2", 1); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "@bear.1", 0); + checkPermanentCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "@bear.2", 0); + checkPermanentCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "@bear.3", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Damage_Boulderfall_AI() { + // AI don't use multi amount dialogs like human (it's just one target amount choose/simulation) + + // Boulderfall deals 5 damage divided as you choose among any number of target creatures and/or players. + addCard(Zone.HAND, playerA, "Boulderfall", 1); // {6}{R}{R} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 8); + // + addCard(Zone.BATTLEFIELD, playerB, "Kitesail Corsair", 6); // 2/1 + + // play card and distribute damage by game simulations for best score (kills 5x creatures) + aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Kitesail Corsair", 5); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/targets/TargetRestrictionsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/targets/TargetRestrictionsTest.java index 605de74d77a6..d530b6e79699 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/targets/TargetRestrictionsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/targets/TargetRestrictionsTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.targets; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BiovisionaryTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BiovisionaryTest.java index bdce917bf741..4ba7acdb2b32 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BiovisionaryTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BiovisionaryTest.java @@ -1,9 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - package org.mage.test.cards.triggers; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BroodSliverTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BroodSliverTest.java index 92fb807605be..350798e27c20 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BroodSliverTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BroodSliverTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.triggers; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/GripOfChaosTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/GripOfChaosTest.java index 5ab1c0c851fb..e06012092049 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/GripOfChaosTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/GripOfChaosTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.triggers; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/WorldgorgerDragonTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/WorldgorgerDragonTest.java index 8550b9c08127..fe7b4515d3b5 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/WorldgorgerDragonTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/WorldgorgerDragonTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.triggers; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ZadaHedronGrinderTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ZadaHedronGrinderTest.java index 2152c47107ad..73a8f8d72bf8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ZadaHedronGrinderTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ZadaHedronGrinderTest.java @@ -4,6 +4,7 @@ import mage.constants.PhaseStep; import mage.constants.Zone; import org.junit.Test; +import org.mage.test.player.TestPlayer; import org.mage.test.serverside.base.CardTestPlayerBase; /** @@ -58,8 +59,8 @@ public void testTargetsByTestPlayer() { addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); // cast + setChoice(playerA, TestPlayer.CHOICE_SKIP); // skip stack order castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Arbor Armament", "Zada, Hedron Grinder"); - addTarget(playerA, "Balduvian Bears^Silvercoat Lion"); setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/SpitemareTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/SpitemareTest.java index a9f0cd907805..7fe4af695fd3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/SpitemareTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/SpitemareTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.triggers.damage; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/NettlingImplTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/NettlingImplTest.java index c2192aadd1ac..228fe36f2864 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/NettlingImplTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/NettlingImplTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.triggers.delayed; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/AshenRiderTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/AshenRiderTest.java index 646c46e35c50..ed02d06706f8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/AshenRiderTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/AshenRiderTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.triggers.dies; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/state/SynodCenturionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/state/SynodCenturionTest.java index 03ecd3287aa7..4a2d72b9e106 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/state/SynodCenturionTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/state/SynodCenturionTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.triggers.state; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/combat/DamageDistributionTest.java b/Mage.Tests/src/test/java/org/mage/test/combat/DamageDistributionTest.java index cb268a8524eb..c4f4c2218b21 100644 --- a/Mage.Tests/src/test/java/org/mage/test/combat/DamageDistributionTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/combat/DamageDistributionTest.java @@ -9,7 +9,6 @@ import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author ayrat */ public class DamageDistributionTest extends CardTestPlayerBase { @@ -181,4 +180,21 @@ public void testCombatDamagePhyrexianUnlife() { assertLife(playerB, 20); } + @Test + public void testTrampleDeathtouch() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + addCard(Zone.BATTLEFIELD, playerA, "Colossal Dreadmaw"); + addCard(Zone.BATTLEFIELD, playerB, "Colossapede"); + addCard(Zone.HAND, playerA, "Bladebrand"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bladebrand", "Colossal Dreadmaw"); + + attack(1, playerA, "Colossal Dreadmaw"); + block(1, playerB, "Colossapede", "Colossal Dreadmaw"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertLife(playerB, 20 - 5); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/combat/PowerToughnessCalculationAfterCombatDamageTest.java b/Mage.Tests/src/test/java/org/mage/test/combat/PowerToughnessCalculationAfterCombatDamageTest.java index 86f1932b96bd..5366a8b957f8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/combat/PowerToughnessCalculationAfterCombatDamageTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/combat/PowerToughnessCalculationAfterCombatDamageTest.java @@ -1,9 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - package org.mage.test.combat; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/FFA3/PlayerLeftTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/FFA3/PlayerLeftTest.java index 179402a7de24..53a95b7882ae 100644 --- a/Mage.Tests/src/test/java/org/mage/test/commander/FFA3/PlayerLeftTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/commander/FFA3/PlayerLeftTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.commander.FFA3; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/CommanderColorChangeTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CommanderColorChangeTest.java index b28101e754a7..55d909519ab6 100644 --- a/Mage.Tests/src/test/java/org/mage/test/commander/duel/CommanderColorChangeTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CommanderColorChangeTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.commander.duel; import java.io.FileNotFoundException; diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/MairsilThePretenderTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/MairsilThePretenderTest.java index 2792c326bb59..884220a133f2 100644 --- a/Mage.Tests/src/test/java/org/mage/test/commander/duel/MairsilThePretenderTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/MairsilThePretenderTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.commander.duel; import java.io.FileNotFoundException; diff --git a/Mage.Tests/src/test/java/org/mage/test/lki/LastKnownInformationTest.java b/Mage.Tests/src/test/java/org/mage/test/lki/LastKnownInformationTest.java index 352ec36de59e..3fd6ee02708f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/lki/LastKnownInformationTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/lki/LastKnownInformationTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.lki; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/multiplayer/BlatantThieveryTest.java b/Mage.Tests/src/test/java/org/mage/test/multiplayer/BlatantThieveryTest.java index 7fc682e8b6bc..300e7964ef28 100644 --- a/Mage.Tests/src/test/java/org/mage/test/multiplayer/BlatantThieveryTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/multiplayer/BlatantThieveryTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.multiplayer; import mage.constants.MultiplayerAttackOption; @@ -50,8 +45,10 @@ public void NormalTest() { addTarget(playerA, "Walking Corpse"); addTarget(playerA, "Pillarfield Ox"); + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertGraveyardCount(playerA, "Blatant Thievery", 1); assertPermanentCount(playerA, "Silvercoat Lion", 1); @@ -84,6 +81,7 @@ public void ControlChangeTest() { setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertGraveyardCount(playerA, "Blatant Thievery", 1); assertGraveyardCount(playerB, "Act of Aggression", 1); diff --git a/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerDiedStackTargetHandlingTest.java b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerDiedStackTargetHandlingTest.java index 1e0d42917cac..c3b3bc7b0e6f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerDiedStackTargetHandlingTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerDiedStackTargetHandlingTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.multiplayer; import mage.constants.MultiplayerAttackOption; diff --git a/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerLeftGameRange1Test.java b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerLeftGameRange1Test.java index 8699e82cd9e8..c62bb027ca9e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerLeftGameRange1Test.java +++ b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerLeftGameRange1Test.java @@ -1,6 +1,5 @@ package org.mage.test.multiplayer; -import java.io.FileNotFoundException; import mage.constants.MultiplayerAttackOption; import mage.constants.PhaseStep; import mage.constants.RangeOfInfluence; @@ -15,6 +14,8 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestMultiPlayerBase; +import java.io.FileNotFoundException; + /** * @author LevelX2 */ @@ -346,7 +347,7 @@ public void TestPithingNeedle() { /** * Captive Audience doesn't work correctly in multiplayer #5593 - * + *

* Currently, if the controller of Captive Audience leaves the game, Captive * Audience returns to its owner instead of being exiled. */ @@ -364,8 +365,6 @@ public void TestCaptiveAudienceGoesToExile() { addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); addCard(Zone.BATTLEFIELD, playerA, "Pillarfield Ox", 1); - setChoice(playerA, "PlayerA"); // Starting Player - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Captive Audience"); setChoice(playerA, "PlayerD"); diff --git a/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerWinsTest.java b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerWinsTest.java index b2297d779a83..c398f867a22b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerWinsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerWinsTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.multiplayer; import java.io.FileNotFoundException; diff --git a/Mage.Tests/src/test/java/org/mage/test/multiplayer/VotingTest.java b/Mage.Tests/src/test/java/org/mage/test/multiplayer/VotingTest.java new file mode 100644 index 000000000000..15f782020731 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/multiplayer/VotingTest.java @@ -0,0 +1,313 @@ +package org.mage.test.multiplayer; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommander4PlayersWithAIHelps; + +/** + * @author TheElk801 + */ +public class VotingTest extends CardTestCommander4PlayersWithAIHelps { + + // Player order: A -> D -> C -> B + + // Council’s dilemma — When Lieutenants of the Guard enters the battlefield, starting with you, + // each player votes for strength or numbers. Put a +1/+1 counter on Lieutenants of the Guard + // for each strength vote and create a 1/1 white Soldier creature token for each numbers vote. + private static final String lieutenant = "Lieutenants of the Guard"; + + // While voting, you get an additional vote. (The votes can be for different choices or for the same choice.) + private static final String rep = "Brago's Representative"; + + // While voting, you may vote an additional time. (The votes can be for different choices or for the same choice.) + private static final String broker = "Ballot Broker"; + + // You choose how each player votes this turn. + private static final String illusion = "Illusion of Choice"; + + // TODO: add test with broker + // rulues: + // The ability only affects spells and abilities that use the word “vote.” Other cards that involve choices, + // such as Archangel of Strife, are unaffected. + // (2016-08-23) + + // Whenever players finish voting, each opponent who voted for a choice you didn’t vote for loses 2 life. + private static final String keeper = "Grudge Keeper"; + + // Will of the council - Starting with you, each player votes for death or torture. If death gets more votes, + // each opponent sacrifices a creature. If torture gets more votes or the vote is tied, each opponent loses 4 life. + private static final String tyrant = "Tyrant's Choice"; + + private void setChoices(String choice) { + setChoices(choice, choice, choice, choice); + } + + private void setChoices(String choiceA, String choiceB, String choiceC, String choiceD) { + setChoice(playerA, choiceA); + setChoice(playerB, choiceB); + setChoice(playerC, choiceC); + setChoice(playerD, choiceD); + } + + @Test + public void test_LieutenantsOfTheGuard_1() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.HAND, playerA, lieutenant); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lieutenant); + setChoices("Yes"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, lieutenant, 6, 6); + assertPermanentCount(playerA, "Soldier", 0); + } + + @Test + public void test_LieutenantsOfTheGuard_2() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.HAND, playerA, lieutenant); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lieutenant); + setChoices("No"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, lieutenant, 2, 2); + assertPermanentCount(playerA, "Soldier", 4); + } + + @Test + public void test_LieutenantsOfTheGuard_3() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.HAND, playerA, lieutenant); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lieutenant); + setChoices("Yes", "Yes", "No", "No"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, lieutenant, 4, 4); + assertPermanentCount(playerA, "Soldier", 2); + } + + @Test + public void test_TyrantsChoice_AI_Normal() { + addCard(Zone.HAND, playerA, tyrant); // {1}{B} + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + + // ai play + // opponents must have more votes so final result is sacrifice (best for opponents) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, tyrant); + aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA); + aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerB); + aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerC); + aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerD); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, tyrant, 1); + assertLife(playerA, 20); + assertLife(playerB, 20); + assertLife(playerC, 20); + assertLife(playerD, 20); + } + + @Test + public void test_TyrantsChoice_AI_UnderControl() { + addCard(Zone.HAND, playerA, tyrant); // {1}{B} + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // + addCard(Zone.HAND, playerA, illusion, 1); // {U} + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + + // prepare vote control + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, illusion); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, illusion, 1); + + // ai play + // you control the opponents, so votes result must be lose life (best for controller) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, tyrant); + checkStackSize("before resolve", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 1); + aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA); + aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerB); + aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerC); + aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerD); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, tyrant, 1); + assertLife(playerA, 20); + assertLife(playerB, 20 - 4); + assertLife(playerC, 20 - 4); + assertLife(playerD, 20 - 4); + } + + @Test + public void test_BragosRepresentative() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.BATTLEFIELD, playerA, rep); + addCard(Zone.HAND, playerA, lieutenant); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lieutenant); + setChoice(playerA, "Yes"); + setChoices("Yes", "Yes", "No", "No"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, lieutenant, 5, 5); + assertPermanentCount(playerA, "Soldier", 2); + } + + @Test + public void test_BallotBroker() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.BATTLEFIELD, playerA, broker); + addCard(Zone.HAND, playerA, lieutenant); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lieutenant); + setChoices("Yes", "Yes", "No", "No"); + setChoice(playerA, "Yes"); // to have an additional vote + setChoice(playerA, "No"); // the additional vote + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, lieutenant, 4, 4); + assertPermanentCount(playerA, "Soldier", 3); + } + + @Test + public void test_IllusionOfChoice_Single() { + addCard(Zone.BATTLEFIELD, playerA, "Tundra", 6); + addCard(Zone.HAND, playerA, illusion); + addCard(Zone.HAND, playerA, lieutenant); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, illusion); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, lieutenant); + setChoice(playerA, "Yes", 4); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, lieutenant, 6, 6); + assertPermanentCount(playerA, "Soldier", 0); + } + + @Test + public void test_IllusionOfChoice_WithBragosRepresentative() { + addCard(Zone.BATTLEFIELD, playerA, "Tundra", 6); + addCard(Zone.BATTLEFIELD, playerB, rep); + addCard(Zone.HAND, playerA, illusion); + addCard(Zone.HAND, playerA, lieutenant); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, illusion); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, lieutenant); + setChoice(playerA, "Yes", 5); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, lieutenant, 7, 7); + assertPermanentCount(playerA, "Soldier", 0); + } + + @Test + public void test_IllusionOfChoice_Double() { + addCard(Zone.BATTLEFIELD, playerA, "Tundra", 6); + addCard(Zone.BATTLEFIELD, playerB, "Island"); + addCard(Zone.HAND, playerA, illusion); + addCard(Zone.HAND, playerB, illusion); + addCard(Zone.HAND, playerA, lieutenant); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, illusion); + castSpell(1, PhaseStep.BEGIN_COMBAT, playerB, illusion); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, lieutenant); + setChoice(playerB, "Yes", 4); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, lieutenant, 6, 6); + assertPermanentCount(playerA, "Soldier", 0); + } + + @Test + public void test_GrudgeKeeper_1() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.BATTLEFIELD, playerA, keeper); + addCard(Zone.BATTLEFIELD, playerB, rep); + addCard(Zone.HAND, playerA, lieutenant); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, lieutenant); + setChoice(playerB, "Yes"); + setChoices("Yes", "No", "No", "No"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, lieutenant, 4, 4); + assertPermanentCount(playerA, "Soldier", 3); + assertLife(playerA, 20); + assertLife(playerB, 20 - 2); + assertLife(playerC, 20 - 2); + assertLife(playerD, 20 - 2); + } + + @Test + public void test_GrudgeKeeper_2() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.BATTLEFIELD, playerA, keeper); + addCard(Zone.BATTLEFIELD, playerA, rep); + addCard(Zone.BATTLEFIELD, playerB, rep); + addCard(Zone.HAND, playerA, lieutenant); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, lieutenant); + setChoice(playerA, "No"); + setChoice(playerB, "No"); + setChoices("Yes"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, lieutenant, 6, 6); + assertPermanentCount(playerA, "Soldier", 2); + assertLife(playerA, 20); + assertLife(playerB, 20); + assertLife(playerC, 20); + assertLife(playerD, 20); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/oathbreaker/FFA3/BasicSaheeliSublimeArtificerTest.java b/Mage.Tests/src/test/java/org/mage/test/oathbreaker/FFA3/BasicSaheeliSublimeArtificerTest.java index 38d1577020c8..0c7a7dff797a 100644 --- a/Mage.Tests/src/test/java/org/mage/test/oathbreaker/FFA3/BasicSaheeliSublimeArtificerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/oathbreaker/FFA3/BasicSaheeliSublimeArtificerTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.oathbreaker.FFA3; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java index 8c2d4f256225..d65201b13193 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java @@ -90,7 +90,7 @@ private Ability getAction(Game game) { } } if (!ability.getManaCosts().getVariableCosts().isEmpty()) { - int amount = getAvailableManaProducers(game).size() - ability.getManaCosts().convertedManaCost(); + int amount = getAvailableManaProducers(game).size() - ability.getManaCosts().manaValue(); if (amount > 0) { ability = ability.copy(); ability.getManaCostsToPay().add(new GenericManaCost(RandomUtil.nextInt(amount))); @@ -123,7 +123,7 @@ protected List getPlayableAbilities(Game game) { @Override public boolean triggerAbility(TriggeredAbility source, Game game) { - if (source != null && source.canChooseTarget(game)) { + if (source != null && source.canChooseTarget(game, playerId)) { Ability ability; List options = getPlayableOptions(source, game); if (options.isEmpty()) { diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 02e48baf3575..8cf09ff0ceee 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -86,8 +86,14 @@ public class TestPlayer implements Player { private int maxCallsWithoutAction = 400; private int foundNoAction = 0; - private boolean AIPlayer; // full playable AI - private boolean AICanChooseInStrictMode = false; // AI can choose in custom aiXXX commands (e.g. on one priority or step) + + // full playable AI, TODO: can be deleted? + private boolean AIPlayer; + // AI simulates a real game, e.g. ignores strict mode and play command/priority, see aiXXX commands + // true - unit tests uses real AI logic (e.g. AI hints and AI workarounds in cards) + // false - unit tests uses Human logic and dialogs + private boolean AIRealGameSimulation = false; + private final List actions = new ArrayList<>(); private final Map actionsToRemoveLater = new HashMap<>(); // remove actions later, on next step (e.g. for AI commands) private final Map>> rollbackActions = new HashMap<>(); // actions to add after a executed rollback @@ -125,7 +131,7 @@ public TestPlayer(TestComputerPlayerMonteCarlo computerPlayer) { public TestPlayer(final TestPlayer testPlayer) { this.AIPlayer = testPlayer.AIPlayer; - this.AICanChooseInStrictMode = testPlayer.AICanChooseInStrictMode; + this.AIRealGameSimulation = testPlayer.AIRealGameSimulation; this.foundNoAction = testPlayer.foundNoAction; this.actions.addAll(testPlayer.actions); this.choices.addAll(testPlayer.choices); @@ -720,7 +726,7 @@ public boolean priority(Game game) { // play priority if (command.equals(AI_COMMAND_PLAY_PRIORITY)) { - AICanChooseInStrictMode = true; // disable on action's remove + AIRealGameSimulation = true; // disable on action's remove computerPlayer.priority(game); actions.remove(action); return true; @@ -728,7 +734,7 @@ public boolean priority(Game game) { // play step if (command.equals(AI_COMMAND_PLAY_STEP)) { - AICanChooseInStrictMode = true; // disable on action's remove + AIRealGameSimulation = true; // disable on action's remove actionsToRemoveLater.put(action, game.getStep().getType()); computerPlayer.priority(game); return true; @@ -1897,7 +1903,7 @@ private void chooseStrictModeFailed(String choiceType, Game game, String reason) } private void chooseStrictModeFailed(String choiceType, Game game, String reason, boolean printAbilities) { - if (strictChooseMode && !AICanChooseInStrictMode) { + if (strictChooseMode && !AIRealGameSimulation) { if (printAbilities) { printStart("Available mana for " + computerPlayer.getName()); printMana(game, computerPlayer.getManaAvailable(game)); @@ -1957,6 +1963,13 @@ public Mode chooseMode(Modes modes, Ability source, Game game) { public boolean choose(Outcome outcome, Choice choice, Game game) { assertAliasSupportInChoices(false); if (!choices.isEmpty()) { + + // skip choices + if (choices.get(0).equals(CHOICE_SKIP)) { + choices.remove(0); + return true; + } + if (choice.setChoiceByAnswers(choices, true)) { return true; } @@ -2001,6 +2014,11 @@ public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game, abilityControllerId = target.getAbilityController(); } + // ignore player select + if (target.getMessage().equals("Select a starting player")) { + return computerPlayer.choose(outcome, target, sourceId, game, options); + } + assertAliasSupportInChoices(true); if (!choices.isEmpty()) { @@ -2202,15 +2220,12 @@ public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game, */ } - // ignore player select - if (!target.getMessage().equals("Select a starting player")) { - this.chooseStrictModeFailed("choice", game, getInfo(game.getObject(sourceId)) + ";\n" + getInfo(target)); - } + this.chooseStrictModeFailed("choice", game, getInfo(game.getObject(sourceId)) + ";\n" + getInfo(target)); return computerPlayer.choose(outcome, target, sourceId, game, options); } private void checkTargetDefinitionMarksSupport(Target needTarget, String targetDefinition, String canSupportChars) { - // fail on wrong chars in definition + // fail on wrong chars in definition ` ` ` ` ` ` ` `````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````` // ^ - multiple targets // [] - special option like [no copy] // = - target type like targetPlayer=PlayerA @@ -2652,6 +2667,51 @@ public int getAmount(int min, int max, String message, Game game) { return computerPlayer.getAmount(min, max, message, game); } + @Override + public List getMultiAmount(Outcome outcome, List messages, int min, int max, MultiAmountType type, Game game) { + assertAliasSupportInChoices(false); + + int needCount = messages.size(); + List defaultList = MultiAmountType.prepareDefaltValues(needCount, min, max); + if (needCount == 0) { + return defaultList; + } + + List answer = new ArrayList<>(defaultList); + if (!choices.isEmpty()) { + // must fill all possible choices or skip it + for (int i = 0; i < messages.size(); i++) { + if (!choices.isEmpty()) { + // normal choice + if (choices.get(0).startsWith("X=")) { + answer.set(i, Integer.parseInt(choices.get(0).substring(2))); + choices.remove(0); + continue; + } + // skip + if (choices.get(0).equals(CHOICE_SKIP)) { + choices.remove(0); + break; + } + } + Assert.fail(String.format("Missing choice in multi amount: %s (pos %d - %s)", type.getHeader(), i + 1, messages.get(i))); + } + + // extra check + if (!MultiAmountType.isGoodValues(answer, needCount, min, max)) { + Assert.fail("Wrong choices in multi amount: " + answer + .stream() + .map(String::valueOf) + .collect(Collectors.joining(","))); + } + + return answer; + } + + this.chooseStrictModeFailed("choice", game, "Multi amount: " + type.getHeader()); + return computerPlayer.getMultiAmount(outcome, messages, min, max, type, game); + } + @Override public void addAbility(Ability ability) { computerPlayer.addAbility(ability); @@ -2826,6 +2886,11 @@ public Cards discard(int amount, boolean random, boolean payForCost, Ability sou return computerPlayer.discard(amount, random, payForCost, source, game); } + @Override + public Cards discard(int minAmount, int maxAmount, boolean payForCost, Ability source, Game game) { + return computerPlayer.discard(minAmount, maxAmount, payForCost, source, game); + } + @Override public Cards discard(Cards cards, boolean payForCost, Ability source, Game game) { return computerPlayer.discard(cards, payForCost, source, game); @@ -3023,6 +3088,11 @@ public void untap(Game game) { computerPlayer.untap(game); } + @Override + public void updateRange(Game game) { + computerPlayer.updateRange(game); + } + @Override public UUID getId() { return computerPlayer.getId(); @@ -3055,7 +3125,18 @@ public String getLogName() { @Override public boolean isHuman() { - return computerPlayer.isHuman(); + return false; + } + + @Override + public boolean isComputer() { + // all players in unit tests are computers, so you must use AIRealGameSimulation to test different logic (Human vs AI) + if (isTestsMode()) { + return AIRealGameSimulation; + } else { + throw new IllegalStateException("Can't use test player outside of unit tests"); + //return !isHuman(); + } } @Override @@ -3133,6 +3214,11 @@ public int gainLife(int amount, Game game, Ability source) { return computerPlayer.gainLife(amount, game, source); } + @Override + public void exchangeLife(Player player, Ability source, Game game) { + computerPlayer.exchangeLife(player, source, game); + } + @Override public int damage(int damage, UUID attackerId, Ability source, Game game) { return computerPlayer.damage(damage, attackerId, source, game); @@ -3444,8 +3530,8 @@ public List getPlayableOptions(Ability ability, Game game) { } @Override - public boolean isTestMode() { - return computerPlayer.isTestMode(); + public boolean isTestsMode() { + return computerPlayer.isTestsMode(); } @Override @@ -3639,7 +3725,7 @@ public boolean moveCardsToExile(Set cards, Ability source, Game game, bool } @Override - public Set moveCardsToGraveyardWithInfo(Set allCards, Ability source, Game game, Zone fromZone) { + public Set moveCardsToGraveyardWithInfo(Set allCards, Ability source, Game game, Zone fromZone) { return computerPlayer.moveCardsToGraveyardWithInfo(allCards, source, game, fromZone); } @@ -3777,6 +3863,21 @@ public boolean choose(Outcome outcome, Cards cards, ) { assertAliasSupportInChoices(false); if (!choices.isEmpty()) { + + // skip choices + if (choices.get(0).equals(CHOICE_SKIP)) { + choices.remove(0); + if (cards.isEmpty()) { + // cancel button forced in GUI on no possible choices + return false; + } else { + Assert.assertTrue("found skip choice, but it require more choices, needs " + + (target.getMinNumberOfTargets() - target.getTargets().size()) + " more", + target.getTargets().size() >= target.getMinNumberOfTargets()); + return true; + } + } + for (String choose2 : choices) { // TODO: More targetting to fix String[] targetList = choose2.split("\\^"); @@ -3817,7 +3918,7 @@ public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Assert.assertNotEquals("chooseTargetAmount needs non zero amount remaining", 0, target.getAmountRemaining()); - assertAliasSupportInTargets(false); + assertAliasSupportInTargets(true); if (!targets.isEmpty()) { // skip targets @@ -3838,6 +3939,8 @@ public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, String targetName = choiceSettings[0]; int targetAmount = Integer.parseInt(choiceSettings[1].substring("X=".length())); + checkTargetDefinitionMarksSupport(target, targetName, "="); + // player target support if (targetName.startsWith("targetPlayer=")) { targetName = targetName.substring(targetName.indexOf("targetPlayer=") + "targetPlayer=".length()); @@ -3849,10 +3952,21 @@ public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, if (target.getAmountRemaining() > 0) { for (UUID possibleTarget : target.possibleTargets(source.getSourceId(), source.getControllerId(), game)) { + boolean foundTarget = false; + + // permanent MageObject objectPermanent = game.getObject(possibleTarget); + if (objectPermanent != null && hasObjectTargetNameOrAlias(objectPermanent, targetName)) { + foundTarget = true; + } + + // player Player objectPlayer = game.getPlayer(possibleTarget); - String objectName = objectPermanent != null ? objectPermanent.getName() : objectPlayer.getName(); - if (objectName.equals(targetName)) { + if (!foundTarget && objectPlayer != null && objectPlayer.getName().equals(targetName)) { + foundTarget = true; + } + + if (foundTarget) { if (!target.getTargets().contains(possibleTarget) && target.canTarget(possibleTarget, source, game)) { // can select target.addTarget(possibleTarget, targetAmount, source, game); @@ -4052,14 +4166,14 @@ public boolean moveCards(Cards cards, Zone toZone, } @Override - public boolean moveCards(Set cards, Zone toZone, + public boolean moveCards(Set cards, Zone toZone, Ability source, Game game ) { return computerPlayer.moveCards(cards, toZone, source, game); } @Override - public boolean moveCards(Set cards, Zone toZone, + public boolean moveCards(Set cards, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects ) { @@ -4146,7 +4260,8 @@ public FilterMana getPhyrexianColors() { public SpellAbility chooseAbilityForCast(Card card, Game game, boolean noMana) { assertAliasSupportInChoices(false); - Map useable = PlayerImpl.getSpellAbilities(this.getId(), card, game.getState().getZone(card.getId()), game); + MageObject object = game.getObject(card.getId()); // must be object to find real abilities (example: commander) + Map useable = PlayerImpl.getSpellAbilities(this.getId(), object, game.getState().getZone(object.getId()), game); String allInfo = useable.values().stream().map(Object::toString).collect(Collectors.joining("\n")); if (useable.size() == 1) { return (SpellAbility) useable.values().iterator().next(); @@ -4172,12 +4287,16 @@ public ComputerPlayer getComputerPlayer() { return computerPlayer; } - public void setAICanChooseInStrictMode(boolean AICanChooseInStrictMode) { - this.AICanChooseInStrictMode = AICanChooseInStrictMode; + public void setAIRealGameSimulation(boolean AIRealGameSimulation) { + this.AIRealGameSimulation = AIRealGameSimulation; } public Map>> getRollbackActions() { return rollbackActions; } + @Override + public String toString() { + return computerPlayer.toString(); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestCommander4PlayersWithAIHelps.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestCommander4PlayersWithAIHelps.java new file mode 100644 index 000000000000..52741581ef57 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestCommander4PlayersWithAIHelps.java @@ -0,0 +1,21 @@ +package org.mage.test.serverside.base; + +import mage.constants.RangeOfInfluence; +import org.mage.test.player.TestComputerPlayer7; +import org.mage.test.player.TestPlayer; + +/** + * See more details in CardTestPlayerBaseWithAIHelps + * + * @author JayDi85 + */ +public abstract class CardTestCommander4PlayersWithAIHelps extends CardTestCommander4Players { + + @Override + protected TestPlayer createPlayer(String name, RangeOfInfluence rangeOfInfluence) { + // use same RangeOfInfluence.ALL as CardTestCommander4Players do + TestPlayer testPlayer = new TestPlayer(new TestComputerPlayer7(name, RangeOfInfluence.ALL, 6)); + testPlayer.setAIPlayer(false); // AI can't play it by itself, use AI commands + return testPlayer; + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestOathbreaker3PlayersFFA.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestOathbreaker3PlayersFFA.java index 9875fd05cb6a..e9e3bf34de8b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestOathbreaker3PlayersFFA.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestOathbreaker3PlayersFFA.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.serverside.base; import java.io.FileNotFoundException; diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseAI.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseAI.java index 5f1e9d2f9d93..27475a73d400 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseAI.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseAI.java @@ -13,6 +13,10 @@ import java.io.FileNotFoundException; /** + * PlayerA is full AI player and process all actions as AI logic. You don't need aiXXX commands in that tests. + * + * If you need custom AI tests then use CardTestPlayerBaseWithAIHelps with aiXXX commands + * * @author LevelX2 */ public abstract class CardTestPlayerBaseAI extends CardTestPlayerAPIImpl { @@ -33,6 +37,7 @@ protected TestPlayer createPlayer(String name, RangeOfInfluence rangeOfInfluence if (name.equals("PlayerA")) { TestPlayer testPlayer = new TestPlayer(new TestComputerPlayer7("PlayerA", RangeOfInfluence.ONE, skill)); testPlayer.setAIPlayer(true); + testPlayer.setAIRealGameSimulation(true); // enable AI logic simulation for all turns by default return testPlayer; } return super.createPlayer(name, rangeOfInfluence); diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseWithMonteCarloAIHelps.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseWithMonteCarloAIHelps.java index 30a4f16f879f..47bcbafb0b6c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseWithMonteCarloAIHelps.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseWithMonteCarloAIHelps.java @@ -5,9 +5,7 @@ import org.mage.test.player.TestPlayer; /** - * Base class but with Monte Carlo computer player to test single AI commands (it's different from full AI simulation from CardTestPlayerBaseAI): - * 1. AI don't play normal priorities (you must use ai*** commands to play it); - * 2. AI will choose in non strict mode (it's simulated ComputerPlayerMCTS, not simple ComputerPlayer from basic tests) + * See more details in CardTestPlayerBaseWithAIHelps * * @author JayDi85 */ diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java index 1ed6be9e9cdf..533367ec58e2 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java @@ -10,8 +10,12 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.ReturnFromExileEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.abilities.effects.common.cost.SpellsCostReductionAllEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -32,7 +36,11 @@ import mage.server.util.PluginClassLoader; import mage.server.util.config.GamePlugin; import mage.server.util.config.Plugin; +import mage.target.TargetPermanent; import mage.target.common.TargetAnyTarget; +import mage.target.common.TargetCardInExile; +import mage.target.common.TargetCardInGraveyard; +import mage.target.common.TargetCardInLibrary; import mage.util.CardUtil; import mage.util.Copier; import org.apache.log4j.Level; @@ -467,6 +475,54 @@ protected void addCustomEffect_TargetDamage(TestPlayer controller, int damageAmo ability ); } + + /** + * Add target destroy ability that can be called by text "target destroy" + * + * @param controller + */ + protected void addCustomEffect_DestroyTarget(TestPlayer controller) { + Ability ability = new SimpleActivatedAbility(new DestroyTargetEffect().setText("target destroy"), new ManaCostsImpl("")); + ability.addTarget(new TargetPermanent()); + addCustomCardWithAbility( + "target destroy for " + controller.getName(), + controller, + ability + ); + } + + /** + * Return target card to hand that can be called by text "return from ..." + * + * @param controller + */ + protected void addCustomEffect_ReturnFromAnyToHand(TestPlayer controller) { + // graveyard + Ability ability = new SimpleActivatedAbility(new ReturnFromGraveyardToHandTargetEffect().setText("return from graveyard"), new ManaCostsImpl("")); + ability.addTarget(new TargetCardInGraveyard(StaticFilters.FILTER_CARD)); + addCustomCardWithAbility( + "return from graveyard for " + controller.getName(), + controller, + ability + ); + + // exile + ability = new SimpleActivatedAbility(new ReturnFromExileEffect(Zone.HAND).setText("return from exile"), new ManaCostsImpl("")); + ability.addTarget(new TargetCardInExile(StaticFilters.FILTER_CARD)); + addCustomCardWithAbility( + "return from exile for " + controller.getName(), + controller, + ability + ); + + // library + ability = new SimpleActivatedAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD)).setText("return from library"), new ManaCostsImpl("")); + addCustomCardWithAbility( + "return from library for " + controller.getName(), + controller, + ability + ); + } } // custom card with global abilities list to init (can contains abilities per card name) diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index c8cd5cb4c59a..ca574fb47a79 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -298,6 +298,11 @@ public void execute() throws IllegalStateException { (maxTurn > this.stopOnTurn) || (maxTurn == this.stopOnTurn && maxPhase > this.stopAtStep.getIndex())); if (!currentGame.isPaused()) { + // workaround to fill range info (cause real range fills after game start, but some cheated cards needs range on ETB) + for (Player player : currentGame.getPlayers().values()) { + player.updateRange(currentGame); + } + // add cards to game for (Player player : currentGame.getPlayers().values()) { TestPlayer testPlayer = (TestPlayer) player; currentGame.cheat(testPlayer.getId(), getCommands(testPlayer)); @@ -581,6 +586,7 @@ public void removeAllCardsFromLibrary(TestPlayer player) { * * @param player {@link Player} to remove all cards from hand. */ + @Deprecated // TODO: remove, cause test games don't use starting draws public void removeAllCardsFromHand(TestPlayer player) { getCommands(player).put(Zone.HAND, "clear"); } @@ -1348,6 +1354,25 @@ public void assertGraveyardCount(Player player, int count) throws AssertionError Assert.assertEquals("(Graveyard) Card counts are not equal ", count, actual); } + /** + * Assert card subtype in exile. + * + * @param cardName Name of the card. + * @param subType Expected subtype. + */ + public void assertExiledCardSubtype(String cardName, SubType subType) throws AssertionError { + boolean found = false; + for (ExileZone exile : currentGame.getExile().getExileZones()) { + for (Card card : exile.getCards(currentGame)) { + if(CardUtil.haveSameNames(card.getName(), cardName, true) && card.hasSubtype(subType, currentGame)){ + found = true; + } + } + } + + Assert.assertTrue("There is no card named " + cardName + " found in exile, with subtype " + subType, found); + } + /** * Assert card count in exile. * @@ -1566,9 +1591,9 @@ public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String car } /** - * AI play one PRIORITY with multi game simulations (calcs and play ONE best - * action, can be called with stack) All choices must be made by AI - * (e.g.strict mode possible) + * AI play one PRIORITY with multi game simulations like real game + * (calcs and play ONE best action, can be called with stack) + * All choices must be made by AI (e.g.strict mode possible) * * @param turnNum * @param step @@ -1590,11 +1615,11 @@ public void aiPlayStep(int turnNum, PhaseStep step, TestPlayer player) { } public PlayerAction createAIPlayerAction(int turnNum, PhaseStep step, String aiCommand) { - // AI actions must disable and enable strict mode + // AI commands must disable and enable real game simulation and strict mode return new PlayerAction("", turnNum, step, AI_PREFIX + aiCommand) { @Override public void onActionRemovedLater(Game game, TestPlayer player) { - player.setAICanChooseInStrictMode(false); + player.setAIRealGameSimulation(false); } }; } @@ -1902,6 +1927,20 @@ public void setChoice(TestPlayer player, String choice, int timesToChoose) { } } + /** + * Setup amount choices. + * + * Multi amount choices uses WUBRG order (so use 1,2,3,4,5 values list) + * + * @param player + * @param amountList + */ + public void setChoiceAmount(TestPlayer player, int... amountList) { + for (int amount : amountList) { + setChoice(player, "X=" + amount); + } + } + /** * Set the modes for modal spells * diff --git a/Mage.Tests/src/test/java/org/mage/test/sets/BoosterGenerationTest.java b/Mage.Tests/src/test/java/org/mage/test/sets/BoosterGenerationTest.java index fc562171a804..6960f9a01b9f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/sets/BoosterGenerationTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/sets/BoosterGenerationTest.java @@ -9,6 +9,7 @@ import mage.cards.repository.CardScanner; import mage.constants.CardType; import mage.constants.Rarity; +import mage.constants.SubType; import mage.sets.*; import org.junit.Assert; import org.junit.Before; @@ -335,6 +336,7 @@ public void testKaldheim_SnowLandAndMDFC() { boolean foundVale = false; boolean foundMDFC = false; boolean foundNoMDFC = false; + for (int i = 1; i <= 100; i++) { List booster = Kaldheim.getInstance().createBooster(); @@ -414,4 +416,94 @@ public void testKaldheim_SnowLandAndMDFC() { assertTrue("No booster contained an MDFC", foundMDFC); assertTrue("Every booster contained an MDFC", foundNoMDFC); } + + @Test + public void testTimeSpiralRemastered_BonusSheet() { + for (int i = 1; i <= 5; i++) { + List booster = TimeSpiralRemastered.getInstance().createBooster(); + + assertFalse( + "Booster should have no basic lands:" + str(booster), + contains(booster, basics, null) + ); + + assertEquals( + "Booster should have 10 commons", 10, + booster.stream().map(Card::getRarity).filter(Rarity.COMMON::equals).count() + ); + assertEquals( + "Booster should have 3 uncommons", 3, + booster.stream().map(Card::getRarity).filter(Rarity.UNCOMMON::equals).count() + ); + assertEquals( + "Booster should have 1 rare/mythic", 1, + booster.stream().map(Card::getRarity).filter(r -> r == Rarity.RARE || r == Rarity.MYTHIC).count() + ); + assertEquals( + "Booster should have 1 bonus card", 1, + booster.stream().map(Card::getRarity).filter(Rarity.SPECIAL::equals).count() + ); + } + } + + @Test + public void testStrixhavenSchoolOfMages_LessonsAndArchive() { + boolean foundUncommonLesson = false; + boolean foundNoUncommonLesson = false; + + for (int i = 1; i <= 100; i++) { + List booster = StrixhavenSchoolOfMages.getInstance().createBooster(); + List nonLessons = booster + .stream() + .filter(c -> "STX".equals(c.getExpansionSetCode())) + .filter(c -> !c.hasSubtype(SubType.LESSON, null)) + .collect(Collectors.toList()); + List lessons = booster + .stream() + .filter(c -> "STX".equals(c.getExpansionSetCode())) + .filter(c -> c.hasSubtype(SubType.LESSON, null)) + .collect(Collectors.toList()); + + assertEquals("Booster should have 15 cards", 15, booster.size()); + + assertFalse( + "Booster should have no basic lands:" + str(booster), + contains(booster, basics, null) + ); + + assertEquals( + "Booster should have 9 non-Lesson commons", 9, + nonLessons.stream().map(Card::getRarity).filter(Rarity.COMMON::equals).count() + ); + assertEquals( + "Booster should have 3 uncommons", 3, + booster.stream().filter(c -> "STX".equals(c.getExpansionSetCode())).map(Card::getRarity).filter(Rarity.UNCOMMON::equals).count() + ); + assertEquals( + "Booster should have 1 non-Lesson rare/mythic", 1, + nonLessons.stream().map(Card::getRarity).filter(r -> r == Rarity.RARE || r == Rarity.MYTHIC).count() + ); + + assertEquals( + "Booster should have 1 Mystical Archive card", 1, + booster.stream().map(Card::getExpansionSetCode).filter("STA"::equals).count() + ); + + assertTrue("Booster should have no more than 2 total Lessons", lessons.size() <= 2); + assertEquals( + "Booster should have 1 non-uncommon Lesson", 1, + lessons.stream().filter(c -> c.getRarity() != Rarity.UNCOMMON).count() + ); + long uncommonLessonCount = lessons.stream().filter(c -> c.getRarity() == Rarity.UNCOMMON).count(); + assertTrue("Booster should have no more than 1 uncommon Lesson", uncommonLessonCount <= 1); + + foundUncommonLesson |= uncommonLessonCount > 0; + foundNoUncommonLesson |= uncommonLessonCount == 0; + if (foundUncommonLesson && foundNoUncommonLesson && i > 20) { + break; + } + } + assertTrue("No booster contained an uncommon Lesson", foundUncommonLesson); + assertTrue("Every booster contained an uncommon Lesson", foundNoUncommonLesson); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java index 78fd6b985915..dc7f44f0d7ed 100644 --- a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java +++ b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java @@ -137,6 +137,10 @@ public int gainLife(int amount, Game game, Ability source) { return 0; } + @Override + public void exchangeLife(Player player, Ability source, Game game) { + } + @Override public int damage(int damage, UUID attackerId, Ability source, Game game) { return 0; @@ -477,7 +481,7 @@ public void setGameUnderYourControl(boolean value) { } @Override - public boolean isTestMode() { + public boolean isTestsMode() { return false; } @@ -660,6 +664,11 @@ public Cards discard(int amount, boolean random, boolean payForCost, Ability sou return null; } + @Override + public Cards discard(int minAmount, int maxAmount, boolean payForCost, Ability source, Game game) { + return null; + } + @Override public void discardToMax(Game game) { @@ -965,6 +974,11 @@ public int getAmount(int min, int max, String message, Game game) { return 0; } + @Override + public List getMultiAmount(Outcome outcome, List messages, int min, int max, MultiAmountType type, Game game) { + return null; + } + @Override public void sideboard(Match match, Deck deck) { @@ -1030,6 +1044,11 @@ public void untap(Game game) { } + @Override + public void updateRange(Game game) { + + } + @Override public ManaOptions getManaAvailable(Game game) { return null; @@ -1161,12 +1180,12 @@ public boolean moveCards(Cards cards, Zone toZone, Ability source, Game game) { } @Override - public boolean moveCards(Set cards, Zone toZone, Ability source, Game game) { + public boolean moveCards(Set cards, Zone toZone, Ability source, Game game) { return false; } @Override - public boolean moveCards(Set cards, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects) { + public boolean moveCards(Set cards, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects) { return false; } @@ -1196,7 +1215,7 @@ public boolean moveCardToGraveyardWithInfo(Card card, Ability source, Game game, } @Override - public Set moveCardsToGraveyardWithInfo(Set cards, Ability source, Game game, Zone fromZone) { + public Set moveCardsToGraveyardWithInfo(Set cards, Ability source, Game game, Zone fromZone) { return null; } diff --git a/Mage.Verify/src/main/java/mage/verify/mtgjson/MtgJsonMetadata.java b/Mage.Verify/src/main/java/mage/verify/mtgjson/MtgJsonMetadata.java new file mode 100644 index 000000000000..10c10dfae351 --- /dev/null +++ b/Mage.Verify/src/main/java/mage/verify/mtgjson/MtgJsonMetadata.java @@ -0,0 +1,9 @@ +package mage.verify.mtgjson; + +public final class MtgJsonMetadata { + // MTGJSON metadata + // https://mtgjson.com/file-models/meta/ + + public String date; + public String version; +} diff --git a/Mage.Verify/src/main/java/mage/verify/mtgjson/MtgJsonService.java b/Mage.Verify/src/main/java/mage/verify/mtgjson/MtgJsonService.java index 52c4b193a2c0..dbc1935c8674 100644 --- a/Mage.Verify/src/main/java/mage/verify/mtgjson/MtgJsonService.java +++ b/Mage.Verify/src/main/java/mage/verify/mtgjson/MtgJsonService.java @@ -33,9 +33,8 @@ private static Map loadAllCards() throws IOException { return json.prepareIndex(); } - private static Map loadAllSets() throws IOException { - AllPrintingsModel json = readFromZip("AllPrintings.json.zip", AllPrintingsModel.class); - return json.data; + private static AllPrintingsModel loadAllSets() throws IOException { + return readFromZip("AllPrintings.json.zip", AllPrintingsModel.class); } private static T readFromZip(String filename, Class clazz) throws IOException { @@ -66,6 +65,10 @@ public static Map sets() { return SetHolder.sets; } + public static MtgJsonMetadata meta() { + return SetHolder.meta; + } + public static MtgJsonCard card(String name) { return findReference(CardHolder.cards, name); } @@ -178,6 +181,7 @@ public HashMap prepareIndex() { private static final class AllPrintingsModel { public HashMap data; + public MtgJsonMetadata meta; } private static final class CardHolder { @@ -218,10 +222,14 @@ private static final class CardHolder { private static final class SetHolder { private static final Map sets; + private static final MtgJsonMetadata meta; static { try { - sets = loadAllSets(); + AllPrintingsModel model = loadAllSets(); + sets = model.data; + meta = model.meta; + System.out.println("MTGJSON version " + meta.version + ", release date " + meta.date); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java index db9f94bfa0e2..6a75a20ccb20 100644 --- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java +++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java @@ -4,6 +4,8 @@ import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SagaAbility; +import mage.abilities.common.WerewolfBackTriggeredAbility; +import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.effects.keyword.ScryEffect; import mage.abilities.keyword.MenaceAbility; import mage.abilities.keyword.MultikickerAbility; @@ -54,7 +56,7 @@ public class VerifyCardDataTest { private static final Logger logger = Logger.getLogger(VerifyCardDataTest.class); - private static final String FULL_ABILITIES_CHECK_SET_CODE = "ZNR"; // check all abilities and output cards with wrong abilities texts; + private static final String FULL_ABILITIES_CHECK_SET_CODE = "C21"; // check all abilities and output cards with wrong abilities texts; private static final boolean AUTO_FIX_SAMPLE_DECKS = false; // debug only: auto-fix sample decks by test_checkSampleDecks test run private static final HashMap> skipCheckLists = new HashMap<>(); @@ -1281,6 +1283,14 @@ private void checkMissingAbilities(Card card, MtgJsonCard ref) { fail(card, "abilities", "card is a Saga but is missing this.addAbility(sagaAbility)"); } + // special check: Werewolves front ability should only be on front and vice versa + if (card.getAbilities().containsClass(WerewolfFrontTriggeredAbility.class) && card.isNightCard()) { + fail(card, "abilities", "card is a back face werewolf with a front face ability"); + } + if (card.getAbilities().containsClass(WerewolfBackTriggeredAbility.class) && !card.isNightCard()) { + fail(card, "abilities", "card is a front face werewolf with a back face ability"); + } + // special check: missing or wrong ability/effect hints Map hints = new HashMap<>(); hints.put(MenaceAbility.class, "can't be blocked except by two or more"); @@ -1362,14 +1372,14 @@ private String prepareRule(String cardName, String rule) { .replace("", "") .replace("", ""); - return newRule; + return newRule.trim(); } @Test public void test_showCardInfo() throws Exception { // debug only: show direct card info (takes it from class file, not from db repository) // can check multiple cards at once, example: name1;name2;name3 - String cardNames = "Dire Fleet Warmonger"; + String cardNames = "Spark Double"; CardScanner.scan(); Arrays.stream(cardNames.split(";")).forEach(cardName -> { cardName = cardName.trim(); @@ -1621,7 +1631,7 @@ private void checkCost(Card card, MtgJsonCard ref) { } String expected = ref.manaCost; - String cost = join(card.getManaCost().getSymbols()); + String cost = String.join("", card.getManaCostSymbols()); if (cost.isEmpty()) { cost = null; } @@ -1655,7 +1665,7 @@ private void checkBasicLands(Card card, MtgJsonCard ref) { // other cards can't have that stats if (isBasicLandName(card.getName())) { // lands - if (card.getRarity() != Rarity.LAND) { + if (card.getRarity() != Rarity.LAND && card.getRarity() != Rarity.SPECIAL) { fail(card, "rarity", "basic land must be Rarity.LAND"); } @@ -1674,14 +1684,6 @@ private void checkBasicLands(Card card, MtgJsonCard ref) { } } - private String join(Iterable items) { - StringBuilder result = new StringBuilder(); - for (Object item : items) { - result.append(item); - } - return result.toString(); - } - @Test public void test_checkCardRatingConsistency() { // all cards with same name must have same rating (see RateCard.rateCard) @@ -1714,6 +1716,9 @@ public void test_checkCardConstructors() { if (card == null) { errorsList.add("Error: broken constructor " + setInfo.getCardClass()); } + if (!card.getExpansionSetCode().equals(set.getCode())) { + errorsList.add("Error: card constructor have custom expansionSetCode, must be removed " + setInfo.getCardClass()); + } } catch (Throwable e) { // CardImpl.createCard don't throw exceptions (only error logs), so that logs are useless here logger.error("Error: can't create card " + setInfo.getName() + ": " + e.getMessage(), e); diff --git a/Mage/src/main/java/mage/ApprovingObject.java b/Mage/src/main/java/mage/ApprovingObject.java index afc12f332e52..3701880d7151 100644 --- a/Mage/src/main/java/mage/ApprovingObject.java +++ b/Mage/src/main/java/mage/ApprovingObject.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/ConditionalMana.java b/Mage/src/main/java/mage/ConditionalMana.java index 4c51a6cbcb07..1c2cb209915d 100644 --- a/Mage/src/main/java/mage/ConditionalMana.java +++ b/Mage/src/main/java/mage/ConditionalMana.java @@ -17,7 +17,7 @@ /** * @author nantuko */ -public class ConditionalMana extends Mana implements Serializable { +public class ConditionalMana extends Mana implements Serializable, Emptiable { /** * Conditions that should be met (all or any depending on comparison scope) diff --git a/Mage/src/main/java/mage/Emptiable.java b/Mage/src/main/java/mage/Emptiable.java new file mode 100644 index 000000000000..8578452fe390 --- /dev/null +++ b/Mage/src/main/java/mage/Emptiable.java @@ -0,0 +1,15 @@ +package mage; + +import mage.constants.ManaType; + +/** + * @author TheElk801 + */ +public interface Emptiable { + + public void add(ManaType manaType, int amount); + + public void clear(ManaType manaType); + + public int get(final ManaType manaType); +} diff --git a/Mage/src/main/java/mage/MageObject.java b/Mage/src/main/java/mage/MageObject.java index 532a23b9837d..b95615cb15d1 100644 --- a/Mage/src/main/java/mage/MageObject.java +++ b/Mage/src/main/java/mage/MageObject.java @@ -88,7 +88,15 @@ default Abilities getInitAbilities() { ManaCosts getManaCost(); - int getConvertedManaCost(); + default List getManaCostSymbols() { + List symbols = new ArrayList<>(); + for (ManaCost cost : getManaCost()) { + symbols.add(cost.getText()); + } + return symbols; + } + + int getManaValue(); MageInt getPower(); @@ -98,6 +106,21 @@ default Abilities getInitAbilities() { void setStartingLoyalty(int startingLoyalty); + /** + * Dynamic cost modification for card (process only OWN abilities). + *

+ * Usage example: if it need stack related info (like real targets) then must check two + * states (game.inCheckPlayableState): + *

+ * 1. In playable state it must check all possible use cases (e.g. allow to + * reduce on any available target and modes) + *

+ * 2. In real cast state it must check current use case (e.g. real selected + * targets and modes) + * + * @param ability + * @param game + */ void adjustCosts(Ability ability, Game game); void adjustTargets(Ability ability, Game game); @@ -272,6 +295,11 @@ default void retainAllEnchantmentSubTypes(Game game) { game.getState().getCreateMageObjectAttribute(this, game).getSubtype().retainAll(SubType.getEnchantmentTypes()); } + default void retainAllLandSubTypes(Game game) { + setIsAllCreatureTypes(game, false); + game.getState().getCreateMageObjectAttribute(this, game).getSubtype().retainAll(SubType.getLandTypes()); + } + /** * Remove object's own creature types forever (for copy effects usage) */ diff --git a/Mage/src/main/java/mage/MageObjectImpl.java b/Mage/src/main/java/mage/MageObjectImpl.java index d6d56a068d2c..ea5f0c980380 100644 --- a/Mage/src/main/java/mage/MageObjectImpl.java +++ b/Mage/src/main/java/mage/MageObjectImpl.java @@ -9,6 +9,7 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; +import mage.abilities.keyword.ChangelingAbility; import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.abilities.text.TextPart; import mage.abilities.text.TextPartSubType; @@ -57,7 +58,7 @@ public MageObjectImpl(UUID id) { color = new ObjectColor(); frameColor = new ObjectColor(); frameStyle = FrameStyle.M15_NORMAL; - manaCost = new ManaCostsImpl<>(""); + manaCost = new ManaCostsImpl<>(); abilities = new AbilitiesImpl<>(); textParts = new ArrayList<>(); } @@ -232,19 +233,21 @@ public ManaCosts getManaCost() { } @Override - public int getConvertedManaCost() { + public int getManaValue() { if (manaCost != null) { - return manaCost.convertedManaCost(); + return manaCost.manaValue(); } return 0; } @Override - public void adjustCosts(Ability ability, Game game) { + public final void adjustCosts(Ability ability, Game game) { + ability.adjustCosts(game); } @Override - public void adjustTargets(Ability ability, Game game) { + public final void adjustTargets(Ability ability, Game game) { + ability.adjustTargets(game); } @Override @@ -291,6 +294,9 @@ public void setZoneChangeCounter(int value, Game game) { @Override public boolean isAllCreatureTypes(Game game) { + if (game == null) { + return this.getAbilities().containsClass(ChangelingAbility.class); + } return this.getSubtype(game).isAllCreatureTypes(); } @@ -339,4 +345,9 @@ public void removePTCDA() { } } } + + @Override + public String toString() { + return getIdName() + " (" + super.getClass().getSuperclass().getSimpleName() + " -> " + this.getClass().getSimpleName() + ")"; + } } diff --git a/Mage/src/main/java/mage/MageObjectReference.java b/Mage/src/main/java/mage/MageObjectReference.java index b78a1c22fea7..f8ca0fb9271a 100644 --- a/Mage/src/main/java/mage/MageObjectReference.java +++ b/Mage/src/main/java/mage/MageObjectReference.java @@ -26,13 +26,17 @@ public class MageObjectReference implements Comparable, Ser private final int zoneChangeCounter; public MageObjectReference(MageObject mageObject, Game game) { + this(mageObject, game, 0); + } + + public MageObjectReference(MageObject mageObject, Game game, int offset) { if (mageObject == null) { this.sourceId = null; this.zoneChangeCounter = -1; return; } this.sourceId = mageObject.getId(); - this.zoneChangeCounter = mageObject.getZoneChangeCounter(game); + this.zoneChangeCounter = mageObject.getZoneChangeCounter(game) + offset; } /** diff --git a/Mage/src/main/java/mage/Mana.java b/Mage/src/main/java/mage/Mana.java index 5bb92c6d1e25..556f5d7c4654 100644 --- a/Mage/src/main/java/mage/Mana.java +++ b/Mage/src/main/java/mage/Mana.java @@ -24,6 +24,10 @@ protected static final class ManaColor implements Serializable { private ManaColor() { } + protected ManaColor(final ManaColor manaColor) { + this.copyFrom(manaColor); + } + private ManaColor(int amount) { this.amount = amount; } @@ -76,9 +80,12 @@ protected boolean removeOne(ManaColor manaColor) { } protected ManaColor copy() { - ManaColor copy = new ManaColor(); - copy.incrementAmount(this); - return copy; + return new ManaColor(this); + } + + protected void copyFrom(final ManaColor manaColor) { + this.amount = manaColor.amount; + this.snowAmount = manaColor.snowAmount; } @Override @@ -93,6 +100,15 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(amount, snowAmount); } + + @Override + public String toString() { + if (amount != 0 || snowAmount != 0) { + return amount + "/" + snowAmount; + } else { + return ""; + } + } } private static final transient Logger logger = Logger.getLogger(Mana.class); @@ -1202,14 +1218,15 @@ public boolean getFlag() { * @param mana the mana to set this object to. */ public void setToMana(final Mana mana) { - this.any = mana.any.copy(); - this.white = mana.white.copy(); - this.blue = mana.blue.copy(); - this.black = mana.black.copy(); - this.red = mana.red.copy(); - this.green = mana.green.copy(); - this.colorless = mana.colorless.copy(); - this.generic = mana.generic.copy(); + this.any.copyFrom(mana.any); + this.white.copyFrom(mana.white); + this.blue.copyFrom(mana.blue.copy()); + this.black.copyFrom(mana.black.copy()); + this.red.copyFrom(mana.red.copy()); + this.green.copyFrom(mana.green.copy()); + this.colorless.copyFrom(mana.colorless.copy()); + this.generic.copyFrom(mana.generic.copy()); + //this.flag = mana.flag; } /** diff --git a/Mage/src/main/java/mage/abilities/AbilitiesImpl.java b/Mage/src/main/java/mage/abilities/AbilitiesImpl.java index d3a78f2d7cfa..2c69d020bf4e 100644 --- a/Mage/src/main/java/mage/abilities/AbilitiesImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilitiesImpl.java @@ -4,6 +4,7 @@ import mage.abilities.costs.Cost; import mage.abilities.keyword.ProtectionAbility; import mage.abilities.mana.ActivatedManaAbilityImpl; +import mage.abilities.mana.ManaAbility; import mage.constants.AbilityType; import mage.constants.Zone; import mage.game.Game; @@ -12,7 +13,6 @@ import java.util.*; import java.util.stream.Collectors; -import mage.abilities.mana.ManaAbility; /** * @param @@ -24,11 +24,8 @@ public class AbilitiesImpl extends ArrayList implements Ab private static final ThreadLocalStringBuilder threadLocalBuilder = new ThreadLocalStringBuilder(200); - public AbilitiesImpl() { - } - public AbilitiesImpl(T... abilities) { - addAll(Arrays.asList(abilities)); + Collections.addAll(this, abilities); } public AbilitiesImpl(final AbilitiesImpl abilities) { @@ -104,42 +101,38 @@ public List getRules(String source, boolean capitalize) { @Override public Abilities getActivatedAbilities(Zone zone) { return stream() - .filter(ability -> ability instanceof ActivatedAbility) + .filter(ActivatedAbility.class::isInstance) .filter(ability -> ability.getZone().match(zone)) - .map(ability -> (ActivatedAbility) ability) + .map(ActivatedAbility.class::cast) .collect(Collectors.toCollection(AbilitiesImpl::new)); - } @Override public Abilities getPlayableAbilities(Zone zone) { return stream() - .filter(ability -> (ability instanceof ActivatedAbility)) + .filter(ActivatedAbility.class::isInstance) .filter(ability -> ability.getZone().match(zone)) - .map(ability -> (ActivatedAbility) ability) + .map(ActivatedAbility.class::cast) .collect(Collectors.toCollection(AbilitiesImpl::new)); - } @Override public Abilities getActivatedManaAbilities(Zone zone) { return stream() - .filter(ability -> ability instanceof ActivatedManaAbilityImpl) + .filter(ActivatedManaAbilityImpl.class::isInstance) .filter(ability -> ability.getZone().match(zone)) - .map(ability -> (ActivatedManaAbilityImpl) ability) + .map(ActivatedManaAbilityImpl.class::cast) .collect(Collectors.toCollection(AbilitiesImpl::new)); - } @Override public Abilities getAvailableActivatedManaAbilities(Zone zone, UUID playerId, Game game) { return stream() - .filter(ability -> ability instanceof ActivatedManaAbilityImpl) + .filter(ActivatedManaAbilityImpl.class::isInstance) .filter(ability -> ability.getZone().match(zone)) - .filter(ability -> (((ActivatedManaAbilityImpl) ability).canActivate(playerId, game).canActivate())) - .map(ability -> (ActivatedManaAbilityImpl) ability) + .map(ActivatedManaAbilityImpl.class::cast) + .filter(ability -> ability.canActivate(playerId, game).canActivate()) .collect(Collectors.toCollection(AbilitiesImpl::new)); - } @Override @@ -148,26 +141,23 @@ public Abilities getManaAbilities(Zone zone) { .filter(ability -> ability.getAbilityType() == AbilityType.MANA) .filter(ability -> ability.getZone().match(zone)) .collect(Collectors.toCollection(AbilitiesImpl::new)); - } @Override public Abilities getEvasionAbilities() { return stream() - .filter(ability -> ability instanceof EvasionAbility) - .map(ability -> (EvasionAbility) ability) + .filter(EvasionAbility.class::isInstance) + .map(EvasionAbility.class::cast) .collect(Collectors.toCollection(AbilitiesImpl::new)); - } @Override public Abilities getStaticAbilities(Zone zone) { return stream() - .filter(ability -> ability instanceof StaticAbility) + .filter(StaticAbility.class::isInstance) .filter(ability -> ability.getZone().match(zone)) - .map(ability -> (StaticAbility) ability) + .map(StaticAbility.class::cast) .collect(Collectors.toCollection(AbilitiesImpl::new)); - } @Override @@ -189,17 +179,17 @@ public Abilities getTriggeredAbilities(Zone zone) { @Override public boolean hasPoolDependantAbilities() { return stream() - .anyMatch(ability -> ability.getAbilityType() == AbilityType.MANA - && ((ManaAbility) ability).isPoolDependant()); + .filter(ability -> ability.getAbilityType() == AbilityType.MANA) + .map(ManaAbility.class::cast) + .anyMatch(ManaAbility::isPoolDependant); } @Override public Abilities getProtectionAbilities() { return stream() - .filter(ability -> ability instanceof ProtectionAbility) - .map(ability -> (ProtectionAbility) ability) + .filter(ProtectionAbility.class::isInstance) + .map(ProtectionAbility.class::cast) .collect(Collectors.toCollection(AbilitiesImpl::new)); - } @Override @@ -232,7 +222,7 @@ public void newOriginalId() { @Override public boolean contains(T ability) { - for (Iterator iterator = this.iterator(); iterator.hasNext();) { // simple loop can cause java.util.ConcurrentModificationException + for (Iterator iterator = this.iterator(); iterator.hasNext(); ) { // simple loop can cause java.util.ConcurrentModificationException T test = iterator.next(); // Checking also by getRule() without other restrictions is a problem when a triggered ability will be copied to a permanent that had the same ability // already before the copy. Because then it keeps the triggered ability twice and it triggers twice. @@ -253,12 +243,11 @@ public boolean contains(T ability) { @Override public boolean containsRule(T ability) { // TODO: remove - return stream().anyMatch(rule -> rule.getRule().equals(ability.getRule())); + return stream().map(T::getRule).anyMatch(ability.getRule()::equals); } @Override public boolean containsAll(Abilities abilities) { - if (this.size() < abilities.size()) { return false; } @@ -272,12 +261,12 @@ public boolean containsAll(Abilities abilities) { @Override public boolean containsKey(UUID abilityId) { // TODO: remove - return stream().anyMatch(ability -> abilityId.equals(ability.getId())); + return stream().map(T::getId).anyMatch(abilityId::equals); } @Override public boolean containsClass(Class classObject) { - return stream().anyMatch(ability -> ability.getClass().equals(classObject)); + return stream().map(T::getClass).anyMatch(classObject::equals); } public Optional get(UUID abilityId) { diff --git a/Mage/src/main/java/mage/abilities/Ability.java b/Mage/src/main/java/mage/abilities/Ability.java index adecc7743843..967164cfbce3 100644 --- a/Mage/src/main/java/mage/abilities/Ability.java +++ b/Mage/src/main/java/mage/abilities/Ability.java @@ -22,6 +22,7 @@ import mage.watchers.Watcher; import java.io.Serializable; +import java.util.ArrayList; import java.util.List; import java.util.UUID; import mage.MageIdentifier; @@ -47,7 +48,7 @@ public interface Ability extends Controllable, Serializable { * * @see mage.players.PlayerImpl#playAbility(mage.abilities.ActivatedAbility, * mage.game.Game) - * @see mage.game.GameImpl#addTriggeredAbility(mage.abilities.TriggeredAbility) + * @see Game#addTriggeredAbility(TriggeredAbility, GameEvent) * @see mage.game.GameImpl#addDelayedTriggeredAbility(mage.abilities.DelayedTriggeredAbility) */ void newId(); @@ -57,7 +58,7 @@ public interface Ability extends Controllable, Serializable { * * @see mage.players.PlayerImpl#playAbility(mage.abilities.ActivatedAbility, * mage.game.Game) - * @see mage.game.GameImpl#addTriggeredAbility(mage.abilities.TriggeredAbility) + * @see Game#addTriggeredAbility(TriggeredAbility, GameEvent) * @see mage.game.GameImpl#addDelayedTriggeredAbility(mage.abilities.DelayedTriggeredAbility) */ void newOriginalId(); @@ -87,6 +88,8 @@ public interface Ability extends Controllable, Serializable { /** * Gets the id of the object which put this ability in motion. * + * WARNING, MageSingleton abilities contains dirty data here, so you can't use sourceId with it + * * @return The {@link java.util.UUID} of the object this ability is * associated with. */ @@ -123,6 +126,14 @@ public interface Ability extends Controllable, Serializable { */ ManaCosts getManaCosts(); + default List getManaCostSymbols() { + List symbols = new ArrayList<>(); + for (ManaCost cost : getManaCosts()) { + symbols.add(cost.getText()); + } + return symbols; + } + /** * Gets all the {@link ManaCosts} that must be paid before activating this * ability. These costs should be modified by any modification effects @@ -311,7 +322,7 @@ public interface Ability extends Controllable, Serializable { Modes getModes(); - boolean canChooseTarget(Game game); + boolean canChooseTarget(Game game, UUID playerId); /** * Gets the list of sub-abilities associated with this ability. diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index ebf5bd8031fe..14376a487c32 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -142,6 +142,10 @@ public void newId() { this.id = UUID.randomUUID(); } getEffects().newId(); + + for (Ability sub : getSubAbilities()) { + sub.newId(); + } } @Override @@ -315,7 +319,7 @@ public boolean activate(Game game, boolean noMana) { // unit tests only: it allows to add targets/choices by two ways: // 1. From cast/activate command params (process it here) // 2. From single addTarget/setChoice, it's a preffered method for tests (process it in normal choose dialogs like human player) - if (controller.isTestMode()) { + if (controller.isTestsMode()) { if (!controller.addTargets(this, game)) { return false; } @@ -901,24 +905,36 @@ public Modes getModes() { } @Override - public boolean canChooseTarget(Game game) { + public boolean canChooseTarget(Game game, UUID playerId) { if (this instanceof SpellAbility) { if (SpellAbilityType.SPLIT_FUSED.equals(((SpellAbility) this).getSpellAbilityType())) { Card card = game.getCard(getSourceId()); if (card != null) { - return canChooseTargetAbility(((SplitCard) card).getLeftHalfCard().getSpellAbility(), game, getControllerId()) - && canChooseTargetAbility(((SplitCard) card).getRightHalfCard().getSpellAbility(), game, getControllerId()); + return canChooseTargetAbility(((SplitCard) card).getLeftHalfCard().getSpellAbility(), game, playerId) + && canChooseTargetAbility(((SplitCard) card).getRightHalfCard().getSpellAbility(), game, playerId); } return false; } } - return canChooseTargetAbility(this, game, getControllerId()); + return canChooseTargetAbility(this, game, playerId); } private static boolean canChooseTargetAbility(Ability ability, Game game, UUID controllerId) { int found = 0; for (Mode mode : ability.getModes().values()) { - if (mode.getTargets().canChoose(ability.getSourceId(), ability.getControllerId(), game)) { + boolean validTargets = true; + for (Target target : mode.getTargets()) { + UUID abilityControllerId = controllerId; + if (target.getTargetController() != null) { + abilityControllerId = target.getTargetController(); + } + if (!target.canChoose(ability.getSourceId(), abilityControllerId, game)) { + validTargets = false; + break; + } + } + + if (validTargets) { found++; if (ability.getModes().isEachModeMoreThanOnce()) { return true; @@ -966,7 +982,7 @@ public boolean isInUseableZone(Game game, MageObject source, GameEvent event) { } // check against current state Zone test = game.getState().getZone(parameterSourceId); - return test != null && zone.match(test); + return zone.match(test); } @Override diff --git a/Mage/src/main/java/mage/abilities/ActivatedAbility.java b/Mage/src/main/java/mage/abilities/ActivatedAbility.java index 521e2e00c0ae..2390b78c0594 100644 --- a/Mage/src/main/java/mage/abilities/ActivatedAbility.java +++ b/Mage/src/main/java/mage/abilities/ActivatedAbility.java @@ -1,11 +1,12 @@ package mage.abilities; +import mage.ApprovingObject; import mage.abilities.mana.ManaOptions; import mage.constants.TargetController; +import mage.constants.TimingRule; import mage.game.Game; import java.util.UUID; -import mage.ApprovingObject; /** * @author BetaSteward_at_googlemail.com @@ -68,4 +69,6 @@ public static ActivationStatus getTrue(Ability approvingObjectAbility, Game game void setMaxActivationsPerTurn(int maxActivationsPerTurn); int getMaxActivationsPerTurn(Game game); + + public void setTiming(TimingRule timing); } diff --git a/Mage/src/main/java/mage/abilities/ActivatedAbilityImpl.java b/Mage/src/main/java/mage/abilities/ActivatedAbilityImpl.java index 9abeadaf0fa3..ecf20a1ca669 100644 --- a/Mage/src/main/java/mage/abilities/ActivatedAbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/ActivatedAbilityImpl.java @@ -1,5 +1,6 @@ package mage.abilities; +import mage.ApprovingObject; import mage.MageObject; import mage.abilities.condition.Condition; import mage.abilities.costs.Cost; @@ -19,7 +20,6 @@ import mage.util.CardUtil; import java.util.UUID; -import mage.ApprovingObject; /** * @author BetaSteward_at_googlemail.com @@ -194,7 +194,7 @@ public ActivationStatus canActivate(UUID playerId, Game game) { || game.canPlaySorcery(playerId) || null != approvingObject) { if (costs.canPay(this, this, playerId, game) - && canChooseTarget(game)) { + && canChooseTarget(game, playerId)) { this.activatorId = playerId; return new ActivationStatus(true, approvingObject); } @@ -237,6 +237,7 @@ public TimingRule getTiming() { return timing; } + @Override public void setTiming(TimingRule timing) { this.timing = timing; } diff --git a/Mage/src/main/java/mage/abilities/CompoundAbility.java b/Mage/src/main/java/mage/abilities/CompoundAbility.java index e07d93498046..9e4afc1b203e 100644 --- a/Mage/src/main/java/mage/abilities/CompoundAbility.java +++ b/Mage/src/main/java/mage/abilities/CompoundAbility.java @@ -7,9 +7,9 @@ * @author noxx */ public class CompoundAbility extends AbilitiesImpl { - + private String ruleText; - + public CompoundAbility(Ability... abilities) { this(null, abilities); } @@ -31,11 +31,13 @@ public String getRule() { } StringBuilder sb = new StringBuilder(); - List rules = super.getRules(null,false); + List rules = super.getRules(null, false); for (int index = 0; index < rules.size(); index++) { if (index > 0) { if (index < rules.size() - 1) { sb.append(", "); + } else if (rules.size() > 2) { + sb.append(", and "); } else { sb.append(" and "); } diff --git a/Mage/src/main/java/mage/abilities/DelayedTriggeredAbilities.java b/Mage/src/main/java/mage/abilities/DelayedTriggeredAbilities.java index c1d4d0e7f01f..1bb564fc2929 100644 --- a/Mage/src/main/java/mage/abilities/DelayedTriggeredAbilities.java +++ b/Mage/src/main/java/mage/abilities/DelayedTriggeredAbilities.java @@ -37,7 +37,7 @@ public void checkTriggers(GameEvent event, Game game) { continue; } if (ability.checkTrigger(event, game)) { - ability.trigger(game, ability.controllerId); + ability.trigger(game, ability.controllerId, event); if (ability.getTriggerOnlyOnce()) { it.remove(); } diff --git a/Mage/src/main/java/mage/abilities/Modes.java b/Mage/src/main/java/mage/abilities/Modes.java index 464b26424d36..4230bee2e963 100644 --- a/Mage/src/main/java/mage/abilities/Modes.java +++ b/Mage/src/main/java/mage/abilities/Modes.java @@ -10,6 +10,7 @@ import mage.game.Game; import mage.players.Player; import mage.target.common.TargetOpponent; +import mage.util.CardUtil; import mage.util.RandomUtil; import java.util.*; @@ -38,6 +39,7 @@ public class Modes extends LinkedHashMap { private String chooseText = null; private boolean resetEachTurn = false; private Condition moreCondition; + private boolean mayChooseNone = false; public Modes() { this.currentMode = new Mode(); @@ -77,6 +79,7 @@ public Modes(final Modes modes) { this.currentMode = get(modes.getMode().getId()); // need fix? } this.moreCondition = modes.moreCondition; + this.mayChooseNone = modes.mayChooseNone; } public Modes copy() { @@ -326,7 +329,8 @@ public boolean choose(Game game, Ability source) { if (isEachModeOnlyOnce()) { setAlreadySelectedModes(source, game); } - return this.selectedModes.size() >= this.getMinModes(); + return this.selectedModes.size() >= this.getMinModes() + || (this.selectedModes.size() == 0 && mayChooseNone); } this.addSelectedMode(choice.getId()); if (currentMode == null) { @@ -460,26 +464,23 @@ public String getText() { return this.getMode().getEffects().getText(this.getMode()); } StringBuilder sb = new StringBuilder(); + if (mayChooseNone) { + sb.append("you may "); + } if (this.chooseText != null) { sb.append(chooseText); - } else if (this.getMaxModesFilter() != null) { - sb.append("choose one or more. Each mode must target ").append(getMaxModesFilter().getMessage()); } else if (this.getMinModes() == 0 && this.getMaxModes(null, null) == 1) { sb.append("choose up to one"); - } else if (this.getMinModes() == 0 && this.getMaxModes(null, null) == 3) { + } else if (this.getMinModes() == 0 && this.getMaxModes(null, null) > 2) { sb.append("choose any number"); - } else if (this.getMinModes() == 1 && this.getMaxModes(null, null) > 2) { - sb.append("choose one or more"); } else if (this.getMinModes() == 1 && this.getMaxModes(null, null) == 2) { sb.append("choose one or both"); - } else if (this.getMinModes() == 2 && this.getMaxModes(null, null) == 2) { - sb.append("choose two"); - } else if (this.getMinModes() == 3 && this.getMaxModes(null, null) == 3) { - sb.append("choose three"); - } else if (this.getMinModes() == 4 && this.getMaxModes(null, null) == 4) { - sb.append("choose four"); + } else if (this.getMinModes() == 1 && this.getMaxModes(null, null) > 2) { + sb.append("choose one or more"); + } else if (this.getMinModes() == this.getMaxModes(null, null)) { + sb.append("choose " + CardUtil.numberToText(this.getMinModes())); } else { - sb.append("choose one"); + throw new UnsupportedOperationException("no text available for this selection of min and max modes"); } if (isEachModeOnlyOnce()) { @@ -489,7 +490,9 @@ public String getText() { sb.append(" this turn"); } - if (isEachModeMoreThanOnce()) { + if (this.getMaxModesFilter() != null) { + sb.append(". Each mode must target ").append(getMaxModesFilter().getMessage()).append('.'); + } else if (isEachModeMoreThanOnce()) { sb.append(". You may choose the same mode more than once."); } else if (chooseText == null) { sb.append(" —"); @@ -540,4 +543,8 @@ public void setResetEachTurn(boolean resetEachTurn) { public void setChooseText(String chooseText) { this.chooseText = chooseText; } + + public void setMayChooseNone(boolean mayChooseNone) { + this.mayChooseNone = mayChooseNone; + } } diff --git a/Mage/src/main/java/mage/abilities/OpeningHandAction.java b/Mage/src/main/java/mage/abilities/OpeningHandAction.java index 6f7b2f8c433c..1cf713f70991 100644 --- a/Mage/src/main/java/mage/abilities/OpeningHandAction.java +++ b/Mage/src/main/java/mage/abilities/OpeningHandAction.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities; import mage.cards.Card; diff --git a/Mage/src/main/java/mage/abilities/SpellAbility.java b/Mage/src/main/java/mage/abilities/SpellAbility.java index c44baebfe6c9..956d5fab00f9 100644 --- a/Mage/src/main/java/mage/abilities/SpellAbility.java +++ b/Mage/src/main/java/mage/abilities/SpellAbility.java @@ -13,9 +13,9 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; +import mage.util.CardUtil; -import java.util.Optional; -import java.util.UUID; +import java.util.*; /** * @author BetaSteward_at_googlemail.com @@ -65,9 +65,19 @@ public boolean spellCanBeActivatedRegularlyNow(UUID playerId, Game game) { if (object == null) { return false; } - if (game.getState().getValue("PlayFromNotOwnHandZone" + object.getId()) != null) { - return (Boolean) game.getState().getValue("PlayFromNotOwnHandZone" + object.getId()); // card like Chandra, Torch of Defiance +1 loyal ability) + + // forced to cast (can be part id or main id) + Set idsToCheck = new HashSet<>(); + idsToCheck.add(object.getId()); + if (object instanceof Card) { + idsToCheck.add(((Card) object).getMainCard().getId()); + } + for (UUID idToCheck : idsToCheck) { + if (game.getState().getValue("PlayFromNotOwnHandZone" + idToCheck) != null) { + return (Boolean) game.getState().getValue("PlayFromNotOwnHandZone" + idToCheck); // card like Chandra, Torch of Defiance +1 loyal ability) + } } + return null != game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.CAST_AS_INSTANT, this, playerId, game) // check this first to allow Offering in main phase || timing == TimingRule.INSTANT || object.hasAbility(FlashAbility.getInstance(), game) @@ -118,13 +128,13 @@ public ActivationStatus canActivate(UUID playerId, Game game) { // fused can be called from hand only, so not permitting object allows or other zones checks // see https://www.mtgsalvation.com/forums/magic-fundamentals/magic-rulings/magic-rulings-archives/251926-snapcaster-mage-and-fuse if (game.getState().getZone(splitCard.getId()) == Zone.HAND) { - return new ActivationStatus(splitCard.getLeftHalfCard().getSpellAbility().canChooseTarget(game) - && splitCard.getRightHalfCard().getSpellAbility().canChooseTarget(game), null); + return new ActivationStatus(splitCard.getLeftHalfCard().getSpellAbility().canChooseTarget(game, playerId) + && splitCard.getRightHalfCard().getSpellAbility().canChooseTarget(game, playerId), null); } } return ActivationStatus.getFalse(); } else { - return new ActivationStatus(canChooseTarget(game), approvingObject); + return new ActivationStatus(canChooseTarget(game, playerId), approvingObject); } } } @@ -164,9 +174,17 @@ public SpellAbility copy() { return new SpellAbility(this); } - public SpellAbility copySpell() { + public SpellAbility copySpell(Card originalCard, Card copiedCard) { + // all copied spells must have own copied card + Map mapOldToNew = CardUtil.getOriginalToCopiedPartsMap(originalCard, copiedCard); + if (!mapOldToNew.containsKey(this.getSourceId())) { + throw new IllegalStateException("Can't find source id after copy: " + originalCard.getName() + " -> " + copiedCard.getName()); + } + UUID copiedSourceId = mapOldToNew.getOrDefault(this.getSourceId(), copiedCard).getId(); + SpellAbility spell = new SpellAbility(this); - spell.id = UUID.randomUUID(); + spell.newId(); + spell.setSourceId(copiedSourceId); return spell; } diff --git a/Mage/src/main/java/mage/abilities/StateTriggeredAbility.java b/Mage/src/main/java/mage/abilities/StateTriggeredAbility.java index ae45a834e8db..137ea7f95ffd 100644 --- a/Mage/src/main/java/mage/abilities/StateTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/StateTriggeredAbility.java @@ -31,10 +31,10 @@ public final boolean checkEventType(GameEvent event, Game game) { } @Override - public void trigger(Game game, UUID controllerId) { + public void trigger(Game game, UUID controllerId, GameEvent triggeringEvent) { //20100716 - 603.8 game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.TRUE); - super.trigger(game, controllerId); + super.trigger(game, controllerId, triggeringEvent); } @Override diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbilities.java b/Mage/src/main/java/mage/abilities/TriggeredAbilities.java index 39d20f9f0df1..644313f542d8 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbilities.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbilities.java @@ -2,8 +2,6 @@ package mage.abilities; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; import mage.MageObject; import mage.constants.Zone; import mage.game.Game; @@ -11,6 +9,10 @@ import mage.game.events.NumberOfTriggersEvent; import mage.game.permanent.Permanent; import mage.game.stack.Spell; +import org.apache.log4j.Logger; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; /** * @author BetaSteward_at_googlemail.com @@ -21,6 +23,8 @@ */ public class TriggeredAbilities extends ConcurrentHashMap { + private static final Logger logger = Logger.getLogger(TriggeredAbilities.class); + private final Map> sources = new HashMap<>(); public TriggeredAbilities() { @@ -36,7 +40,7 @@ public TriggeredAbilities(final TriggeredAbilities abilities) { } public void checkStateTriggers(Game game) { - for (Iterator it = this.values().iterator(); it.hasNext();) { + for (Iterator it = this.values().iterator(); it.hasNext(); ) { TriggeredAbility ability = it.next(); if (ability instanceof StateTriggeredAbility && ((StateTriggeredAbility) ability).canTrigger(game)) { checkTrigger(ability, null, game); @@ -45,7 +49,7 @@ public void checkStateTriggers(Game game) { } public void checkTriggers(GameEvent event, Game game) { - for (Iterator it = this.values().iterator(); it.hasNext();) { + for (Iterator it = this.values().iterator(); it.hasNext(); ) { TriggeredAbility ability = it.next(); if (ability.checkEventType(event, game)) { checkTrigger(ability, event, game); @@ -87,11 +91,11 @@ private void checkTrigger(TriggeredAbility ability, GameEvent event, Game game) } } - if (ability.checkTrigger(event, game)) { + if (ability.checkTrigger(event, game) && ability.checkTriggeredAlready(game)) { NumberOfTriggersEvent numberOfTriggersEvent = new NumberOfTriggersEvent(ability, event); if (!game.replaceEvent(numberOfTriggersEvent)) { for (int i = 0; i < numberOfTriggersEvent.getAmount(); i++) { - ability.trigger(game, ability.getControllerId()); + ability.trigger(game, ability.getControllerId(), event); } } } @@ -102,8 +106,8 @@ private void checkTrigger(TriggeredAbility ability, GameEvent event, Game game) /** * Adds a by sourceId gained triggered ability * - * @param ability - the gained ability - * @param sourceId - the source that assigned the ability + * @param ability - the gained ability + * @param sourceId - the source that assigned the ability * @param attachedTo - the object that gained the ability */ public void add(TriggeredAbility ability, UUID sourceId, MageObject attachedTo) { @@ -115,7 +119,7 @@ public void add(TriggeredAbility ability, UUID sourceId, MageObject attachedTo) this.add(ability, attachedTo); List uuidList = new LinkedList<>(); uuidList.add(sourceId); - // if the object that gained the ability moves zone, also then the triggered ability must be removed + // if the object that gained the ability moves from zone then the triggered ability must be removed uuidList.add(attachedTo.getId()); sources.put(getKey(ability, attachedTo), uuidList); } diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbility.java b/Mage/src/main/java/mage/abilities/TriggeredAbility.java index c5f60d5a9eaa..943c054e5496 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbility.java @@ -1,17 +1,17 @@ package mage.abilities; -import java.util.UUID; import mage.game.Game; import mage.game.events.GameEvent; +import java.util.UUID; + /** - * * @author BetaSteward_at_googlemail.com */ public interface TriggeredAbility extends Ability { - void trigger(Game game, UUID controllerId); + void trigger(Game game, UUID controllerId, GameEvent event); /** * This check for the relevant event types is called at first to prevent @@ -37,6 +37,10 @@ public interface TriggeredAbility extends Ability { */ boolean checkTrigger(GameEvent event, Game game); + boolean checkTriggeredAlready(Game game); + + TriggeredAbility setTriggersOnce(boolean triggersOnce); + boolean checkInterveningIfClause(Game game); boolean isOptional(); @@ -48,4 +52,7 @@ public interface TriggeredAbility extends Ability { @Override TriggeredAbility copy(); + void setTriggerEvent(GameEvent event); + + GameEvent getTriggerEvent(); } diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java b/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java index 992567d5afb0..3f81d96a849e 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java @@ -8,13 +8,13 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentToken; import mage.players.Player; import mage.util.CardUtil; import java.util.Locale; import java.util.UUID; -import mage.game.permanent.Permanent; -import mage.game.permanent.PermanentToken; /** * @author BetaSteward_at_googlemail.com @@ -23,6 +23,8 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge protected boolean optional; protected boolean leavesTheBattlefieldTrigger; + private boolean triggersOnce = false; + private GameEvent triggerEvent = null; public TriggeredAbilityImpl(Zone zone, Effect effect) { this(zone, effect, false); @@ -48,16 +50,53 @@ public TriggeredAbilityImpl(final TriggeredAbilityImpl ability) { super(ability); this.optional = ability.optional; this.leavesTheBattlefieldTrigger = ability.leavesTheBattlefieldTrigger; + this.triggersOnce = ability.triggersOnce; } @Override - public void trigger(Game game, UUID controllerId) { + public void trigger(Game game, UUID controllerId, GameEvent triggeringEvent) { //20091005 - 603.4 if (checkInterveningIfClause(game)) { - game.addTriggeredAbility(this); + setLastTrigger(game); + game.addTriggeredAbility(this, triggeringEvent); } } + private final void setLastTrigger(Game game) { + if (!triggersOnce) { + return; + } + game.getState().setValue(CardUtil.getCardZoneString( + "lastTurnTriggered" + originalId, sourceId, game + ), game.getTurnNum()); + } + + @Override + public void setTriggerEvent(GameEvent triggerEvent) { + this.triggerEvent = triggerEvent; + } + + @Override + public GameEvent getTriggerEvent() { + return triggerEvent; + } + + @Override + public boolean checkTriggeredAlready(Game game) { + if (!triggersOnce) { + return true; + } + Integer lastTurnTriggered = (Integer) game.getState().getValue( + CardUtil.getCardZoneString("lastTurnTriggered" + originalId, sourceId, game) + ); + return lastTurnTriggered == null || lastTurnTriggered != game.getTurnNum(); + } + + public TriggeredAbility setTriggersOnce(boolean triggersOnce) { + this.triggersOnce = triggersOnce; + return this; + } + @Override public boolean checkInterveningIfClause(Game game) { return true; @@ -130,6 +169,9 @@ public String getRule() { } sb.append(superRule); + if (triggersOnce) { + sb.append(" This ability triggers only once each turn."); + } } return sb.toString(); @@ -247,13 +289,13 @@ public static boolean isInUseableZoneDiesTrigger(TriggeredAbility source, GameEv if (!source.hasSourceObjectAbility(game, sourceObject, event)) { return false; // the permanent does currently not have or before it dies the ability so no trigger } - + // check now it is in graveyard (only if it is no token and was the target itself) if (source.getSourceId().equals(event.getTargetId()) // source is also the target && !(sourceObject instanceof PermanentToken) // it's no token && sourceObject.getZoneChangeCounter(game) + 1 == game.getState().getZoneChangeCounter(source.getSourceId())) { // It's in the next zone Zone after = game.getState().getZone(source.getSourceId()); - if (after == null || !Zone.GRAVEYARD.match(after)) { // Zone is not the graveyard + if (!Zone.GRAVEYARD.match(after)) { // Zone is not the graveyard return false; // Moving to graveyard was replaced so no trigger } } diff --git a/Mage/src/main/java/mage/abilities/common/ActivateAsSorceryActivatedAbility.java b/Mage/src/main/java/mage/abilities/common/ActivateAsSorceryActivatedAbility.java index 52e7ec285931..a3a300619902 100644 --- a/Mage/src/main/java/mage/abilities/common/ActivateAsSorceryActivatedAbility.java +++ b/Mage/src/main/java/mage/abilities/common/ActivateAsSorceryActivatedAbility.java @@ -3,6 +3,7 @@ import mage.abilities.ActivatedAbilityImpl; import mage.abilities.costs.Cost; import mage.abilities.effects.Effect; +import mage.constants.TargetController; import mage.constants.TimingRule; import mage.constants.Zone; @@ -28,6 +29,9 @@ public ActivateAsSorceryActivatedAbility copy() { @Override public String getRule() { - return super.getRule() + " Activate this ability only any time you could cast a sorcery."; + if (mayActivate == TargetController.OPPONENT) { + return super.getRule() + " Only your opponents may activate this ability and only as a sorcery."; + } + return super.getRule() + " Activate only as a sorcery."; } } diff --git a/Mage/src/main/java/mage/abilities/common/ActivateIfConditionActivatedAbility.java b/Mage/src/main/java/mage/abilities/common/ActivateIfConditionActivatedAbility.java index 425baedf81a8..37697a4675ab 100644 --- a/Mage/src/main/java/mage/abilities/common/ActivateIfConditionActivatedAbility.java +++ b/Mage/src/main/java/mage/abilities/common/ActivateIfConditionActivatedAbility.java @@ -34,7 +34,7 @@ public String getRule() { if (condition instanceof InvertCondition) { sb.append(" You can't activate this ability "); } else { - sb.append(" Activate this ability only "); + sb.append(" Activate only "); } if (!condition.toString().startsWith("during") && !condition.toString().startsWith("before") diff --git a/Mage/src/main/java/mage/abilities/common/ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility.java index ba4bd5c48bd0..70e397ab4a29 100644 --- a/Mage/src/main/java/mage/abilities/common/ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility.java @@ -48,14 +48,14 @@ public boolean checkTrigger(GameEvent event, Game game) { || !permanent.hasSubtype(planeswalkerSubType, game)) { return false; } - Effect effect = this.getEffects().get(0); - effect.setValue("stackAbility", stackAbility); + this.getEffects().setValue("stackAbility", stackAbility); return true; } @Override public String getRule() { - return "Whenever you activate a loyalty ability of a " + planeswalkerSubType.getDescription() + " planeswalker, " + - this.getEffects().get(0).getText(getModes().getMode()) + "."; + return "Whenever you activate a loyalty ability of a " + + planeswalkerSubType.getDescription() + + " planeswalker, " + super.getRule(); } } diff --git a/Mage/src/main/java/mage/abilities/common/AttachableToRestrictedAbility.java b/Mage/src/main/java/mage/abilities/common/AttachableToRestrictedAbility.java index 3cc732e8c398..fac6a4a26b6c 100644 --- a/Mage/src/main/java/mage/abilities/common/AttachableToRestrictedAbility.java +++ b/Mage/src/main/java/mage/abilities/common/AttachableToRestrictedAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/common/AttacksAndIsNotBlockedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttacksAndIsNotBlockedTriggeredAbility.java index c76c3c5fd643..6ac4db886ceb 100644 --- a/Mage/src/main/java/mage/abilities/common/AttacksAndIsNotBlockedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/AttacksAndIsNotBlockedTriggeredAbility.java @@ -1,14 +1,11 @@ - package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.constants.Zone; import mage.game.Game; -import mage.game.combat.CombatGroup; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; public class AttacksAndIsNotBlockedTriggeredAbility extends TriggeredAbilityImpl { @@ -40,23 +37,20 @@ public AttacksAndIsNotBlockedTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARE_BLOCKERS_STEP; + return event.getType() == EventType.UNBLOCKED_ATTACKER; } @Override public boolean checkTrigger(GameEvent event, Game game) { - Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(getSourceId()); - if (sourcePermanent != null && sourcePermanent.isAttacking()) { - for (CombatGroup combatGroup : game.getCombat().getGroups()) { - if (combatGroup.getBlockers().isEmpty() && combatGroup.getAttackers().contains(getSourceId())) { - if (setTargetPointer) { - this.getEffects().setTargetPointer(new FixedTarget(game.getCombat().getDefendingPlayerId(getSourceId(), game))); - } - return true; - } - } + if (!event.getTargetId().equals(getSourceId())) { + return false; + } + if (setTargetPointer) { + this.getEffects().setTargetPointer(new FixedTarget( + game.getCombat().getDefendingPlayerId(getSourceId(), game), game + )); } - return false; + return true; } @Override diff --git a/Mage/src/main/java/mage/abilities/common/AttacksAttachedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttacksAttachedTriggeredAbility.java index 4d8c1289fb1e..c6d7b212b142 100644 --- a/Mage/src/main/java/mage/abilities/common/AttacksAttachedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/AttacksAttachedTriggeredAbility.java @@ -1,7 +1,6 @@ package mage.abilities.common; -import java.util.Locale; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.constants.AttachmentType; @@ -10,6 +9,8 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import java.util.Locale; + /** * "When enchanted/equipped creature attacks " triggered ability * @@ -52,9 +53,8 @@ public boolean checkTrigger(GameEvent event, Game game) { Permanent equipment = game.getPermanent(this.sourceId); if (equipment != null && equipment.getAttachedTo() != null && event.getSourceId().equals(equipment.getAttachedTo())) { - for (Effect effect : this.getEffects()) { - effect.setValue("sourceId", event.getSourceId()); - } + getEffects().setValue("sourceId", event.getSourceId()); + getEffects().setValue("attachedPermanent", game.getPermanent(event.getSourceId())); return true; } return false; diff --git a/Mage/src/main/java/mage/abilities/common/AttacksCreatureYouControlTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttacksCreatureYouControlTriggeredAbility.java index 8de2fcbd020b..6ec8413e06c2 100644 --- a/Mage/src/main/java/mage/abilities/common/AttacksCreatureYouControlTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/AttacksCreatureYouControlTriggeredAbility.java @@ -8,6 +8,7 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @author noxx @@ -59,7 +60,7 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { Permanent sourcePermanent = game.getPermanent(event.getSourceId()); - if (sourcePermanent != null && filter.match(sourcePermanent, sourceId, controllerId, game)) { + if (filter.match(sourcePermanent, sourceId, controllerId, game)) { if (setTargetPointer) { this.getEffects().setTargetPointer(new FixedTarget(event.getSourceId(), game)); } @@ -75,17 +76,6 @@ public AttacksCreatureYouControlTriggeredAbility copy() { @Override public String getRule() { - String an; - String who = filter.getMessage(); - if (who.startsWith("another") || who.startsWith("a ")) { - an = ""; - } else if (who.length() > 0 && "aeiou".contains(who.charAt(0) + "")) { - an = "an "; - } else { - an = "a "; - } - - return "When" + (once ? "" : "ever") - + " " + an + who + " attacks, " + super.getRule(); + return "When" + (once ? "" : "ever") + " " + CardUtil.addArticle(filter.getMessage()) + " attacks, " + super.getRule(); } } diff --git a/Mage/src/main/java/mage/abilities/common/AttacksEachCombatStaticAbility.java b/Mage/src/main/java/mage/abilities/common/AttacksEachCombatStaticAbility.java index 51c6c3a3592e..eba1a419f866 100644 --- a/Mage/src/main/java/mage/abilities/common/AttacksEachCombatStaticAbility.java +++ b/Mage/src/main/java/mage/abilities/common/AttacksEachCombatStaticAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.common; import mage.abilities.StaticAbility; diff --git a/Mage/src/main/java/mage/abilities/common/AttacksWithCreaturesTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttacksWithCreaturesTriggeredAbility.java index 6bae81f1d48a..35611ab11e55 100644 --- a/Mage/src/main/java/mage/abilities/common/AttacksWithCreaturesTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/AttacksWithCreaturesTriggeredAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.common; import java.util.UUID; @@ -60,8 +55,8 @@ public boolean checkTrigger(GameEvent event, Game game) { if (game.getCombat().getAttackingPlayerId().equals(getControllerId())) { int attackerCount = 0; for (UUID attackerId : game.getCombat().getAttackers()) { - Permanent permanent = game.getPermanent(attackerId); - if (permanent != null && filter.match(game.getPermanent(attackerId), game)) { + Permanent attacker = game.getPermanent(attackerId); + if (filter.match(attacker, game)) { attackerCount++; } } @@ -72,6 +67,9 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { + if (minAttackers == 0) { + return "Whenever you attack, " + super.getRule(); + } StringBuilder sb = new StringBuilder("Whenever you attack with " + CardUtil.numberToText(minAttackers) + " or more "); sb.append(filter.getMessage()); sb.append(", "); diff --git a/Mage/src/main/java/mage/abilities/common/BecomesBlockedAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesBlockedAllTriggeredAbility.java index 035f4339cebf..47d4d724c7a2 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesBlockedAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesBlockedAllTriggeredAbility.java @@ -43,7 +43,7 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && filter.match(permanent, getSourceId(), getControllerId(), game)) { + if (filter.match(permanent, getSourceId(), getControllerId(), game)) { if (setTargetPointer) { this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId())); } diff --git a/Mage/src/main/java/mage/abilities/common/BecomesMonstrousTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesMonstrousTriggeredAbility.java index 02f922b6c466..221bee4e577d 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesMonstrousTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesMonstrousTriggeredAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; diff --git a/Mage/src/main/java/mage/abilities/common/BecomesRenownedSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesRenownedSourceTriggeredAbility.java index be2ce971599a..c34399683bd3 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesRenownedSourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesRenownedSourceTriggeredAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTappedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTappedTriggeredAbility.java index 641e408622c4..5157df744c27 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesTappedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesTappedTriggeredAbility.java @@ -55,7 +55,7 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && filter.match(permanent, getSourceId(), getControllerId(), game)) { + if (filter.match(permanent, getSourceId(), getControllerId(), game)) { if (setTargetPointer) { this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId())); } diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetControlledPermanentTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetControlledPermanentTriggeredAbility.java new file mode 100644 index 000000000000..6132b9e5057d --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetControlledPermanentTriggeredAbility.java @@ -0,0 +1,77 @@ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.StackObject; +import mage.players.Player; + +import java.util.*; + +/** + * @author weirddan455 + */ +public class BecomesTargetControlledPermanentTriggeredAbility extends TriggeredAbilityImpl { + + public BecomesTargetControlledPermanentTriggeredAbility(Effect effect, boolean optional) { + super(Zone.BATTLEFIELD, effect, optional); + } + + private BecomesTargetControlledPermanentTriggeredAbility(final BecomesTargetControlledPermanentTriggeredAbility ability) { + super(ability); + } + + @Override + public BecomesTargetControlledPermanentTriggeredAbility copy() { + return new BecomesTargetControlledPermanentTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.TARGETED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + StackObject sourceObject = game.getStack().getStackObject(event.getSourceId()); + if (sourceObject == null) { + return false; + } + Player targetter = game.getPlayer(event.getPlayerId()); + if (targetter == null || !targetter.hasOpponent(this.controllerId, game)) { + return false; + } + Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); + if (permanent == null || !permanent.isControlledBy(this.getControllerId())) { + return false; + } + // If a spell or ability an opponent controls targets a single permanent you control more than once, + // Battle Mammoth’s triggered ability will trigger only once. + // However, if a spell or ability an opponent controls targets multiple permanents you control, + // Battle Mammoth’s triggered ability will trigger once for each of those permanents. + + // targetMap - key - targetId, value - Set of stackObject Ids + Map> targetMap = (Map>) game.getState().getValue("targetMap" + this.id); + if (targetMap == null) { + targetMap = new HashMap<>(); + } + Set sourceObjects = targetMap.get(event.getTargetId()); + if (sourceObjects == null) { + sourceObjects = new HashSet<>(); + } + if (!sourceObjects.add(sourceObject.getId())) { + return false; + } + targetMap.put(event.getTargetId(), sourceObjects); + game.getState().setValue("targetMap" + this.id, targetMap); + return true; + } + + @Override + public String getRule() { + return "Whenever a permanent you control becomes the target of a spell or ability an opponent controls, " + super.getRule(); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetTriggeredAbility.java index 7c945d0a1963..642099b6d65b 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesTargetTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetTriggeredAbility.java @@ -20,7 +20,7 @@ public class BecomesTargetTriggeredAbility extends TriggeredAbilityImpl { private final SetTargetPointer setTargetPointer; public BecomesTargetTriggeredAbility(Effect effect) { - this(effect, StaticFilters.FILTER_SPELL_OR_ABILITY); + this(effect, StaticFilters.FILTER_SPELL_OR_ABILITY_A); } public BecomesTargetTriggeredAbility(Effect effect, FilterStackObject filter) { diff --git a/Mage/src/main/java/mage/abilities/common/BeginningOfPostCombatMainTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BeginningOfPostCombatMainTriggeredAbility.java index 39b13ac515da..4d7c30504688 100644 --- a/Mage/src/main/java/mage/abilities/common/BeginningOfPostCombatMainTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BeginningOfPostCombatMainTriggeredAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; diff --git a/Mage/src/main/java/mage/abilities/common/BeginningOfUpkeepAttachedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BeginningOfUpkeepAttachedTriggeredAbility.java new file mode 100644 index 000000000000..27d0c1262121 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/BeginningOfUpkeepAttachedTriggeredAbility.java @@ -0,0 +1,52 @@ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +/** + * @author TheElk801 + */ +public class BeginningOfUpkeepAttachedTriggeredAbility extends TriggeredAbilityImpl { + + public BeginningOfUpkeepAttachedTriggeredAbility(Effect effect) { + this(effect, false); + } + + public BeginningOfUpkeepAttachedTriggeredAbility(Effect effect, boolean optional) { + super(Zone.BATTLEFIELD, effect, optional); + } + + private BeginningOfUpkeepAttachedTriggeredAbility(final BeginningOfUpkeepAttachedTriggeredAbility ability) { + super(ability); + } + + @Override + public BeginningOfUpkeepAttachedTriggeredAbility copy() { + return new BeginningOfUpkeepAttachedTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.UPKEEP_STEP_PRE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent enchantment = getSourcePermanentOrLKI(game); + if (enchantment == null || !game.isActivePlayer(enchantment.getAttachedTo())) { + return false; + } + this.getEffects().setTargetPointer(new FixedTarget(enchantment.getAttachedTo())); + return true; + } + + @Override + public String getRule() { + return "At the beginning of enchanted player's upkeep, " + super.getRule(); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedByOneOrMoreTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedByOneOrMoreTriggeredAbility.java index 67fb9361456a..bc17e8591820 100644 --- a/Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedByOneOrMoreTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedByOneOrMoreTriggeredAbility.java @@ -57,7 +57,7 @@ public boolean checkTrigger(GameEvent event, Game game) { } for (UUID uuid : group.getBlockers()){ Permanent permanent = game.getPermanentOrLKIBattlefield(uuid); - if (permanent != null && filter.match(permanent, game)){ + if (filter.match(permanent, game)){ return true; } } @@ -67,7 +67,7 @@ public boolean checkTrigger(GameEvent event, Game game) { } for (UUID uuid : group.getAttackers()){ Permanent permanent = game.getPermanentOrLKIBattlefield(uuid); - if (permanent != null && filter.match(permanent, game)){ + if (filter.match(permanent, game)){ return true; } } diff --git a/Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedSourceTriggeredAbility.java index 3863d1904541..63f1fba4d07e 100644 --- a/Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedSourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedSourceTriggeredAbility.java @@ -57,7 +57,7 @@ public boolean checkEventType(GameEvent event, Game game) { public boolean checkTrigger(GameEvent event, Game game) { if (event.getSourceId().equals(this.getSourceId())) { Permanent blocked = game.getPermanent(event.getTargetId()); - if (blocked != null && filter.match(blocked, game)) { + if (filter.match(blocked, game)) { if (setTargetPointer) { this.getEffects().setTargetPointer(new FixedTarget(blocked, game)); } @@ -66,7 +66,7 @@ public boolean checkTrigger(GameEvent event, Game game) { } if (event.getTargetId().equals(this.getSourceId())) { Permanent blocker = game.getPermanent(event.getSourceId()); - if (blocker != null && filter.match(blocker, game)) { + if (filter.match(blocker, game)) { if (setTargetPointer) { this.getEffects().setTargetPointer(new FixedTarget(blocker, game)); } diff --git a/Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedTriggeredAbility.java index 79351ba4747c..85abdd6b62a1 100644 --- a/Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedTriggeredAbility.java @@ -57,7 +57,7 @@ public boolean checkEventType(GameEvent event, Game game) { public boolean checkTrigger(GameEvent event, Game game) { if (event.getSourceId().equals(this.getSourceId())) { Permanent blocked = game.getPermanent(event.getTargetId()); - if (blocked != null && filter.match(blocked, game)) { + if (filter.match(blocked, game)) { if (setTargetPointer) { this.getEffects().setTargetPointer(new FixedTarget(blocked, game)); } @@ -66,7 +66,7 @@ public boolean checkTrigger(GameEvent event, Game game) { } if (event.getTargetId().equals(this.getSourceId())) { Permanent blocker = game.getPermanent(event.getSourceId()); - if (blocker != null && filter.match(blocker, game)) { + if (filter.match(blocker, game)) { if (setTargetPointer) { this.getEffects().setTargetPointer(new FixedTarget(blocker, game)); } diff --git a/Mage/src/main/java/mage/abilities/common/CanBlockOnlyFlyingAbility.java b/Mage/src/main/java/mage/abilities/common/CanBlockOnlyFlyingAbility.java index 4f7d081f5d68..91b83a9f9414 100644 --- a/Mage/src/main/java/mage/abilities/common/CanBlockOnlyFlyingAbility.java +++ b/Mage/src/main/java/mage/abilities/common/CanBlockOnlyFlyingAbility.java @@ -1,9 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - package mage.abilities.common; import mage.abilities.effects.common.combat.CanBlockOnlyFlyingEffect; diff --git a/Mage/src/main/java/mage/abilities/common/CardsLeaveGraveyardTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/CardsLeaveGraveyardTriggeredAbility.java new file mode 100644 index 000000000000..ac04b61a5680 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/CardsLeaveGraveyardTriggeredAbility.java @@ -0,0 +1,54 @@ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.cards.Card; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeGroupEvent; + +import java.util.Objects; + +/** + * @author TheElk801 + */ +public class CardsLeaveGraveyardTriggeredAbility extends TriggeredAbilityImpl { + + public CardsLeaveGraveyardTriggeredAbility(Effect effect) { + super(Zone.BATTLEFIELD, effect, false); + } + + private CardsLeaveGraveyardTriggeredAbility(final CardsLeaveGraveyardTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE_GROUP; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + ZoneChangeGroupEvent zEvent = (ZoneChangeGroupEvent) event; + return zEvent != null + && Zone.GRAVEYARD == zEvent.getFromZone() + && Zone.GRAVEYARD != zEvent.getToZone() + && zEvent.getCards() != null + && zEvent.getCards() + .stream() + .filter(Objects::nonNull) + .map(Card::getOwnerId) + .anyMatch(this::isControlledBy); + } + + @Override + public CardsLeaveGraveyardTriggeredAbility copy() { + return new CardsLeaveGraveyardTriggeredAbility(this); + } + + @Override + public String getRule() { + return "Whenever one or more cards leave your graveyard, " + super.getRule(); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/ChancellorAbility.java b/Mage/src/main/java/mage/abilities/common/ChancellorAbility.java index 4bea26b75b8d..478bd2907ed4 100644 --- a/Mage/src/main/java/mage/abilities/common/ChancellorAbility.java +++ b/Mage/src/main/java/mage/abilities/common/ChancellorAbility.java @@ -44,7 +44,7 @@ public String getRule() { @Override public boolean askUseOpeningHandAction(Card card, Player player, Game game) { - return player.chooseUse(Outcome.PutCardInPlay, "Do you wish to reveal " + card.getIdName() + '?', this, game); + return player.chooseUse(Outcome.PutCardInPlay, "Reveal " + card.getIdName() + '?', this, game); } @Override diff --git a/Mage/src/main/java/mage/abilities/common/ControllerPlaysLandTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/ControllerPlaysLandTriggeredAbility.java index 37a6ff3519b7..5adba04cecd2 100644 --- a/Mage/src/main/java/mage/abilities/common/ControllerPlaysLandTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/ControllerPlaysLandTriggeredAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; diff --git a/Mage/src/main/java/mage/abilities/common/DealsCombatDamageToACreatureTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealsCombatDamageToACreatureTriggeredAbility.java index b52fe3d4b7d6..a274a331ef3d 100644 --- a/Mage/src/main/java/mage/abilities/common/DealsCombatDamageToACreatureTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealsCombatDamageToACreatureTriggeredAbility.java @@ -2,24 +2,24 @@ package mage.abilities.common; -import mage.constants.Zone; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; +import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamagedCreatureEvent; +import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; /** - * * @author LevelX */ public class DealsCombatDamageToACreatureTriggeredAbility extends TriggeredAbilityImpl { - private boolean setTargetPointer; + private final boolean setTargetPointer; public DealsCombatDamageToACreatureTriggeredAbility(Effect effect, boolean optional) { - this(effect, optional, false); + this(effect, optional, false); } public DealsCombatDamageToACreatureTriggeredAbility(Effect effect, boolean optional, boolean setTargetPointer) { @@ -28,38 +28,41 @@ public DealsCombatDamageToACreatureTriggeredAbility(Effect effect, boolean optio } public DealsCombatDamageToACreatureTriggeredAbility(final DealsCombatDamageToACreatureTriggeredAbility ability) { - super(ability); - this.setTargetPointer = ability.setTargetPointer; + super(ability); + this.setTargetPointer = ability.setTargetPointer; } @Override public DealsCombatDamageToACreatureTriggeredAbility copy() { - return new DealsCombatDamageToACreatureTriggeredAbility(this); + return new DealsCombatDamageToACreatureTriggeredAbility(this); } @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getSourceId().equals(this.sourceId) - && ((DamagedCreatureEvent) event).isCombatDamage()) { - if (setTargetPointer) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); - effect.setValue("damage", event.getAmount()); - } - } - return true; + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent == null + || !permanent.isCreature() + || !event.getSourceId().equals(this.sourceId) + || !((DamagedEvent) event).isCombatDamage()) { + return false; + } + if (setTargetPointer) { + for (Effect effect : this.getEffects()) { + effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); + effect.setValue("damage", event.getAmount()); + } } - return false; + return true; } @Override public String getRule() { - return "Whenever {this} deals combat damage to a creature, " + super.getRule(); + return "Whenever {this} deals combat damage to a creature, " + super.getRule(); } } diff --git a/Mage/src/main/java/mage/abilities/common/DealsCombatDamageToAPlayerTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealsCombatDamageToAPlayerTriggeredAbility.java index e05f4cf3bba9..dd9bb2a0b88e 100644 --- a/Mage/src/main/java/mage/abilities/common/DealsCombatDamageToAPlayerTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealsCombatDamageToAPlayerTriggeredAbility.java @@ -6,7 +6,7 @@ import mage.game.Game; import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; -import mage.players.Player; +import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; /** @@ -60,28 +60,34 @@ public DealsCombatDamageToAPlayerTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || (orPlaneswalker && event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER); + || event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getSourceId().equals(getSourceId()) - && ((DamagedEvent) event).isCombatDamage()) { - if (onlyOpponents && event.getType() == GameEvent.EventType.DAMAGED_PLAYER) { - Player controller = game.getPlayer(getControllerId()); - if (controller == null || !controller.hasOpponent(event.getPlayerId(), game)) { + if (!event.getSourceId().equals(getSourceId()) + || !((DamagedEvent) event).isCombatDamage()) { + return false; + } + switch (event.getType()) { + case DAMAGED_PLAYER: + if (onlyOpponents && !game.getOpponents(getControllerId()).contains(event.getTargetId())) { return false; } - } - if (setTargetPointer) { - for (Effect effect : this.getAllEffects()) { - effect.setTargetPointer(new FixedTarget(event.getPlayerId())); - effect.setValue("damage", event.getAmount()); + break; + case DAMAGED_PERMANENT: + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent == null + || !permanent.isPlaneswalker() + || !orPlaneswalker) { + return false; } - } - return true; } - return false; + if (setTargetPointer) { + getAllEffects().setTargetPointer(new FixedTarget(event.getPlayerId())); + getAllEffects().setValue("damage", event.getAmount()); + } + return true; } @Override diff --git a/Mage/src/main/java/mage/abilities/common/DealsDamageAttachedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealsDamageAttachedTriggeredAbility.java index 042e50de1504..d0e35b23a275 100644 --- a/Mage/src/main/java/mage/abilities/common/DealsDamageAttachedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealsDamageAttachedTriggeredAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; @@ -33,9 +28,8 @@ public DealsDamageAttachedTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE - || event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT + || event.getType() == GameEvent.EventType.DAMAGED_PLAYER; } @Override diff --git a/Mage/src/main/java/mage/abilities/common/DealsDamageGainLifeSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealsDamageGainLifeSourceTriggeredAbility.java index b1ba53cfad20..14d3fca3b7db 100644 --- a/Mage/src/main/java/mage/abilities/common/DealsDamageGainLifeSourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealsDamageGainLifeSourceTriggeredAbility.java @@ -33,9 +33,8 @@ public DealsDamageGainLifeSourceTriggeredAbility copy() { } @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE - || event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT + || event.getType() == GameEvent.EventType.DAMAGED_PLAYER; } @Override diff --git a/Mage/src/main/java/mage/abilities/common/DealsDamageToACreatureAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealsDamageToACreatureAllTriggeredAbility.java index e509762fdc18..7d5e8dea3b4a 100644 --- a/Mage/src/main/java/mage/abilities/common/DealsDamageToACreatureAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealsDamageToACreatureAllTriggeredAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; @@ -11,14 +6,12 @@ import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.game.Game; -import mage.game.events.DamagedCreatureEvent; +import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; /** - * * @author LevelX2 */ public class DealsDamageToACreatureAllTriggeredAbility extends TriggeredAbilityImpl { @@ -32,15 +25,15 @@ public class DealsDamageToACreatureAllTriggeredAbility extends TriggeredAbilityI * * @param effect * @param optional - * @param filterPermanent The filter that restricts which permanets have to - * trigger + * @param filterPermanent The filter that restricts which permanets have to + * trigger * @param setTargetPointer The target to be set to target pointer of the - * effect.
- * - PLAYER = player controlling the damage source.
- * - PERMANENT = source permanent.
- * - PERMANENT_TARGET = damaged creature. + * effect.
+ * - PLAYER = player controlling the damage source.
+ * - PERMANENT = source permanent.
+ * - PERMANENT_TARGET = damaged creature. * @param combatDamageOnly The flag to determine if only combat damage has - * to trigger + * to trigger */ public DealsDamageToACreatureAllTriggeredAbility(Effect effect, boolean optional, FilterPermanent filterPermanent, SetTargetPointer setTargetPointer, boolean combatDamageOnly) { super(Zone.BATTLEFIELD, effect, optional); @@ -63,37 +56,42 @@ public DealsDamageToACreatureAllTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (!combatDamageOnly || ((DamagedCreatureEvent) event).isCombatDamage()) { - Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); - if (permanent != null && filterPermanent.match(permanent, getSourceId(), getControllerId(), game)) { - for (Effect effect : this.getEffects()) { - effect.setValue("damage", event.getAmount()); - effect.setValue("sourceId", event.getSourceId()); - switch (setTargetPointer) { - case PLAYER: - effect.setTargetPointer(new FixedTarget(permanent.getControllerId())); - break; - case PERMANENT: - effect.setTargetPointer(new FixedTarget(permanent, game)); - break; - case PERMANENT_TARGET: - Permanent permanent_target = game.getPermanentOrLKIBattlefield(event.getTargetId()); - if (permanent_target != null) { - effect.setTargetPointer(new FixedTarget(permanent_target, game)); - } - break; + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent == null || !permanent.isCreature()) { + return false; + } + if (combatDamageOnly && !((DamagedEvent) event).isCombatDamage()) { + return false; + } + permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); + if (!filterPermanent.match(permanent, getSourceId(), getControllerId(), game)) { + return false; + } + for (Effect effect : this.getEffects()) { + effect.setValue("damage", event.getAmount()); + effect.setValue("sourceId", event.getSourceId()); + switch (setTargetPointer) { + case PLAYER: + effect.setTargetPointer(new FixedTarget(permanent.getControllerId())); + break; + case PERMANENT: + effect.setTargetPointer(new FixedTarget(permanent, game)); + break; + case PERMANENT_TARGET: + Permanent permanent_target = game.getPermanentOrLKIBattlefield(event.getTargetId()); + if (permanent_target != null) { + effect.setTargetPointer(new FixedTarget(permanent_target, game)); } - - } - return true; + break; } + } - return false; + return true; } @Override diff --git a/Mage/src/main/java/mage/abilities/common/DealsDamageToACreatureAttachedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealsDamageToACreatureAttachedTriggeredAbility.java index 10a3024d065b..2fa50eb6e3a1 100644 --- a/Mage/src/main/java/mage/abilities/common/DealsDamageToACreatureAttachedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealsDamageToACreatureAttachedTriggeredAbility.java @@ -6,7 +6,7 @@ import mage.abilities.effects.Effect; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamagedCreatureEvent; +import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; @@ -40,12 +40,12 @@ public DealsDamageToACreatureAttachedTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (!combatOnly || ((DamagedCreatureEvent) event).isCombatDamage()) { + if (!combatOnly || ((DamagedEvent) event).isCombatDamage()) { Permanent attachment = game.getPermanent(this.getSourceId()); if (attachment != null && attachment.isAttachedTo(event.getSourceId())) { diff --git a/Mage/src/main/java/mage/abilities/common/DealsDamageToACreatureTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealsDamageToACreatureTriggeredAbility.java index faa6551756f5..5de73a3a5789 100644 --- a/Mage/src/main/java/mage/abilities/common/DealsDamageToACreatureTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealsDamageToACreatureTriggeredAbility.java @@ -6,7 +6,7 @@ import mage.abilities.effects.Effect; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; -import mage.game.events.DamagedCreatureEvent; +import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; @@ -46,16 +46,16 @@ public DealsDamageToACreatureTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getSourceId().equals(this.sourceId) - && (!combatOnly || ((DamagedCreatureEvent) event).isCombatDamage())) { + && (!combatOnly || ((DamagedEvent) event).isCombatDamage())) { if (filter != null) { Permanent creature = game.getPermanentOrLKIBattlefield(event.getTargetId()); - if (creature == null || !filter.match(creature, getSourceId(), getControllerId(), game)) { + if (!filter.match(creature, getSourceId(), getControllerId(), game)) { return false; } } diff --git a/Mage/src/main/java/mage/abilities/common/DealsDamageToAPlayerAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealsDamageToAPlayerAllTriggeredAbility.java index 1de8a51fa586..4ce2717c7939 100644 --- a/Mage/src/main/java/mage/abilities/common/DealsDamageToAPlayerAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealsDamageToAPlayerAllTriggeredAbility.java @@ -4,6 +4,7 @@ import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.constants.SetTargetPointer; +import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.game.Game; @@ -21,6 +22,7 @@ public class DealsDamageToAPlayerAllTriggeredAbility extends TriggeredAbilityImp private final SetTargetPointer setTargetPointer; private final boolean onlyCombat; private final boolean affectsDefendingPlayer; + private final TargetController targetController; public DealsDamageToAPlayerAllTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional, SetTargetPointer setTargetPointer, boolean onlyCombat) { this(effect, filter, optional, setTargetPointer, onlyCombat, false); @@ -31,11 +33,16 @@ public DealsDamageToAPlayerAllTriggeredAbility(Effect effect, FilterPermanent fi } public DealsDamageToAPlayerAllTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter, boolean optional, SetTargetPointer setTargetPointer, boolean onlyCombat, boolean affectsDefendingPlayer) { + this(zone, effect, filter, optional, setTargetPointer, onlyCombat, affectsDefendingPlayer, TargetController.ANY); + } + + public DealsDamageToAPlayerAllTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter, boolean optional, SetTargetPointer setTargetPointer, boolean onlyCombat, boolean affectsDefendingPlayer, TargetController targetController) { super(zone, effect, optional); this.setTargetPointer = setTargetPointer; this.filter = filter; this.onlyCombat = onlyCombat; this.affectsDefendingPlayer = affectsDefendingPlayer; + this.targetController = targetController; } public DealsDamageToAPlayerAllTriggeredAbility(final DealsDamageToAPlayerAllTriggeredAbility ability) { @@ -44,6 +51,7 @@ public DealsDamageToAPlayerAllTriggeredAbility(final DealsDamageToAPlayerAllTrig this.filter = ability.filter; this.onlyCombat = ability.onlyCombat; this.affectsDefendingPlayer = ability.affectsDefendingPlayer; + this.targetController = ability.targetController; } @Override @@ -61,8 +69,12 @@ public boolean checkTrigger(GameEvent event, Game game) { if (onlyCombat && !((DamagedPlayerEvent) event).isCombatDamage()) { return false; } + if (targetController == TargetController.OPPONENT + && !game.getOpponents(getControllerId()).contains(event.getTargetId())) { + return false; + } Permanent permanent = game.getPermanent(event.getSourceId()); - if (permanent == null || !filter.match(permanent, getSourceId(), getControllerId(), game)) { + if (!filter.match(permanent, getSourceId(), getControllerId(), game)) { return false; } this.getEffects().setValue("damage", event.getAmount()); @@ -84,7 +96,7 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { - return "Whenever " + filter.getMessage() + " deals " + (onlyCombat ? "combat " : "") + "damage to a player, " + super.getRule(); + return "Whenever " + filter.getMessage() + " deals " + (onlyCombat ? "combat " : "") + "damage to " + + (targetController == TargetController.OPPONENT ? "an opponent" : "a player") + ", " + super.getRule(); } - } diff --git a/Mage/src/main/java/mage/abilities/common/DealsDamageToAPlayerTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealsDamageToAPlayerTriggeredAbility.java index 289743355ae2..775b7db56390 100644 --- a/Mage/src/main/java/mage/abilities/common/DealsDamageToAPlayerTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealsDamageToAPlayerTriggeredAbility.java @@ -7,6 +7,7 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; /** @@ -44,21 +45,27 @@ public DealsDamageToAPlayerTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || (orPlaneswalker && event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER); + || event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getSourceId().equals(this.sourceId)) { - if (setTargetPointer) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getPlayerId())); - effect.setValue("damage", event.getAmount()); - } + if (!event.getSourceId().equals(this.sourceId)) { + return false; + } + if (event.getType() == GameEvent.EventType.DAMAGED_PERMANENT) { + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent == null + || !permanent.isPlaneswalker() + || !orPlaneswalker) { + return false; } - return true; } - return false; + if (setTargetPointer) { + getEffects().setTargetPointer(new FixedTarget(event.getPlayerId())); + getEffects().setValue("damage", event.getAmount()); + } + return true; } @Override diff --git a/Mage/src/main/java/mage/abilities/common/DealsDamageToOneOrMoreCreaturesTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealsDamageToOneOrMoreCreaturesTriggeredAbility.java index 7d4a85629ce8..ee984428274c 100644 --- a/Mage/src/main/java/mage/abilities/common/DealsDamageToOneOrMoreCreaturesTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealsDamageToOneOrMoreCreaturesTriggeredAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.common; import mage.abilities.effects.Effect; diff --git a/Mage/src/main/java/mage/abilities/common/DealtDamageAttachedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealtDamageAttachedTriggeredAbility.java index 65d6407e3cfb..a23a25a99e53 100644 --- a/Mage/src/main/java/mage/abilities/common/DealtDamageAttachedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealtDamageAttachedTriggeredAbility.java @@ -41,7 +41,7 @@ public DealtDamageAttachedTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override diff --git a/Mage/src/main/java/mage/abilities/common/DealtDamageToSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealtDamageToSourceTriggeredAbility.java index bbd23b4cbb3d..3fe62d4bb6ae 100644 --- a/Mage/src/main/java/mage/abilities/common/DealtDamageToSourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealtDamageToSourceTriggeredAbility.java @@ -5,7 +5,7 @@ import mage.abilities.effects.Effect; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamagedCreatureEvent; +import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; /** @@ -46,21 +46,19 @@ public DealtDamageToSourceTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_CREATURE || event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_POST; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT || event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_POST; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.DAMAGED_CREATURE && event.getTargetId().equals(getSourceId())) { + if (event.getType() == GameEvent.EventType.DAMAGED_PERMANENT && event.getTargetId().equals(getSourceId())) { if (useValue) { // TODO: this ability should only trigger once for multiple creatures dealing combat damage. // If the damaged creature uses the amount (e.g. Boros Reckoner), this will still trigger separately instead of all at once - for (Effect effect : this.getEffects()) { - effect.setValue("damage", event.getAmount()); - } + getEffects().setValue("damage", event.getAmount()); return true; } else { - if (((DamagedCreatureEvent) event).isCombatDamage()) { + if (((DamagedEvent) event).isCombatDamage()) { if (!usedForCombatDamageStep) { usedForCombatDamageStep = true; return true; diff --git a/Mage/src/main/java/mage/abilities/common/DestroyPlaneswalkerWhenDamagedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DestroyPlaneswalkerWhenDamagedTriggeredAbility.java index 1d8eabc2bd3d..4e31e7ec9ae0 100644 --- a/Mage/src/main/java/mage/abilities/common/DestroyPlaneswalkerWhenDamagedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DestroyPlaneswalkerWhenDamagedTriggeredAbility.java @@ -35,22 +35,23 @@ public DestroyPlaneswalkerWhenDamagedTriggeredAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { Permanent permanent = getSourcePermanentIfItStillExists(game); - if (permanent != null) { - boolean applies = filter != null ? - filter.match(permanent, game) : event.getSourceId().equals(getSourceId()); - if (applies) { - Effect effect = new DestroyTargetEffect(); - effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); - this.getEffects().clear(); - this.addEffect(effect); - return true; - } + if (permanent == null) { + return false; + } + boolean applies = filter != null ? + permanent.isPlaneswalker() && filter.match(permanent, game) : event.getSourceId().equals(getSourceId()); + if (applies) { + Effect effect = new DestroyTargetEffect(); + effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); + this.getEffects().clear(); + this.addEffect(effect); + return true; } return false; } diff --git a/Mage/src/main/java/mage/abilities/common/DiesAttachedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DiesAttachedTriggeredAbility.java index a9874d1f4648..889e190b9a29 100644 --- a/Mage/src/main/java/mage/abilities/common/DiesAttachedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DiesAttachedTriggeredAbility.java @@ -18,8 +18,8 @@ */ public class DiesAttachedTriggeredAbility extends TriggeredAbilityImpl { - private String attachedDescription; - private boolean diesRuleText; + private final String attachedDescription; + private final boolean diesRuleText; protected SetTargetPointer setTargetPointer; public DiesAttachedTriggeredAbility(Effect effect, String attachedDescription) { @@ -35,7 +35,7 @@ public DiesAttachedTriggeredAbility(Effect effect, String attachedDescription, b } public DiesAttachedTriggeredAbility(Effect effect, String attachedDescription, boolean optional, - boolean diesRuleText, SetTargetPointer setTargetPointer) { + boolean diesRuleText, SetTargetPointer setTargetPointer) { super(Zone.ALL, effect, optional); // because the trigger only triggers if the object was attached, it doesn't matter where the Attachment was moved to (e.g. by replacement effect) after the trigger triggered, so Zone.all this.attachedDescription = attachedDescription; this.diesRuleText = diesRuleText; @@ -61,65 +61,64 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (((ZoneChangeEvent) event).isDiesEvent()) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - boolean triggered = false; - if (zEvent.getTarget() != null - && zEvent.getTarget().getAttachments() != null - && zEvent.getTarget().getAttachments().contains(this.getSourceId())) { - triggered = true; - } else { - // If the attachment and attachedTo went to graveyard at the same time, the trigger applies. - // If the attachment is removed beforehand, the trigger fails. - // IE: A player cast Planar Clensing. The attachment is Disenchanted in reponse - // and successfully removed from the attachedTo. The trigger fails. - Permanent attachment = game.getPermanentOrLKIBattlefield(getSourceId()); - Card attachmentCard = game.getCard(getSourceId()); - if (attachment != null - && zEvent.getTargetId() != null - && attachment.getAttachedTo() != null - && zEvent.getTargetId().equals(attachment.getAttachedTo())) { - Permanent attachedTo = game.getPermanentOrLKIBattlefield(attachment.getAttachedTo()); - if (attachedTo != null - && game.getState().getZone(attachedTo.getId()) == (Zone.GRAVEYARD) // Demonic Vigor - && attachmentCard != null - && attachment.getAttachedToZoneChangeCounter() == attachedTo.getZoneChangeCounter(game) - && attachment.getZoneChangeCounter(game) == attachmentCard.getZoneChangeCounter(game)) { - triggered = true; - } + if (!((ZoneChangeEvent) event).isDiesEvent()) { + return false; + } + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + boolean triggered = false; + if (zEvent.getTarget() != null + && zEvent.getTarget().getAttachments() != null + && zEvent.getTarget().getAttachments().contains(this.getSourceId())) { + triggered = true; + } else { + // If the attachment and attachedTo went to graveyard at the same time, the trigger applies. + // If the attachment is removed beforehand, the trigger fails. + // IE: A player cast Planar Clensing. The attachment is Disenchanted in reponse + // and successfully removed from the attachedTo. The trigger fails. + Permanent attachment = getSourcePermanentOrLKI(game); + Card attachmentCard = game.getCard(getSourceId()); + if (attachment != null + && zEvent.getTargetId() != null + && attachment.getAttachedTo() != null + && zEvent.getTargetId().equals(attachment.getAttachedTo())) { + Permanent attachedTo = game.getPermanentOrLKIBattlefield(attachment.getAttachedTo()); + if (attachedTo != null + && game.getState().getZone(attachedTo.getId()) == (Zone.GRAVEYARD) // Demonic Vigor + && attachmentCard != null + && attachment.getAttachedToZoneChangeCounter() == attachedTo.getZoneChangeCounter(game) + && attachment.getZoneChangeCounter(game) == attachmentCard.getZoneChangeCounter(game)) { + triggered = true; } } - if (triggered) { - for (Effect effect : getEffects()) { - if (zEvent.getTarget() != null) { - effect.setValue("attachedTo", zEvent.getTarget()); - effect.setValue("zcc", zEvent.getTarget().getZoneChangeCounter(game) + 1); // zone change info from battlefield - if (setTargetPointer == SetTargetPointer.ATTACHED_TO_CONTROLLER) { - Permanent attachment = game.getPermanentOrLKIBattlefield(getSourceId()); - if (attachment != null - && attachment.getAttachedTo() != null) { - Permanent attachedTo = (Permanent) game.getLastKnownInformation(attachment.getAttachedTo(), - Zone.BATTLEFIELD, attachment.getAttachedToZoneChangeCounter()); - if (attachedTo != null) { - effect.setTargetPointer(new FixedTarget(attachedTo.getControllerId())); - } - } - } - } + } + if (!triggered) { + return false; + } + if (zEvent.getTarget() == null) { + return true; + } + getEffects().setValue("attachedTo", zEvent.getTarget()); + getEffects().setValue("zcc", zEvent.getTarget().getZoneChangeCounter(game) + 1); // zone change info from battlefield + if (setTargetPointer == SetTargetPointer.ATTACHED_TO_CONTROLLER) { + Permanent attachment = game.getPermanentOrLKIBattlefield(getSourceId()); + if (attachment != null + && attachment.getAttachedTo() != null) { + Permanent attachedTo = (Permanent) game.getLastKnownInformation(attachment.getAttachedTo(), + Zone.BATTLEFIELD, attachment.getAttachedToZoneChangeCounter()); + if (attachedTo != null) { + getEffects().setTargetPointer(new FixedTarget(attachedTo.getControllerId())); } - return true; } } - return false; + return true; } @Override public String getRule() { StringBuilder sb = new StringBuilder(); - if(attachedDescription.startsWith("equipped")) { + if (attachedDescription.startsWith("equipped")) { sb.append("Whenever "); - } - else { + } else { sb.append("When "); } sb.append(attachedDescription); diff --git a/Mage/src/main/java/mage/abilities/common/DiesCreatureTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DiesCreatureTriggeredAbility.java index 7603da58232b..38224863c432 100644 --- a/Mage/src/main/java/mage/abilities/common/DiesCreatureTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DiesCreatureTriggeredAbility.java @@ -6,7 +6,7 @@ import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; @@ -69,17 +69,14 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.isDiesEvent()) { - if (filter.match(zEvent.getTarget(), sourceId, controllerId, game)) { - if (setTargetPointer) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); - } - } - return true; - } + if (!zEvent.isDiesEvent() || !filter.match(zEvent.getTarget(), sourceId, controllerId, game)) { + return false; } - return false; + getEffects().setValue("creatureDied", zEvent.getTarget()); + if (setTargetPointer) { + this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game)); + } + return true; } @Override @@ -89,6 +86,6 @@ public boolean isInUseableZone(Game game, MageObject source, GameEvent event) { @Override public String getRule() { - return "Whenever " + filter.getMessage() + " dies, " + super.getRule(); + return "Whenever " + filter.getMessage() + (filter.getMessage().startsWith("one or more") ? " die, " : " dies, ") + super.getRule(); } } diff --git a/Mage/src/main/java/mage/abilities/common/DiesSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DiesSourceTriggeredAbility.java index 906c57762554..ec65ffaba442 100644 --- a/Mage/src/main/java/mage/abilities/common/DiesSourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DiesSourceTriggeredAbility.java @@ -21,7 +21,7 @@ public DiesSourceTriggeredAbility(Effect effect) { this(effect, false); } - public DiesSourceTriggeredAbility(DiesSourceTriggeredAbility ability) { + public DiesSourceTriggeredAbility(final DiesSourceTriggeredAbility ability) { super(ability); } @@ -29,24 +29,19 @@ public DiesSourceTriggeredAbility(DiesSourceTriggeredAbility ability) { public DiesSourceTriggeredAbility copy() { return new DiesSourceTriggeredAbility(this); } - + @Override public boolean checkTrigger(GameEvent event, Game game) { ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.isDiesEvent() && event.getTargetId().equals(getSourceId())) { - for (Effect effect : getEffects()) { - effect.setValue("permanentLeftBattlefield", zEvent.getTarget()); - } - return true; + if (!zEvent.isDiesEvent() || !event.getTargetId().equals(getSourceId())) { + return false; } - return false; + getEffects().setValue("permanentLeftBattlefield", zEvent.getTarget()); + return true; } - + @Override public boolean isInUseableZone(Game game, MageObject source, GameEvent event) { return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game); } - - - } diff --git a/Mage/src/main/java/mage/abilities/common/EndOfCombatTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/EndOfCombatTriggeredAbility.java index 3c332a232b41..77755d1f0e0a 100644 --- a/Mage/src/main/java/mage/abilities/common/EndOfCombatTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/EndOfCombatTriggeredAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; diff --git a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAllTriggeredAbility.java index df37c43e816d..84410cb01c5a 100644 --- a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAllTriggeredAbility.java @@ -84,10 +84,11 @@ public boolean checkEventType(GameEvent event, Game game) { public boolean checkTrigger(GameEvent event, Game game) { UUID targetId = event.getTargetId(); Permanent permanent = game.getPermanent(targetId); - if (permanent == null || !filter.match(permanent, getSourceId(), getControllerId(), game)) { + if (!filter.match(permanent, getSourceId(), getControllerId(), game)) { return false; } this.getEffects().setValue("permanentEnteringBattlefield", permanent); + this.getEffects().setValue("permanentEnteringControllerId", permanent.getControllerId()); if (setTargetPointer == SetTargetPointer.NONE) { return true; } @@ -112,7 +113,11 @@ public String getRule() { sb.append("{this} or another "); } sb.append(filter.getMessage()); - sb.append(" enters the battlefield"); + if (filter.getMessage().startsWith("one or more")) { + sb.append(" enter the battlefield"); + } else { + sb.append(" enters the battlefield"); + } if (controlledText) { sb.append(" under your control, "); } else { diff --git a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldOrAttacksAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldOrAttacksAllTriggeredAbility.java index c42b9026d973..6e4db93693a9 100644 --- a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldOrAttacksAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldOrAttacksAllTriggeredAbility.java @@ -79,7 +79,7 @@ public boolean checkEventType(GameEvent event, Game game) { public boolean checkTrigger(GameEvent event, Game game) { Permanent permanent = game.getPermanent(event.getTargetId()); if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD - && permanent != null && filter.match(permanent, getSourceId(), getControllerId(), game)) { + && filter.match(permanent, getSourceId(), getControllerId(), game)) { if (setTargetPointer != SetTargetPointer.NONE) { for (Effect effect : this.getEffects()) { switch (setTargetPointer) { @@ -98,7 +98,7 @@ public boolean checkTrigger(GameEvent event, Game game) { Permanent attacker = game.getPermanent(event.getSourceId()); if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED - && attacker != null && filter.match(attacker, getSourceId(), getControllerId(), game)) { + && filter.match(attacker, getSourceId(), getControllerId(), game)) { if (setTargetPointer != SetTargetPointer.NONE) { for (Effect effect : this.getEffects()) { switch (setTargetPointer) { diff --git a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldOrLeavesSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldOrLeavesSourceTriggeredAbility.java index 12bebc04f300..edc8f88645a9 100644 --- a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldOrLeavesSourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldOrLeavesSourceTriggeredAbility.java @@ -50,6 +50,6 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { - return "When {this} enters the battlefield or leaves the battlefield, " + super.getRule(); + return "When {this} enters or leaves the battlefield, " + super.getRule(); } } diff --git a/Mage/src/main/java/mage/abilities/common/FetchLandActivatedAbility.java b/Mage/src/main/java/mage/abilities/common/FetchLandActivatedAbility.java index 0180f8ece008..157a18dc376e 100644 --- a/Mage/src/main/java/mage/abilities/common/FetchLandActivatedAbility.java +++ b/Mage/src/main/java/mage/abilities/common/FetchLandActivatedAbility.java @@ -2,49 +2,36 @@ package mage.abilities.common; -import mage.MageObject; import mage.abilities.ActivatedAbilityImpl; import mage.abilities.costs.common.PayLifeCost; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; -import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.Predicate; import mage.filter.predicate.Predicates; import mage.target.common.TargetCardInLibrary; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - /** * @author BetaSteward_at_googlemail.com */ public class FetchLandActivatedAbility extends ActivatedAbilityImpl { - public FetchLandActivatedAbility(Set subtypes) { - this(true, subtypes); + public FetchLandActivatedAbility(SubType subType1, SubType subType2) { + this(true, subType1, subType2); } - public FetchLandActivatedAbility(boolean withDamage, Set subtypes) { + public FetchLandActivatedAbility(boolean withDamage, SubType subType1, SubType subType2) { super(Zone.BATTLEFIELD, null); addCost(new TapSourceCost()); if (withDamage) { addCost(new PayLifeCost(1)); } addCost(new SacrificeSourceCost()); - FilterCard filter = new FilterCard(subTypeNames(subtypes)); - filter.add(CardType.LAND.getPredicate()); - List> subtypePredicates = new ArrayList<>(); - for (SubType subtype : subtypes) { - subtypePredicates.add(subtype.getPredicate()); - } - filter.add(Predicates.or(subtypePredicates)); + FilterCard filter = new FilterCard(subType1.getDescription() + " or " + subType2.getDescription() + " card"); + filter.add(Predicates.or(subType1.getPredicate(), subType2.getPredicate())); TargetCardInLibrary target = new TargetCardInLibrary(filter); addEffect(new SearchLibraryPutInPlayEffect(target, false, true, Outcome.PutLandInPlay)); } @@ -53,10 +40,6 @@ private FetchLandActivatedAbility(FetchLandActivatedAbility ability) { super(ability); } - private String subTypeNames(Set subTypes) { - return subTypes.stream().map(SubType::getDescription).collect(Collectors.joining(" or ")) + " card"; - } - @Override public FetchLandActivatedAbility copy() { return new FetchLandActivatedAbility(this); diff --git a/Mage/src/main/java/mage/abilities/common/GoadAttachedAbility.java b/Mage/src/main/java/mage/abilities/common/GoadAttachedAbility.java index bcf564d9004b..3f5c6b43a96e 100644 --- a/Mage/src/main/java/mage/abilities/common/GoadAttachedAbility.java +++ b/Mage/src/main/java/mage/abilities/common/GoadAttachedAbility.java @@ -25,7 +25,7 @@ public GoadAttachedAbility(Effect... effects) { } this.addEffect(new AttacksIfAbleAttachedEffect( Duration.WhileOnBattlefield, AttachmentType.AURA - ).setText(", and is goaded. ")); + ).setText((getEffects().size() > 1 ? ", " : " ") + "and is goaded. (It attacks each combat if able")); this.addEffect(new GoadAttackEffect()); } @@ -43,7 +43,7 @@ class GoadAttackEffect extends RestrictionEffect { GoadAttackEffect() { super(Duration.WhileOnBattlefield); - staticText = "(It attacks each combat if able and attacks a player other than you if able.)"; + staticText = "and attacks a player other than you if able.)"; } private GoadAttackEffect(final GoadAttackEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/common/LeavesBattlefieldAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/LeavesBattlefieldAllTriggeredAbility.java index 83d6d92f957b..f77104272489 100644 --- a/Mage/src/main/java/mage/abilities/common/LeavesBattlefieldAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/LeavesBattlefieldAllTriggeredAbility.java @@ -62,7 +62,7 @@ public boolean checkTrigger(GameEvent event, Game game) { if (zEvent.getFromZone() == Zone.BATTLEFIELD) { UUID targetId = event.getTargetId(); Permanent permanent = game.getPermanentOrLKIBattlefield(targetId); - if (permanent != null && filter.match(permanent, getSourceId(), getControllerId(), game)) { + if (filter.match(permanent, getSourceId(), getControllerId(), game)) { if (setTargetPointer != SetTargetPointer.NONE) { for (Effect effect : this.getEffects()) { switch (setTargetPointer) { diff --git a/Mage/src/main/java/mage/abilities/common/LimitedTimesPerTurnActivatedAbility.java b/Mage/src/main/java/mage/abilities/common/LimitedTimesPerTurnActivatedAbility.java index 78e0aec13b3a..893fccdf6901 100644 --- a/Mage/src/main/java/mage/abilities/common/LimitedTimesPerTurnActivatedAbility.java +++ b/Mage/src/main/java/mage/abilities/common/LimitedTimesPerTurnActivatedAbility.java @@ -42,12 +42,13 @@ public boolean resolve(Game game) { @Override public String getRule() { - StringBuilder sb = new StringBuilder(super.getRule()).append(" Activate this ability "); + StringBuilder sb = new StringBuilder(super.getRule()).append(" Activate "); if (condition != null) { - sb.append("only ").append(condition.toString()).append(" and "); + String message = condition.toString(); + sb.append("only ").append(message.startsWith("if ") || message.startsWith("during") ? message : "if " + message).append(" and "); } if (getTiming() == TimingRule.SORCERY) { - sb.append("only any time you could cast a sorcery and "); + sb.append("only as a sorcery and "); } switch (maxActivationsPerTurn) { case 1: diff --git a/Mage/src/main/java/mage/abilities/common/MagecraftAbility.java b/Mage/src/main/java/mage/abilities/common/MagecraftAbility.java new file mode 100644 index 000000000000..f88fe3a48c02 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/MagecraftAbility.java @@ -0,0 +1,56 @@ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; + +/** + * @author TheElk801 + */ +public class MagecraftAbility extends TriggeredAbilityImpl { + + public static final String SPELL_KEY = "castCopiedSpell"; + + public MagecraftAbility(Effect effect) { + this(effect, false); + } + + public MagecraftAbility(Effect effect, boolean optional) { + super(Zone.BATTLEFIELD, effect, optional); + } + + private MagecraftAbility(final MagecraftAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.COPIED_STACKOBJECT + || event.getType() == GameEvent.EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Spell spell = game.getSpell(event.getTargetId()); + if (spell == null + || !spell.isControlledBy(getControllerId()) + || !spell.isInstantOrSorcery()) { + return false; + } + getEffects().setValue(SPELL_KEY, spell); + return true; + } + + @Override + public String getRule() { + return "Magecraft — Whenever you cast or copy an instant or sorcery spell, " + super.getRule(); + } + + @Override + public MagecraftAbility copy() { + return new MagecraftAbility(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/OpponentPlaysLandTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/OpponentPlaysLandTriggeredAbility.java index 86b230daedc7..4ef1a719f538 100644 --- a/Mage/src/main/java/mage/abilities/common/OpponentPlaysLandTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/OpponentPlaysLandTriggeredAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; diff --git a/Mage/src/main/java/mage/abilities/common/PayMoreToCastAsThoughtItHadFlashAbility.java b/Mage/src/main/java/mage/abilities/common/PayMoreToCastAsThoughtItHadFlashAbility.java index 3faa62f5c458..7595f01dc4a7 100644 --- a/Mage/src/main/java/mage/abilities/common/PayMoreToCastAsThoughtItHadFlashAbility.java +++ b/Mage/src/main/java/mage/abilities/common/PayMoreToCastAsThoughtItHadFlashAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.common; import mage.abilities.SpellAbility; diff --git a/Mage/src/main/java/mage/abilities/common/PlaneswalkerEntersWithLoyaltyCountersAbility.java b/Mage/src/main/java/mage/abilities/common/PlaneswalkerEntersWithLoyaltyCountersAbility.java index eb7e8edd7939..db7355e37abb 100644 --- a/Mage/src/main/java/mage/abilities/common/PlaneswalkerEntersWithLoyaltyCountersAbility.java +++ b/Mage/src/main/java/mage/abilities/common/PlaneswalkerEntersWithLoyaltyCountersAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.common; import mage.abilities.effects.EntersBattlefieldEffect; diff --git a/Mage/src/main/java/mage/abilities/common/PutIntoGraveFromBattlefieldSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/PutIntoGraveFromBattlefieldSourceTriggeredAbility.java index 0f83feefa23f..802870f032b1 100644 --- a/Mage/src/main/java/mage/abilities/common/PutIntoGraveFromBattlefieldSourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/PutIntoGraveFromBattlefieldSourceTriggeredAbility.java @@ -46,8 +46,7 @@ public boolean checkTrigger(GameEvent event, Game game) { ZoneChangeEvent zEvent = (ZoneChangeEvent) event; Permanent permanent = zEvent.getTarget(); if (permanent != null - && zEvent.getToZone() == Zone.GRAVEYARD - && zEvent.getFromZone() == Zone.BATTLEFIELD) { + && zEvent.isDiesEvent()) { return !onlyToControllerGraveyard || this.isControlledBy(game.getOwnerId(zEvent.getTargetId())); } } diff --git a/Mage/src/main/java/mage/abilities/common/SacrificeAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/SacrificeAllTriggeredAbility.java index 5753fc3e7d84..d7d37ed4239c 100644 --- a/Mage/src/main/java/mage/abilities/common/SacrificeAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/SacrificeAllTriggeredAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; @@ -63,7 +58,7 @@ public boolean checkTrigger(GameEvent event, Game game) { break; } Permanent sacrificedPermanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); - return sacrificed && sacrificedPermanent != null && filter.match(sacrificedPermanent, getSourceId(), getControllerId(), game); + return sacrificed && filter.match(sacrificedPermanent, getSourceId(), getControllerId(), game); } @Override diff --git a/Mage/src/main/java/mage/abilities/common/SpellCastAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/SpellCastAllTriggeredAbility.java index b59492ab8c06..536f85de845b 100644 --- a/Mage/src/main/java/mage/abilities/common/SpellCastAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/SpellCastAllTriggeredAbility.java @@ -10,9 +10,9 @@ import mage.game.events.GameEvent; import mage.game.stack.Spell; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** - * * @author LevelX2 */ public class SpellCastAllTriggeredAbility extends TriggeredAbilityImpl { @@ -59,23 +59,19 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && filter.match(spell, getSourceId(), getControllerId(), game)) { - if (setTargetPointer != SetTargetPointer.NONE) { - for (Effect effect : this.getEffects()) { - switch (setTargetPointer) { - case SPELL: - effect.setTargetPointer(new FixedTarget(spell.getId())); - break; - case PLAYER: - effect.setTargetPointer(new FixedTarget(spell.getControllerId())); - break; - } - - } - } - return true; + if (!filter.match(spell, getSourceId(), getControllerId(), game)) { + return false; + } + getEffects().setValue("spellCast", spell); + switch (setTargetPointer) { + case SPELL: + getEffects().setTargetPointer(new FixedTarget(spell.getId())); + break; + case PLAYER: + getEffects().setTargetPointer(new FixedTarget(spell.getControllerId())); + break; } - return false; + return true; } @Override @@ -83,7 +79,7 @@ public String getRule() { if (rule != null && !rule.isEmpty()) { return rule; } - return "Whenever a player casts " + filter.getMessage() + ", " + super.getRule(); + return "Whenever a player casts " + CardUtil.addArticle(filter.getMessage()) + ", " + super.getRule(); } @Override diff --git a/Mage/src/main/java/mage/abilities/common/SpellCastControllerTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/SpellCastControllerTriggeredAbility.java index 0ffd42761746..612129deea55 100644 --- a/Mage/src/main/java/mage/abilities/common/SpellCastControllerTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/SpellCastControllerTriggeredAbility.java @@ -68,7 +68,7 @@ public boolean checkEventType(GameEvent event, Game game) { public boolean checkTrigger(GameEvent event, Game game) { if (event.getPlayerId().equals(this.getControllerId())) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && filter.match(spell, getSourceId(), getControllerId(), game)) { + if (filter.match(spell, getSourceId(), getControllerId(), game)) { if (rememberSource) { this.getEffects().setValue("spellCast", spell); if (rememberSourceAsCard) { diff --git a/Mage/src/main/java/mage/abilities/common/SpellCastOpponentTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/SpellCastOpponentTriggeredAbility.java index df7e7e02ae47..c4f1513f705c 100644 --- a/Mage/src/main/java/mage/abilities/common/SpellCastOpponentTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/SpellCastOpponentTriggeredAbility.java @@ -1,4 +1,3 @@ - package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; @@ -6,6 +5,7 @@ import mage.constants.SetTargetPointer; import mage.constants.Zone; import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; @@ -16,12 +16,11 @@ */ public class SpellCastOpponentTriggeredAbility extends TriggeredAbilityImpl { - private static final FilterSpell spellCard = new FilterSpell("a spell"); protected FilterSpell filter; protected SetTargetPointer setTargetPointer; public SpellCastOpponentTriggeredAbility(Effect effect, boolean optional) { - this(effect, spellCard, optional); + this(effect, StaticFilters.FILTER_SPELL_A, optional); } public SpellCastOpponentTriggeredAbility(Effect effect, FilterSpell filter, boolean optional) { @@ -58,28 +57,27 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (game.getPlayer(this.getControllerId()).hasOpponent(event.getPlayerId(), game)) { - Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && filter.match(spell, game)) { - if (setTargetPointer != SetTargetPointer.NONE) { - for (Effect effect : this.getEffects()) { - switch (setTargetPointer) { - case SPELL: - effect.setTargetPointer(new FixedTarget(event.getTargetId())); - break; - case PLAYER: - effect.setTargetPointer(new FixedTarget(event.getPlayerId())); - break; - default: - throw new UnsupportedOperationException("Value of SetTargetPointer not supported!"); - } - - } - } - return true; - } + if (!game.getPlayer(this.getControllerId()).hasOpponent(event.getPlayerId(), game)) { + return false; + } + Spell spell = game.getStack().getSpell(event.getTargetId()); + if (!filter.match(spell, getSourceId(), getControllerId(), game)) { + return false; + } + getEffects().setValue("spellCast", spell); + switch (setTargetPointer) { + case NONE: + break; + case SPELL: + getEffects().setTargetPointer(new FixedTarget(event.getTargetId())); + break; + case PLAYER: + getEffects().setTargetPointer(new FixedTarget(event.getPlayerId())); + break; + default: + throw new UnsupportedOperationException("Value of SetTargetPointer not supported!"); } - return false; + return true; } @Override diff --git a/Mage/src/main/java/mage/abilities/common/SpellCounteredControllerTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/SpellCounteredControllerTriggeredAbility.java index d6573ff521d7..1efcec8b7c01 100644 --- a/Mage/src/main/java/mage/abilities/common/SpellCounteredControllerTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/SpellCounteredControllerTriggeredAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; diff --git a/Mage/src/main/java/mage/abilities/common/TapForManaAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/TapForManaAllTriggeredAbility.java index 570487225f59..52d4b20bda88 100644 --- a/Mage/src/main/java/mage/abilities/common/TapForManaAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/TapForManaAllTriggeredAbility.java @@ -45,7 +45,7 @@ public boolean checkTrigger(GameEvent event, Game game) { return false; } Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); - if (permanent != null && filter.match(permanent, getSourceId(), getControllerId(), game)) { + if (filter.match(permanent, getSourceId(), getControllerId(), game)) { ManaEvent mEvent = (ManaEvent) event; for(Effect effect:getEffects()) { effect.setValue("mana", mEvent.getMana()); diff --git a/Mage/src/main/java/mage/abilities/common/TapForManaAllTriggeredManaAbility.java b/Mage/src/main/java/mage/abilities/common/TapForManaAllTriggeredManaAbility.java index 8c73d19bd09f..a9fdfb0c80be 100644 --- a/Mage/src/main/java/mage/abilities/common/TapForManaAllTriggeredManaAbility.java +++ b/Mage/src/main/java/mage/abilities/common/TapForManaAllTriggeredManaAbility.java @@ -43,7 +43,7 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); - if (permanent != null && filter.match(permanent, getSourceId(), getControllerId(), game)) { + if (filter.match(permanent, getSourceId(), getControllerId(), game)) { ManaEvent mEvent = (ManaEvent) event; for(Effect effect:getEffects()) { effect.setValue("mana", mEvent.getMana()); diff --git a/Mage/src/main/java/mage/abilities/common/TurnedFaceUpAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/TurnedFaceUpAllTriggeredAbility.java index 6b954afe1a9e..599f3e3ce269 100644 --- a/Mage/src/main/java/mage/abilities/common/TurnedFaceUpAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/TurnedFaceUpAllTriggeredAbility.java @@ -67,7 +67,7 @@ public boolean checkTrigger(GameEvent event, Game game) { } } Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && filter.match(permanent, getSourceId(), getControllerId(), game)) { + if (filter.match(permanent, getSourceId(), getControllerId(), game)) { if (setTargetPointer) { for (Effect effect : getEffects()) { effect.setTargetPointer(new FixedTarget(event.getTargetId())); diff --git a/Mage/src/main/java/mage/abilities/common/TurnedFaceUpSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/TurnedFaceUpSourceTriggeredAbility.java index e7ab856a9c39..08a564fa1d3d 100644 --- a/Mage/src/main/java/mage/abilities/common/TurnedFaceUpSourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/TurnedFaceUpSourceTriggeredAbility.java @@ -1,9 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; diff --git a/Mage/src/main/java/mage/abilities/common/WerewolfBackTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/WerewolfBackTriggeredAbility.java new file mode 100644 index 000000000000..536d4c10a057 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/WerewolfBackTriggeredAbility.java @@ -0,0 +1,35 @@ +package mage.abilities.common; + +import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.constants.TargetController; +import mage.game.Game; + +/** + * @author TheElk801 + */ +public class WerewolfBackTriggeredAbility extends BeginningOfUpkeepTriggeredAbility { + + public WerewolfBackTriggeredAbility() { + super(new TransformSourceEffect(false), TargetController.ANY, false); + } + + private WerewolfBackTriggeredAbility(final WerewolfBackTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkInterveningIfClause(Game game) { + return TwoOrMoreSpellsWereCastLastTurnCondition.instance.apply(game, this); + } + + @Override + public WerewolfBackTriggeredAbility copy() { + return new WerewolfBackTriggeredAbility(this); + } + + @Override + public String getRule() { + return "At the beginning of each upkeep, if a player cast two or more spells last turn, transform {this}."; + } +} diff --git a/Mage/src/main/java/mage/abilities/common/WerewolfFrontTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/WerewolfFrontTriggeredAbility.java new file mode 100644 index 000000000000..70d91b1a37cd --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/WerewolfFrontTriggeredAbility.java @@ -0,0 +1,35 @@ +package mage.abilities.common; + +import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.constants.TargetController; +import mage.game.Game; + +/** + * @author TheElk801 + */ +public class WerewolfFrontTriggeredAbility extends BeginningOfUpkeepTriggeredAbility { + + public WerewolfFrontTriggeredAbility() { + super(new TransformSourceEffect(true), TargetController.ANY, false); + } + + private WerewolfFrontTriggeredAbility(final WerewolfFrontTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkInterveningIfClause(Game game) { + return NoSpellsWereCastLastTurnCondition.instance.apply(game, this); + } + + @Override + public WerewolfFrontTriggeredAbility copy() { + return new WerewolfFrontTriggeredAbility(this); + } + + @Override + public String getRule() { + return "At the beginning of each upkeep, if no spells were cast last turn, transform {this}."; + } +} diff --git a/Mage/src/main/java/mage/abilities/common/ZoneChangeAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/ZoneChangeAllTriggeredAbility.java index 10ee16f53840..0ceb073ef178 100644 --- a/Mage/src/main/java/mage/abilities/common/ZoneChangeAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/ZoneChangeAllTriggeredAbility.java @@ -60,7 +60,7 @@ public boolean checkTrigger(GameEvent event, Game game) { } else { perm = game.getPermanent(event.getTargetId()); // LevelX2: maybe this part is not neccessary } - if (perm != null && filter.match(perm, sourceId, controllerId, game)) { + if (filter.match(perm, sourceId, controllerId, game)) { return true; } } diff --git a/Mage/src/main/java/mage/abilities/common/delayed/AtTheBeginOfNextCleanupDelayedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/delayed/AtTheBeginOfNextCleanupDelayedTriggeredAbility.java index c190783a5dfe..dff7c520944b 100644 --- a/Mage/src/main/java/mage/abilities/common/delayed/AtTheBeginOfNextCleanupDelayedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/delayed/AtTheBeginOfNextCleanupDelayedTriggeredAbility.java @@ -1,9 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - package mage.abilities.common.delayed; import mage.abilities.DelayedTriggeredAbility; diff --git a/Mage/src/main/java/mage/abilities/common/delayed/PactDelayedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/delayed/PactDelayedTriggeredAbility.java index 442915ff96e5..a3fef3628c40 100644 --- a/Mage/src/main/java/mage/abilities/common/delayed/PactDelayedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/delayed/PactDelayedTriggeredAbility.java @@ -39,7 +39,7 @@ public boolean checkTrigger(GameEvent event, Game game) { @Override public String getRule() { - return "At the beginning of your next upkeep " + modes.getText(); + return "
At the beginning of your next upkeep, " + modes.getText(); } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/AddendumCondition.java b/Mage/src/main/java/mage/abilities/condition/common/AddendumCondition.java index b982b23060cd..bc634265c11c 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/AddendumCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/AddendumCondition.java @@ -6,6 +6,8 @@ import mage.game.stack.Spell; /** + * Addendum — If you cast this spell during your main phase, you get some boost + * * @author LevelX2 */ @@ -23,6 +25,6 @@ public boolean apply(Game game, Ability source) { return true; } Spell spell = game.getSpell(source.getSourceId()); - return spell != null && !spell.isCopy(); + return spell != null && !spell.isCopy(); // copies are not casted } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/AfterBlockersAreDeclaredCondition.java b/Mage/src/main/java/mage/abilities/condition/common/AfterBlockersAreDeclaredCondition.java index f4b682f30628..05c0b1cac65d 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/AfterBlockersAreDeclaredCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/AfterBlockersAreDeclaredCondition.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.condition.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/condition/common/AfterUpkeepStepCondtion.java b/Mage/src/main/java/mage/abilities/condition/common/AfterUpkeepStepCondtion.java index 34e628e587e2..94de517c5936 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/AfterUpkeepStepCondtion.java +++ b/Mage/src/main/java/mage/abilities/condition/common/AfterUpkeepStepCondtion.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.condition.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/condition/common/AttachedToMatchesFilterCondition.java b/Mage/src/main/java/mage/abilities/condition/common/AttachedToMatchesFilterCondition.java index 380bccbd88ec..98fefd12798b 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/AttachedToMatchesFilterCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/AttachedToMatchesFilterCondition.java @@ -29,12 +29,10 @@ public boolean apply(Game game, Ability source) { if (attachedTo == null) { attachedTo = (Permanent) game.getLastKnownInformation(permanent.getAttachedTo(), Zone.BATTLEFIELD); } - if (attachedTo != null) { - if (filter.match(attachedTo, attachedTo.getId(), attachedTo.getControllerId(), game)) { - return true; - } - + if (filter.match(attachedTo, attachedTo.getId(), attachedTo.getControllerId(), game)) { + return true; } + } return false; } diff --git a/Mage/src/main/java/mage/abilities/condition/common/AttackedOrBlockedThisCombatSourceCondition.java b/Mage/src/main/java/mage/abilities/condition/common/AttackedOrBlockedThisCombatSourceCondition.java index 17ee4adf336d..ea858470f5c0 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/AttackedOrBlockedThisCombatSourceCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/AttackedOrBlockedThisCombatSourceCondition.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.condition.common; import mage.MageObjectReference; diff --git a/Mage/src/main/java/mage/abilities/condition/common/AttackedThisTurnCondition.java b/Mage/src/main/java/mage/abilities/condition/common/AttackedThisTurnCondition.java index 5ee7dfb4a859..fdea49e2d43e 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/AttackedThisTurnCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/AttackedThisTurnCondition.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.condition.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/condition/common/BeforeAttackersAreDeclaredCondition.java b/Mage/src/main/java/mage/abilities/condition/common/BeforeAttackersAreDeclaredCondition.java index 08b515ec81bf..21a294ed4ca6 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/BeforeAttackersAreDeclaredCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/BeforeAttackersAreDeclaredCondition.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.condition.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/condition/common/BeforeBlockersAreDeclaredCondition.java b/Mage/src/main/java/mage/abilities/condition/common/BeforeBlockersAreDeclaredCondition.java index cf529fa0de26..3e80bb6418ba 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/BeforeBlockersAreDeclaredCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/BeforeBlockersAreDeclaredCondition.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.condition.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/condition/common/CastFromEverywhereSourceCondition.java b/Mage/src/main/java/mage/abilities/condition/common/CastFromEverywhereSourceCondition.java index 1f6882a8b5dc..35e079ab84d6 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/CastFromEverywhereSourceCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/CastFromEverywhereSourceCondition.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.condition.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/condition/common/ControlsPermanentGreatestCMCCondition.java b/Mage/src/main/java/mage/abilities/condition/common/ControlsPermanentGreatestCMCCondition.java index 87b79fe88bc4..291ec51fa2cd 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/ControlsPermanentGreatestCMCCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/ControlsPermanentGreatestCMCCondition.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.condition.common; import java.util.HashSet; @@ -39,7 +34,7 @@ public boolean apply(Game game, Ability source) { List permanents = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game); for (Permanent permanent : permanents) { - int cmc = permanent.getManaCost().convertedManaCost(); + int cmc = permanent.getManaCost().manaValue(); if (maxCMC == null || cmc > maxCMC) { maxCMC = cmc; controllers.clear(); @@ -53,7 +48,7 @@ public boolean apply(Game game, Ability source) { @Override public String toString() { - return "you control the " + filter.getMessage() + " with the highest converted mana cost or tied for the highest converted mana cost"; + return "you control the " + filter.getMessage() + " with the highest mana value or tied for the highest mana value"; } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/CreatureCountCondition.java b/Mage/src/main/java/mage/abilities/condition/common/CreatureCountCondition.java index 6cb7afd8e2cf..305c0775a5b2 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/CreatureCountCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/CreatureCountCondition.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.condition.common; import java.util.UUID; diff --git a/Mage/src/main/java/mage/abilities/condition/common/DidNotAttackThisTurnEnchantedCondition.java b/Mage/src/main/java/mage/abilities/condition/common/DidNotAttackThisTurnEnchantedCondition.java index 178a5a16600f..80fc43a45275 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/DidNotAttackThisTurnEnchantedCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/DidNotAttackThisTurnEnchantedCondition.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.condition.common; import mage.MageObjectReference; diff --git a/Mage/src/main/java/mage/abilities/condition/common/EnchantedCreatureColorCondition.java b/Mage/src/main/java/mage/abilities/condition/common/EnchantedCreatureColorCondition.java index 718dcbcd38ac..12f62254b4b7 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/EnchantedCreatureColorCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/EnchantedCreatureColorCondition.java @@ -30,10 +30,8 @@ public boolean apply(Game game, Ability source) { Permanent enchantment = game.getPermanent(source.getSourceId()); if (enchantment != null) { Permanent creature = game.getPermanent(enchantment.getAttachedTo()); - if (creature != null) { - if(filter.match(creature, source.getSourceId(), enchantment.getControllerId(), game)){ - return true; - } + if(filter.match(creature, source.getSourceId(), enchantment.getControllerId(), game)){ + return true; } } return false; diff --git a/Mage/src/main/java/mage/abilities/condition/common/EnchantedCreatureSubtypeCondition.java b/Mage/src/main/java/mage/abilities/condition/common/EnchantedCreatureSubtypeCondition.java index 434dcc83597e..5aaf26241ab1 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/EnchantedCreatureSubtypeCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/EnchantedCreatureSubtypeCondition.java @@ -26,11 +26,10 @@ public boolean apply(Game game, Ability source) { Permanent enchantment = game.getPermanent(source.getSourceId()); if (enchantment != null) { Permanent creature = game.getPermanent(enchantment.getAttachedTo()); - if (creature != null) { - if (filter.match(creature, source.getSourceId(), enchantment.getControllerId(), game)) { - return true; - } + if (filter.match(creature, source.getSourceId(), enchantment.getControllerId(), game)) { + return true; } + } return false; } diff --git a/Mage/src/main/java/mage/abilities/condition/common/IsPhaseCondition.java b/Mage/src/main/java/mage/abilities/condition/common/IsPhaseCondition.java index 95a7c514cbbe..0e00e9b6b8c5 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/IsPhaseCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/IsPhaseCondition.java @@ -15,19 +15,29 @@ public class IsPhaseCondition implements Condition { protected TurnPhase turnPhase; + protected boolean yourTurn; public IsPhaseCondition(TurnPhase turnPhase) { + this(turnPhase, false); + } + + public IsPhaseCondition(TurnPhase turnPhase, boolean yourTurn) { this.turnPhase = turnPhase; + this.yourTurn = yourTurn; } @Override public boolean apply(Game game, Ability source) { - return turnPhase == game.getTurn().getPhaseType(); + return turnPhase == game.getTurn().getPhaseType() && (!yourTurn || game.getActivePlayerId().equals(source.getControllerId())); } @Override public String toString() { - return new StringBuilder("during ").append(turnPhase).toString().toLowerCase(Locale.ENGLISH); + return new StringBuilder("during ") + .append(yourTurn ? "your " : "") + .append(turnPhase) + .toString() + .toLowerCase(Locale.ENGLISH); } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/MeldCondition.java b/Mage/src/main/java/mage/abilities/condition/common/MeldCondition.java index 603d43cc69fb..a0a3f552d209 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/MeldCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/MeldCondition.java @@ -6,7 +6,7 @@ import mage.abilities.condition.Condition; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.NamePredicate; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage/src/main/java/mage/abilities/condition/common/MostCommonColorCondition.java b/Mage/src/main/java/mage/abilities/condition/common/MostCommonColorCondition.java index d4a82233992b..1b3c81573870 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/MostCommonColorCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/MostCommonColorCondition.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.condition.common; import mage.ObjectColor; diff --git a/Mage/src/main/java/mage/abilities/condition/common/OnOpponentsTurnCondition.java b/Mage/src/main/java/mage/abilities/condition/common/OnOpponentsTurnCondition.java index 0c5704b3f640..7e1f5a82bea0 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/OnOpponentsTurnCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/OnOpponentsTurnCondition.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.condition.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/condition/common/OneOpponentCondition.java b/Mage/src/main/java/mage/abilities/condition/common/OneOpponentCondition.java index 36b835488583..21aac3c45fe2 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/OneOpponentCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/OneOpponentCondition.java @@ -1,15 +1,12 @@ - package mage.abilities.condition.common; import mage.abilities.Ability; import mage.abilities.condition.Condition; import mage.game.Game; -import mage.players.Player; -import java.util.UUID; +import java.util.Objects; /** - * * @author TheElk801 */ public enum OneOpponentCondition implements Condition { @@ -18,20 +15,11 @@ public enum OneOpponentCondition implements Condition { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - int opponentCount = 0; - if (controller != null) { - for (UUID uuid : game.getOpponents(controller.getId())) { - Player opponent = game.getPlayer(uuid); - if (opponent != null) { - opponentCount++; - if (opponentCount > 1) { - return false; - } - } - } - } - return true; + return game.getOpponents(source.getControllerId()) + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .count() <= 1; } @Override diff --git a/Mage/src/main/java/mage/abilities/condition/common/RenownedSourceCondition.java b/Mage/src/main/java/mage/abilities/condition/common/RenownedSourceCondition.java index 47dc9f3a226a..3b49012cfefd 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/RenownedSourceCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/RenownedSourceCondition.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.condition.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/condition/common/RevealedOrControlledDragonCondition.java b/Mage/src/main/java/mage/abilities/condition/common/RevealedOrControlledDragonCondition.java new file mode 100644 index 000000000000..8658e5897bf2 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/condition/common/RevealedOrControlledDragonCondition.java @@ -0,0 +1,30 @@ +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.abilities.costs.common.RevealDragonFromHandCost; +import mage.constants.AbilityType; +import mage.game.Game; +import mage.watchers.common.DragonOnTheBattlefieldWhileSpellWasCastWatcher; + +/** + * @author TheElk801 + */ +public enum RevealedOrControlledDragonCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + if (source.getAbilityType() == AbilityType.SPELL) { + return source + .getCosts() + .stream() + .filter(RevealDragonFromHandCost.class::isInstance) + .map(RevealDragonFromHandCost.class::cast) + .anyMatch(RevealDragonFromHandCost::isRevealedOrControlled); + } + DragonOnTheBattlefieldWhileSpellWasCastWatcher watcher + = game.getState().getWatcher(DragonOnTheBattlefieldWhileSpellWasCastWatcher.class); + return watcher != null && watcher.checkCondition(source, game); + } +} diff --git a/Mage/src/main/java/mage/abilities/condition/common/SourceDealtDamageCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceDealtDamageCondition.java index 7a9f111d2296..2f4e4423e244 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/SourceDealtDamageCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/SourceDealtDamageCondition.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.condition.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/condition/common/SourceHasCounterCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceHasCounterCondition.java index abce22fd54b9..61ee7102a074 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/SourceHasCounterCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/SourceHasCounterCondition.java @@ -7,9 +7,9 @@ import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.util.CardUtil; /** - * * @author nantuko */ public class SourceHasCounterCondition implements Condition { @@ -68,14 +68,13 @@ public boolean apply(Game game, Ability source) { @Override public String toString() { - if (from != -1) { //range compare + if (from != -1) { if (to == Integer.MAX_VALUE) { return "{this} has equal to or more than " + from + " " + this.counterType.toString() + " counters"; } return "{this} has between " + from + " and " + to + " " + this.counterType.toString() + " counters"; - } else // single compare (lte) - { - return "{this} has equal or more than " + amount + " " + this.counterType.toString() + " counters"; + } else { + return "{this} has " + CardUtil.numberToText(amount) + " or more " + this.counterType.toString() + " counters on it"; } } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/SourceMatchesFilterCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceMatchesFilterCondition.java index ef760a3619b3..cfacae4ec2c4 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/SourceMatchesFilterCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/SourceMatchesFilterCondition.java @@ -29,11 +29,10 @@ public SourceMatchesFilterCondition(String text, FilterPermanent filter) { @Override public boolean apply(Game game, Ability source) { Permanent permanent = game.getBattlefield().getPermanent(source.getSourceId()); - if (permanent != null) { - if (FILTER.match(permanent, permanent.getId(), permanent.getControllerId(), game)) { - return true; - } + if (FILTER.match(permanent, permanent.getId(), permanent.getControllerId(), game)) { + return true; } + return false; } diff --git a/Mage/src/main/java/mage/abilities/condition/common/SourceRemainsInZoneCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceRemainsInZoneCondition.java index 67fe507a4a4c..2fb1c3c6429a 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/SourceRemainsInZoneCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/SourceRemainsInZoneCondition.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.condition.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/condition/common/SourceTargetsPermanentCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceTargetsPermanentCondition.java index b4e3697752ec..545518e6efad 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/SourceTargetsPermanentCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/SourceTargetsPermanentCondition.java @@ -30,7 +30,7 @@ public boolean apply(Game game, Ability source) { Iterator targets = sourceSpell.getStackAbility().getTargets().iterator(); while (targets.hasNext()) { Permanent permanent = game.getPermanentOrLKIBattlefield(targets.next().getFirstTarget()); - if (permanent != null && filter.match(permanent, source.getSourceId(), source.getControllerId(), game)) { + if (filter.match(permanent, source.getSourceId(), source.getControllerId(), game)) { return true; } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/SpellMasteryCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SpellMasteryCondition.java index fff9abbf65f7..b165160a8dd0 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/SpellMasteryCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/SpellMasteryCondition.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.condition.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/condition/common/TargetHasSuperTypeCondition.java b/Mage/src/main/java/mage/abilities/condition/common/TargetHasSuperTypeCondition.java index af12e5cf83e7..b9a9bc16f53d 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/TargetHasSuperTypeCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/TargetHasSuperTypeCondition.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.condition.common; import mage.MageObject; diff --git a/Mage/src/main/java/mage/abilities/condition/common/YouGainedLifeCondition.java b/Mage/src/main/java/mage/abilities/condition/common/YouGainedLifeCondition.java index 48c0ae17a6bc..6ebf31b6aa32 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/YouGainedLifeCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/YouGainedLifeCondition.java @@ -17,16 +17,12 @@ public YouGainedLifeCondition(ComparisonType type, int value) { @Override protected int getInputValue(Game game, Ability source) { - int gainedLife = 0; PlayerGainedLifeWatcher watcher = game.getState().getWatcher(PlayerGainedLifeWatcher.class); - if (watcher != null) { - gainedLife = watcher.getLifeGained(source.getControllerId()); - } - return gainedLife; + return watcher == null ? 0 : watcher.getLifeGained(source.getControllerId()); } @Override public String toString() { - return String.format("if you gained %s or more life this turn", value + 1); + return "if you gained " + (value == 0 ? "" : (value + 1) + " or more ") + "life this turn"; } } diff --git a/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java b/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java index 99d39ab90a49..53d22aa39181 100644 --- a/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java +++ b/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java @@ -1,4 +1,3 @@ - package mage.abilities.costs; import mage.abilities.Ability; @@ -16,13 +15,15 @@ import mage.util.CardUtil; import java.util.Iterator; -import mage.MageObject; +import java.util.UUID; /** * @author LevelX2 */ public class AlternativeCostSourceAbility extends StaticAbility implements AlternativeSourceCosts { + private static final String ALTERNATIVE_COST_ACTIVATION_KEY = "AlternativeCostActivated"; + private Costs alternateCosts = new CostsImpl<>(); protected Condition condition; protected String rule; @@ -159,6 +160,9 @@ public boolean askToActivateAlternativeCosts(Ability ability, Game game) { } } } + + // save activated status + game.getState().setValue(getActivatedKey(ability), Boolean.TRUE); } else { return false; } @@ -169,6 +173,38 @@ public boolean askToActivateAlternativeCosts(Ability ability, Game game) { return isActivated(ability, game); } + private String getActivatedKey(Ability source) { + return getActivatedKey(this.getOriginalId(), source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + } + + private static String getActivatedKey(UUID alternativeCostOriginalId, UUID sourceId, int sourceZCC) { + // can't use sourceId cause copied cards are different... + // TODO: enable sourceId after copy card fix (it must copy cards with all related game state values) + return ALTERNATIVE_COST_ACTIVATION_KEY + "_" + alternativeCostOriginalId + "_" /*+ sourceId + "_"*/ + sourceZCC; + } + + /** + * Search activated status of alternative cost. + *

+ * If you need it on resolve then use current ZCC (on stack) + * If you need it on battlefield then use previous ZCC (-1) + * + * @param game + * @param source + * @param alternativeCostOriginalId you must save originalId on card's creation + * @param searchPrevZCC true on battlefield, false on stack + * @return + */ + public static boolean getActivatedStatus(Game game, Ability source, UUID alternativeCostOriginalId, boolean searchPrevZCC) { + String key = getActivatedKey( + alternativeCostOriginalId, + source.getSourceId(), + source.getSourceObjectZoneChangeCounter() + (searchPrevZCC ? -1 : 0) + ); + Boolean status = (Boolean) game.getState().getValue(key); + return status != null && status; + } + @Override public boolean isActivated(Ability source, Game game) { Costs alternativeCostsToCheck; diff --git a/Mage/src/main/java/mage/abilities/costs/common/ControlPermanentCost.java b/Mage/src/main/java/mage/abilities/costs/common/ControlPermanentCost.java index c557d3b7d839..ad5d3f50838f 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/ControlPermanentCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/ControlPermanentCost.java @@ -13,7 +13,7 @@ public class ControlPermanentCost extends CostImpl { public ControlPermanentCost(FilterControlledPermanent filter) { this.filter = filter.copy(); - this.text = "Activate this ability only if you control " + filter.getMessage(); + this.text = "Activate only if you control " + filter.getMessage(); } public ControlPermanentCost(final ControlPermanentCost cost) { diff --git a/Mage/src/main/java/mage/abilities/costs/common/ExileFromGraveCost.java b/Mage/src/main/java/mage/abilities/costs/common/ExileFromGraveCost.java index af8299e18eb0..dacc8bcbde1c 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/ExileFromGraveCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/ExileFromGraveCost.java @@ -1,9 +1,6 @@ package mage.abilities.costs.common; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.costs.Cost; import mage.abilities.costs.CostImpl; @@ -19,8 +16,11 @@ import mage.target.targetpointer.FixedTargets; import mage.util.CardUtil; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + /** - * * @author nantuko */ public class ExileFromGraveCost extends CostImpl { @@ -38,9 +38,7 @@ public ExileFromGraveCost(TargetCardInYourGraveyard target) { + CardUtil.numberToText(target.getMaxNumberOfTargets())) + ' ' + target.getTargetName(); } else { - this.text = "exile " - + (target.getTargetName().startsWith("card ") ? "a ":"") - + target.getTargetName(); + this.text = "exile " + CardUtil.addArticle(target.getTargetName().replace("cards ", "card ")); } if (!this.text.endsWith(" from your graveyard")) { this.text = this.text + " from your graveyard"; @@ -53,6 +51,12 @@ public ExileFromGraveCost(TargetCardInYourGraveyard target, String text) { this.text = text; } + public ExileFromGraveCost(TargetCardInASingleGraveyard target, String text) { + target.setNotTarget(true); + this.addTarget(target); + this.text = text; + } + public ExileFromGraveCost(TargetCardInASingleGraveyard target) { target.setNotTarget(true); this.addTarget(target); diff --git a/Mage/src/main/java/mage/abilities/costs/common/ExileFromHandCost.java b/Mage/src/main/java/mage/abilities/costs/common/ExileFromHandCost.java index e7e5d58bed38..b937c39c19f0 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/ExileFromHandCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/ExileFromHandCost.java @@ -58,7 +58,7 @@ public boolean pay(Ability ability, Game game, Ability source, UUID controllerId if (card == null) { return false; } - cmc += card.getConvertedManaCost(); + cmc += card.getManaValue(); this.cards.add(card); } Cards cardsToExile = new CardsImpl(); diff --git a/Mage/src/main/java/mage/abilities/costs/common/ExileFromStackCost.java b/Mage/src/main/java/mage/abilities/costs/common/ExileFromStackCost.java deleted file mode 100644 index 62fe1191fcff..000000000000 --- a/Mage/src/main/java/mage/abilities/costs/common/ExileFromStackCost.java +++ /dev/null @@ -1,63 +0,0 @@ - -package mage.abilities.costs.common; - -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; -import mage.constants.Outcome; -import mage.game.Game; -import mage.game.stack.Spell; -import mage.players.Player; -import mage.target.TargetSpell; - -/** - * - * @author LevelX2 - */ -public class ExileFromStackCost extends CostImpl { - - public ExileFromStackCost(TargetSpell target) { - this.addTarget(target); - this.text = "Exile " + target.getTargetName(); - } - - public ExileFromStackCost(ExileFromStackCost cost) { - super(cost); - } - - @Override - public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - if (targets.choose(Outcome.Exile, controllerId, source.getSourceId(), game)) { - Player player = game.getPlayer(controllerId); - for (UUID targetId : targets.get(0).getTargets()) { - Spell spellToExile = game.getStack().getSpell(targetId); - if (spellToExile == null) { - return false; - } - String spellName = spellToExile.getName(); - if (spellToExile.isCopy()) { - game.getStack().remove(spellToExile, game); - } else { - spellToExile.moveToExile(null, "", ability, game); - } - paid = true; - if (!game.isSimulation()) { - game.informPlayers(player.getLogName() + " exiles " + spellName + " (as costs)"); - } - } - } - return paid; - } - - @Override - public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { - return targets.canChoose(source.getSourceId(), controllerId, game); - } - - @Override - public ExileFromStackCost copy() { - return new ExileFromStackCost(this); - } - -} diff --git a/Mage/src/main/java/mage/abilities/costs/common/ExileOpponentsCardFromExileToGraveyardCost.java b/Mage/src/main/java/mage/abilities/costs/common/ExileOpponentsCardFromExileToGraveyardCost.java index dba83ddaa7a8..6506c3b167a8 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/ExileOpponentsCardFromExileToGraveyardCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/ExileOpponentsCardFromExileToGraveyardCost.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.costs.common; import java.util.UUID; diff --git a/Mage/src/main/java/mage/abilities/costs/common/ExileSourceFromHandCost.java b/Mage/src/main/java/mage/abilities/costs/common/ExileSourceFromHandCost.java new file mode 100644 index 000000000000..8d6ba5bc718f --- /dev/null +++ b/Mage/src/main/java/mage/abilities/costs/common/ExileSourceFromHandCost.java @@ -0,0 +1,46 @@ +package mage.abilities.costs.common; + +import mage.abilities.Ability; +import mage.abilities.costs.Cost; +import mage.abilities.costs.CostImpl; +import mage.cards.Card; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public class ExileSourceFromHandCost extends CostImpl { + + public ExileSourceFromHandCost() { + this.text = "exile {this} from your hand"; + } + + private ExileSourceFromHandCost(ExileSourceFromHandCost cost) { + super(cost); + } + + @Override + public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { + Player player = game.getPlayer(controllerId); + Card card = game.getCard(source.getSourceId()); + if (player != null && player.getHand().contains(source.getSourceId()) && card != null) { + paid = player.moveCards(card, Zone.EXILED, source, game); + } + return paid; + } + + @Override + public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { + Player player = game.getPlayer(controllerId); + return player != null && player.getHand().contains(source.getSourceId()); + } + + @Override + public ExileSourceFromHandCost copy() { + return new ExileSourceFromHandCost(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/costs/common/ExileTopCardOfGraveyardCost.java b/Mage/src/main/java/mage/abilities/costs/common/ExileTopCardOfGraveyardCost.java index 4a5f2de1e638..9784495831c9 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/ExileTopCardOfGraveyardCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/ExileTopCardOfGraveyardCost.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.costs.common; import java.util.UUID; diff --git a/Mage/src/main/java/mage/abilities/costs/common/ExileTopCreatureCardOfGraveyardCost.java b/Mage/src/main/java/mage/abilities/costs/common/ExileTopCreatureCardOfGraveyardCost.java index 3f9fb4979835..62d12aeaadc7 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/ExileTopCreatureCardOfGraveyardCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/ExileTopCreatureCardOfGraveyardCost.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.costs.common; import java.util.UUID; diff --git a/Mage/src/main/java/mage/abilities/costs/common/GainLifeOpponentCost.java b/Mage/src/main/java/mage/abilities/costs/common/GainLifeOpponentCost.java index f41f3bd6698c..06266c337d10 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/GainLifeOpponentCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/GainLifeOpponentCost.java @@ -1,9 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - package mage.abilities.costs.common; import java.util.UUID; diff --git a/Mage/src/main/java/mage/abilities/costs/common/PutCardFromHandOnTopOfLibraryCost.java b/Mage/src/main/java/mage/abilities/costs/common/PutCardFromHandOnTopOfLibraryCost.java index 1b2ff3ae7793..fdb2d4cb48f2 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/PutCardFromHandOnTopOfLibraryCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/PutCardFromHandOnTopOfLibraryCost.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.costs.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/costs/common/RevealDragonFromHandCost.java b/Mage/src/main/java/mage/abilities/costs/common/RevealDragonFromHandCost.java new file mode 100644 index 000000000000..26c0112f3cef --- /dev/null +++ b/Mage/src/main/java/mage/abilities/costs/common/RevealDragonFromHandCost.java @@ -0,0 +1,58 @@ +package mage.abilities.costs.common; + +import mage.abilities.Ability; +import mage.abilities.costs.Cost; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.target.common.TargetCardInHand; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public class RevealDragonFromHandCost extends RevealTargetFromHandCost { + + private static final FilterCard filter = new FilterCard("a Dragon card from your hand"); + private static final FilterPermanent filter2 = new FilterControlledPermanent(SubType.DRAGON); + + static { + filter.add(SubType.DRAGON.getPredicate()); + } + + private boolean revealedOrControlled = false; + + public RevealDragonFromHandCost() { + super(new TargetCardInHand(0, 1, filter)); + } + + private RevealDragonFromHandCost(final RevealDragonFromHandCost cost) { + super(cost); + this.revealedOrControlled = cost.revealedOrControlled; + } + + @Override + public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { + return true; + } + + @Override + public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { + super.pay(ability, game, source, controllerId, noMana, costToPay); + revealedOrControlled = numberCardsRevealed > 0 + || game.getBattlefield().count(filter2, source.getSourceId(), controllerId, game) > 0; + return paid = true; + } + + @Override + public RevealDragonFromHandCost copy() { + return new RevealDragonFromHandCost(this); + } + + public boolean isRevealedOrControlled() { + return revealedOrControlled; + } +} diff --git a/Mage/src/main/java/mage/abilities/costs/common/RevealSecretOpponentCost.java b/Mage/src/main/java/mage/abilities/costs/common/RevealSecretOpponentCost.java index 6b9e09291246..aa4ae4bdbccd 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/RevealSecretOpponentCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/RevealSecretOpponentCost.java @@ -12,57 +12,51 @@ import java.util.UUID; /** - * * @author LevelX2 * @author credman0 */ public class RevealSecretOpponentCost extends CostImpl { - public RevealSecretOpponentCost() { - this.text = "Reveal the player you chose"; + public RevealSecretOpponentCost() { + this.text = "Reveal the player you chose"; + } + + public RevealSecretOpponentCost(final RevealSecretOpponentCost cost) { + super(cost); + } + + @Override + public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { + return controllerId != null + && controllerId.equals(ChooseSecretOpponentEffect.getSecretOwner(source, game)) + && ChooseSecretOpponentEffect.getChosenPlayer(source, game) != null; + } + + @Override + public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { + if (controllerId == null || !controllerId.equals(ChooseSecretOpponentEffect.getSecretOwner(source, game))) { + return false; } - - public RevealSecretOpponentCost(final RevealSecretOpponentCost cost) { - super(cost); - } - - @Override - public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { - UUID playerThatChoseId = (UUID) game.getState().getValue(source.getSourceId() + ChooseSecretOpponentEffect.SECRET_OWNER); - if (playerThatChoseId == null || !playerThatChoseId.equals(controllerId)) { - return false; - } - UUID opponentId = (UUID) game.getState().getValue(source.getSourceId() + ChooseSecretOpponentEffect.SECRET_OPPONENT); - return opponentId != null; - } - - @Override - public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - UUID playerThatChoseId = (UUID) game.getState().getValue(source.getSourceId() + ChooseSecretOpponentEffect.SECRET_OWNER); - if (playerThatChoseId == null || !playerThatChoseId.equals(controllerId)) { - return false; - } - UUID opponentId = (UUID) game.getState().getValue(source.getSourceId() + ChooseSecretOpponentEffect.SECRET_OPPONENT); - if (opponentId != null) { - game.getState().setValue(source.getSourceId() + ChooseSecretOpponentEffect.SECRET_OWNER, null); // because only once, the vale is set to null - Player controller = game.getPlayer(controllerId); - Player opponent = game.getPlayer(opponentId); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (controller != null && opponent != null && sourceObject != null) { - if (sourceObject instanceof Permanent) { - ((Permanent) sourceObject).addInfo(ChooseSecretOpponentEffect.SECRET_OPPONENT, null, game); - } - game.informPlayers(sourceObject.getLogName() + ": " + controller.getLogName() + " reveals the secretly chosen opponent " + opponent.getLogName()); - } - paid = true; - } + UUID opponentId = ChooseSecretOpponentEffect.getChosenPlayer(source, game); + if (opponentId == null) { return paid; } - - @Override - public RevealSecretOpponentCost copy() { - return new RevealSecretOpponentCost(this); + ChooseSecretOpponentEffect.setSecretOwner(null, source, game); // because only once, the value is set to null + Player controller = game.getPlayer(controllerId); + Player opponent = game.getPlayer(opponentId); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (controller != null && opponent != null && sourceObject != null) { + if (sourceObject instanceof Permanent) { + ((Permanent) sourceObject).addInfo(ChooseSecretOpponentEffect.SECRET_OPPONENT, null, game); + } + game.informPlayers(sourceObject.getLogName() + ": " + controller.getLogName() + " reveals the secretly chosen opponent " + opponent.getLogName()); } - - + paid = true; + return paid; + } + + @Override + public RevealSecretOpponentCost copy() { + return new RevealSecretOpponentCost(this); + } } diff --git a/Mage/src/main/java/mage/abilities/costs/common/RevealTargetFromHandCost.java b/Mage/src/main/java/mage/abilities/costs/common/RevealTargetFromHandCost.java index 1ab24181ba54..12c206a2f4ba 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/RevealTargetFromHandCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/RevealTargetFromHandCost.java @@ -21,7 +21,7 @@ */ public class RevealTargetFromHandCost extends CostImpl { - public int convertedManaCosts = 0; + public int manaValues = 0; protected int numberCardsRevealed = 0; protected List revealedCards; @@ -33,7 +33,7 @@ public RevealTargetFromHandCost(TargetCardInHand target) { public RevealTargetFromHandCost(final RevealTargetFromHandCost cost) { super(cost); - this.convertedManaCosts = cost.convertedManaCosts; + this.manaValues = cost.manaValues; this.numberCardsRevealed = cost.numberCardsRevealed; this.revealedCards = new ArrayList<>(cost.revealedCards); } @@ -41,14 +41,14 @@ public RevealTargetFromHandCost(final RevealTargetFromHandCost cost) { @Override public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { if (targets.choose(Outcome.Benefit, controllerId, source.getSourceId(), game)) { - convertedManaCosts = 0; + manaValues = 0; numberCardsRevealed = 0; Player player = game.getPlayer(controllerId); Cards cards = new CardsImpl(); for (UUID targetId : targets.get(0).getTargets()) { Card card = player.getHand().get(targetId, game); if (card != null) { - convertedManaCosts += card.getConvertedManaCost(); + manaValues += card.getManaValue(); numberCardsRevealed++; cards.add(card); revealedCards.add(card); @@ -68,7 +68,7 @@ public boolean pay(Ability ability, Game game, Ability source, UUID controllerId } public int getConvertedCosts() { - return convertedManaCosts; + return manaValues; } public int getNumberRevealedCards() { diff --git a/Mage/src/main/java/mage/abilities/costs/common/SacrificeTargetCost.java b/Mage/src/main/java/mage/abilities/costs/common/SacrificeTargetCost.java index c672f40ab38c..fdd4b8cbe935 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/SacrificeTargetCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/SacrificeTargetCost.java @@ -27,15 +27,7 @@ public SacrificeTargetCost(TargetControlledPermanent target) { this.addTarget(target); target.setNotTarget(true); // sacrifice is never targeted target.setRequired(false); // can be canceled - this.text = "sacrifice " - + ((target.getNumberOfTargets() != 1 - || (target.getTargetName().startsWith("an") - || target.getTargetName().startsWith("a "))) - ? (target.getMinNumberOfTargets() == target.getMaxNumberOfTargets() - && target.getMinNumberOfTargets() > 1 - ? CardUtil.numberToText(target.getNumberOfTargets()) + " " : "") - : (target.getTargetName().startsWith("artifact") ? "an " : "a ")) - + target.getTargetName(); + this.text = "sacrifice " + makeText(target); target.setTargetName(target.getTargetName() + " (to sacrifice)"); } @@ -106,4 +98,16 @@ public SacrificeTargetCost copy() { public List getPermanents() { return permanents; } + + private static final String makeText(TargetControlledPermanent target) { + if (target.getMinNumberOfTargets() != target.getMaxNumberOfTargets()) { + return target.getTargetName(); + } + if (target.getNumberOfTargets() == 1 + || target.getTargetName().startsWith("a ") + || target.getTargetName().startsWith("an ")) { + return CardUtil.addArticle(target.getTargetName()); + } + return CardUtil.numberToText(target.getNumberOfTargets()) + ' ' + target.getTargetName(); + } } diff --git a/Mage/src/main/java/mage/abilities/costs/common/TapTargetCost.java b/Mage/src/main/java/mage/abilities/costs/common/TapTargetCost.java index dba40f067584..295e6b4b74b0 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/TapTargetCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/TapTargetCost.java @@ -24,11 +24,9 @@ public TapTargetCost(TargetControlledPermanent target) { this.target = target; this.target.setNotTarget(true); // costs are never targeted this.target.setRequired(false); // can be cancel by user - this.text - = new StringBuilder("tap ") - .append((target.getTargetName().startsWith("a ") || target.getTargetName().startsWith("an ") || target.getTargetName().startsWith("another")) - ? "" : CardUtil.numberToText(target.getMaxNumberOfTargets()) + ' ') - .append(target.getTargetName()).toString(); + this.text = "tap " + (target.getNumberOfTargets() > 1 + ? CardUtil.numberToText(target.getMaxNumberOfTargets()) + ' ' + target.getTargetName() + : CardUtil.addArticle(target.getTargetName())); } public TapTargetCost(final TapTargetCost cost) { diff --git a/Mage/src/main/java/mage/abilities/costs/mana/ColoredManaCost.java b/Mage/src/main/java/mage/abilities/costs/mana/ColoredManaCost.java index 9d4f8cdd5712..87319e0172a3 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/ColoredManaCost.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/ColoredManaCost.java @@ -24,7 +24,7 @@ public ColoredManaCost(ColoredManaCost manaCost) { } @Override - public int convertedManaCost() { + public int manaValue() { return 1; } diff --git a/Mage/src/main/java/mage/abilities/costs/mana/ColorlessManaCost.java b/Mage/src/main/java/mage/abilities/costs/mana/ColorlessManaCost.java index 038e6dc3f4cb..2715cb0702c2 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/ColorlessManaCost.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/ColorlessManaCost.java @@ -32,7 +32,7 @@ public void setMana(int mana) { } @Override - public int convertedManaCost() { + public int manaValue() { return mana; } diff --git a/Mage/src/main/java/mage/abilities/costs/mana/GenericManaCost.java b/Mage/src/main/java/mage/abilities/costs/mana/GenericManaCost.java index 53baaa9cfaf8..4310ab67c156 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/GenericManaCost.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/GenericManaCost.java @@ -30,7 +30,7 @@ public void setMana(int mana) { } @Override - public int convertedManaCost() { + public int manaValue() { return mana; } @@ -75,5 +75,4 @@ public GenericManaCost copy() { public boolean containsColor(ColoredManaSymbol coloredManaSymbol) { return false; } - } diff --git a/Mage/src/main/java/mage/abilities/costs/mana/HybridManaCost.java b/Mage/src/main/java/mage/abilities/costs/mana/HybridManaCost.java index 0db245fa8dbc..9c2193aa565b 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/HybridManaCost.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/HybridManaCost.java @@ -31,7 +31,7 @@ public HybridManaCost(HybridManaCost manaCost) { } @Override - public int convertedManaCost() { + public int manaValue() { return 1; } diff --git a/Mage/src/main/java/mage/abilities/costs/mana/ManaCost.java b/Mage/src/main/java/mage/abilities/costs/mana/ManaCost.java index 67067177823d..9f5a3ed02dbd 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/ManaCost.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/ManaCost.java @@ -13,7 +13,7 @@ public interface ManaCost extends Cost { - int convertedManaCost(); + int manaValue(); Mana getMana(); diff --git a/Mage/src/main/java/mage/abilities/costs/mana/ManaCostImpl.java b/Mage/src/main/java/mage/abilities/costs/mana/ManaCostImpl.java index 056480b9a6fc..284852989667 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/ManaCostImpl.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/ManaCostImpl.java @@ -284,4 +284,8 @@ protected void addColoredOption(ColoredManaSymbol symbol) { } } + @Override + public String toString() { + return getText(); + } } diff --git a/Mage/src/main/java/mage/abilities/costs/mana/ManaCosts.java b/Mage/src/main/java/mage/abilities/costs/mana/ManaCosts.java index 1a2030abf8fb..c1196269e3fd 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/ManaCosts.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/ManaCosts.java @@ -39,8 +39,6 @@ default void load(String mana) { */ void load(String mana, boolean extractMonoHybridGenericValue); - List getSymbols(); - boolean payOrRollback(Ability ability, Game game, Ability source, UUID payingPlayerId); @Override @@ -56,5 +54,4 @@ static ManaCosts removeVariableManaCost(ManaCosts m) { .collect(Collectors.toCollection(ManaCostsImpl::new)); } - } diff --git a/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java b/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java index b07eb4cf25c3..165fe43e8a41 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java @@ -63,10 +63,10 @@ public final boolean add(ManaCost cost) { } @Override - public int convertedManaCost() { + public int manaValue() { int total = 0; for (ManaCost cost : this) { - total += cost.convertedManaCost(); + total += cost.manaValue(); } return total; } @@ -194,11 +194,11 @@ private void handlePhyrexianManaCosts(Ability abilityToPay, Player payingPlayer, private void handleLikePhyrexianManaCosts(Player player, Ability source, Game game) { if (this.isEmpty()) { return; // nothing to be done without any mana costs. prevents NRE from occurring here - } + } FilterMana phyrexianColors = player.getPhyrexianColors(); if (player.getPhyrexianColors() != null) { Costs tempCosts = new CostsImpl<>(); - + Iterator manaCostIterator = this.iterator(); while (manaCostIterator.hasNext()) { ManaCost manaCost = manaCostIterator.next(); @@ -440,15 +440,16 @@ public void forceManaRollback(Game game, ManaPool pool) { @Override public final void load(String mana, boolean extractMonoHybridGenericValue) { this.clear(); - if (!extractMonoHybridGenericValue && mana != null && costsCache.containsKey(mana)) { + if (mana == null || mana.isEmpty()) { + return; + } + + if (!extractMonoHybridGenericValue && costsCache.containsKey(mana)) { ManaCosts savedCosts = costsCache.get(mana); for (ManaCost cost : savedCosts) { this.add(cost.copy()); } } else { - if (mana == null || mana.isEmpty()) { - return; - } String[] symbols = mana.split("^\\{|}\\{|}$"); int modifierForX = 0; for (String symbol : symbols) { @@ -463,15 +464,15 @@ public final void load(String mana, boolean extractMonoHybridGenericValue) { } else if (!symbol.equals("X")) { this.add(new ColoredManaCost(ColoredManaSymbol.lookup(symbol.charAt(0)))); } else // check X wasn't added before - if (modifierForX == 0) { - // count X occurence - for (String s : symbols) { - if (s.equals("X")) { - modifierForX++; + if (modifierForX == 0) { + // count X occurence + for (String s : symbols) { + if (s.equals("X")) { + modifierForX++; + } } - } - this.add(new VariableManaCost(modifierForX)); - } //TODO: handle multiple {X} and/or {Y} symbols + this.add(new VariableManaCost(modifierForX)); + } //TODO: handle multiple {X} and/or {Y} symbols } else if (Character.isDigit(symbol.charAt(0))) { MonoHybridManaCost cost; if (extractMonoHybridGenericValue) { @@ -504,15 +505,6 @@ private boolean isNumeric(String symbol) { } } - @Override - public List getSymbols() { - List symbols = new ArrayList<>(); - for (ManaCost cost : this) { - symbols.add(cost.getText()); - } - return symbols; - } - @Override public UUID getId() { return this.id; @@ -534,14 +526,7 @@ public String getText() { StringBuilder sbText = new StringBuilder(); for (ManaCost cost : this) { - if (cost instanceof GenericManaCost) { - sbText.append(cost.getText()); - } - } - for (ManaCost cost : this) { - if (!(cost instanceof GenericManaCost)) { - sbText.append(cost.getText()); - } + sbText.append(cost.getText()); } return sbText.toString(); } diff --git a/Mage/src/main/java/mage/abilities/costs/mana/MonoHybridManaCost.java b/Mage/src/main/java/mage/abilities/costs/mana/MonoHybridManaCost.java index 4ef23fa39639..ca60adc144f3 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/MonoHybridManaCost.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/MonoHybridManaCost.java @@ -35,7 +35,7 @@ public MonoHybridManaCost(MonoHybridManaCost manaCost) { } @Override - public int convertedManaCost() { + public int manaValue() { // from wiki: A card with monocolored hybrid mana symbols in its mana cost has a converted mana cost equal to // the highest possible cost it could be played for. Its converted mana cost never changes. return Math.max(manaGeneric, 1); diff --git a/Mage/src/main/java/mage/abilities/costs/mana/SnowManaCost.java b/Mage/src/main/java/mage/abilities/costs/mana/SnowManaCost.java index 3dca3c698aaf..7ba708f458d1 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/SnowManaCost.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/SnowManaCost.java @@ -28,7 +28,7 @@ public SnowManaCost(SnowManaCost manaCost) { } @Override - public int convertedManaCost() { + public int manaValue() { return 1; } diff --git a/Mage/src/main/java/mage/abilities/costs/mana/VariableManaCost.java b/Mage/src/main/java/mage/abilities/costs/mana/VariableManaCost.java index 8aa8749c363c..bd77f3c076ef 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/VariableManaCost.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/VariableManaCost.java @@ -51,7 +51,7 @@ public VariableManaCost(final VariableManaCost manaCost) { } @Override - public int convertedManaCost() { + public int manaValue() { return 0; } diff --git a/Mage/src/main/java/mage/abilities/decorator/ConditionalActivatedAbility.java b/Mage/src/main/java/mage/abilities/decorator/ConditionalActivatedAbility.java index 79173cd9f4c2..cf693adca51e 100644 --- a/Mage/src/main/java/mage/abilities/decorator/ConditionalActivatedAbility.java +++ b/Mage/src/main/java/mage/abilities/decorator/ConditionalActivatedAbility.java @@ -78,6 +78,6 @@ public String getRule() { || conditionText.startsWith("if")) { additionalText = ""; } - return super.getRule() + " Activate this ability only " + additionalText + condition.toString() + "."; + return super.getRule() + " Activate only " + additionalText + condition.toString() + "."; } } diff --git a/Mage/src/main/java/mage/abilities/decorator/ConditionalInterveningIfTriggeredAbility.java b/Mage/src/main/java/mage/abilities/decorator/ConditionalInterveningIfTriggeredAbility.java index 45959856c938..8bfcb3a40a46 100644 --- a/Mage/src/main/java/mage/abilities/decorator/ConditionalInterveningIfTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/decorator/ConditionalInterveningIfTriggeredAbility.java @@ -36,6 +36,9 @@ public class ConditionalInterveningIfTriggeredAbility extends TriggeredAbilityIm */ public ConditionalInterveningIfTriggeredAbility(TriggeredAbility ability, Condition condition, String text) { super(ability.getZone(), null); + if (ability.isLeavesTheBattlefieldTrigger()) { + this.setLeavesTheBattlefieldTrigger(true); + } this.ability = ability; this.condition = condition; this.abilityText = text; diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/LimitedDynamicValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/LimitedDynamicValue.java index d5a4ad4095f0..f036cb1fc20f 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/LimitedDynamicValue.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/LimitedDynamicValue.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.dynamicvalue; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttachedPermanentToughnessValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttachedPermanentToughnessValue.java index e12404a1ec7d..cfe130c50e1a 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttachedPermanentToughnessValue.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttachedPermanentToughnessValue.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.dynamicvalue.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttackingCreatureCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttackingCreatureCount.java index 5b19df74a5a8..315505335acc 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttackingCreatureCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttackingCreatureCount.java @@ -48,7 +48,7 @@ public int calculate(Game game, Ability sourceAbility, Effect effect) { for (UUID permId : combatGroup.getAttackers()) { if (filter != null) { Permanent attacker = game.getPermanent(permId); - if (attacker != null && filter.match(attacker, sourceAbility.getSourceId(), sourceAbility.getControllerId(), game)) { + if (filter.match(attacker, sourceAbility.getSourceId(), sourceAbility.getControllerId(), game)) { count++; } } else { diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/ColorAssignment.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/ColorAssignment.java index 40e012f3907e..b915b236d675 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/ColorAssignment.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/ColorAssignment.java @@ -4,21 +4,20 @@ import mage.cards.Card; import mage.game.Game; -import java.util.HashSet; +import java.util.Arrays; import java.util.Set; +import java.util.stream.Collectors; public class ColorAssignment extends RoleAssignment { - public ColorAssignment() { - super("W", "U", "B", "R", "G"); + public ColorAssignment(String... colors) { + super(colors); } @Override protected Set makeSet(Card card, Game game) { - Set strings = new HashSet<>(); - for (char c : card.getColor(game).toString().toCharArray()) { - strings.add("" + c); - } - return strings; + return Arrays.stream(card.getColor(game).toString().split("")) + .filter(attributes::contains) + .collect(Collectors.toSet()); } } diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/ColorsOfManaSpentToCastCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/ColorsOfManaSpentToCastCount.java index dc22bad5b116..30c54236a7e8 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/ColorsOfManaSpentToCastCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/ColorsOfManaSpentToCastCount.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.dynamicvalue.common; import java.io.ObjectStreamException; diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/CommanderPlaysCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CommanderPlaysCount.java deleted file mode 100644 index 65051b3f2bdb..000000000000 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/CommanderPlaysCount.java +++ /dev/null @@ -1,52 +0,0 @@ -package mage.abilities.dynamicvalue.common; - -import mage.abilities.Ability; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; -import mage.game.Game; -import mage.watchers.common.CommanderPlaysCountWatcher; - -/** - * @author JayDi85 - */ -public class CommanderPlaysCount implements DynamicValue { - - private Integer multiplier; - - public CommanderPlaysCount() { - this(1); - } - - public CommanderPlaysCount(Integer multiplier) { - this.multiplier = multiplier; - } - - public CommanderPlaysCount(final CommanderPlaysCount dynamicValue) { - this.multiplier = dynamicValue.multiplier; - } - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class); - int value = 0; - if (watcher != null) { - value = watcher.getPlaysCount(sourceAbility.getSourceId()); - } - return value * multiplier; - } - - @Override - public CommanderPlaysCount copy() { - return new CommanderPlaysCount(this); - } - - @Override - public String toString() { - return "X"; - } - - @Override - public String getMessage() { - return ""; - } -} diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/ControllerGotLifeCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/ControllerGotLifeCount.java index 2663f6a04053..091734c6ec78 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/ControllerGotLifeCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/ControllerGotLifeCount.java @@ -1,31 +1,24 @@ - package mage.abilities.dynamicvalue.common; -import java.io.ObjectStreamException; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.MageSingleton; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; import mage.game.Game; import mage.watchers.common.PlayerGainedLifeWatcher; +import java.util.UUID; + /** * Amount of life the controller got this turn. * * @author LevelX2 */ -public class ControllerGotLifeCount implements DynamicValue, MageSingleton { - - private static final ControllerGotLifeCount fINSTANCE = new ControllerGotLifeCount(); +public enum ControllerGotLifeCount implements DynamicValue { + instance; - private Object readResolve() throws ObjectStreamException { - return fINSTANCE; - } - - public static ControllerGotLifeCount getInstance() { - return fINSTANCE; - } + private static final Hint hint = new ValueHint("Life gained this turn", instance); @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { @@ -42,7 +35,7 @@ public int calculate(Game game, UUID controllerId) { @Override public ControllerGotLifeCount copy() { - return new ControllerGotLifeCount(); + return instance; } @Override @@ -54,4 +47,8 @@ public String toString() { public String getMessage() { return "the amount of life you've gained this turn"; } + + public static Hint getHint() { + return hint; + } } diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/DiscardCostCardConvertedMana.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/DiscardCostCardConvertedMana.java index 03e750bcc394..241451d8fe2f 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/DiscardCostCardConvertedMana.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/DiscardCostCardConvertedMana.java @@ -19,7 +19,7 @@ public int calculate(Game game, Ability sourceAbility, Effect effect) { for (Cost cost : sourceAbility.getCosts()) { if (cost instanceof DiscardTargetCost) { DiscardTargetCost discardCost = (DiscardTargetCost) cost; - return discardCost.getCards().stream().mapToInt(Card::getConvertedManaCost).sum(); + return discardCost.getCards().stream().mapToInt(Card::getManaValue).sum(); } } return 0; @@ -37,6 +37,6 @@ public String toString() { @Override public String getMessage() { - return "the discarded card's converted mana cost"; + return "the discarded card's mana value"; } } diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/ExileFromHandCostCardConvertedMana.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/ExileFromHandCostCardConvertedMana.java index 48bba8276624..087b567287c6 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/ExileFromHandCostCardConvertedMana.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/ExileFromHandCostCardConvertedMana.java @@ -25,7 +25,7 @@ public int calculate(Game game, Ability sourceAbility, Effect effect) { if (cost.isPaid() && cost instanceof ExileFromHandCost) { int xValue = 0; for (Card card : ((ExileFromHandCost) cost).getCards()) { - xValue += card.getConvertedManaCost(); + xValue += card.getManaValue(); } return xValue; } diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/HighestCMCOfPermanentValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/HighestCMCOfPermanentValue.java index 96a56a77df3a..338e4b9b51f3 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/HighestCMCOfPermanentValue.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/HighestCMCOfPermanentValue.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.dynamicvalue.common; import mage.abilities.Ability; @@ -41,8 +36,8 @@ public int calculate(Game game, Ability sourceAbility, Effect effect) { for (Permanent permanent : game.getBattlefield() .getActivePermanents(filter, sourceAbility.getControllerId(), sourceAbility.getSourceId(), game)) { if ((!onlyIfCanBeSacrificed || controller.canPaySacrificeCost(permanent, sourceAbility, sourceAbility.getControllerId(), game)) - && permanent.getConvertedManaCost() > value) { - value = permanent.getConvertedManaCost(); + && permanent.getManaValue() > value) { + value = permanent.getManaValue(); } } diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/HighestConvertedManaCostValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/HighestManaValueCount.java similarity index 72% rename from Mage/src/main/java/mage/abilities/dynamicvalue/common/HighestConvertedManaCostValue.java rename to Mage/src/main/java/mage/abilities/dynamicvalue/common/HighestManaValueCount.java index 6e99dfaf8f39..c5892210fc6e 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/HighestConvertedManaCostValue.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/HighestManaValueCount.java @@ -13,19 +13,19 @@ * * @author nigelzor */ -public class HighestConvertedManaCostValue implements DynamicValue { +public class HighestManaValueCount implements DynamicValue { private final FilterPermanent filter; - public HighestConvertedManaCostValue() { + public HighestManaValueCount() { this(StaticFilters.FILTER_PERMANENTS); } - public HighestConvertedManaCostValue(FilterPermanent filter) { + public HighestManaValueCount(FilterPermanent filter) { this.filter = filter; } - public HighestConvertedManaCostValue(final HighestConvertedManaCostValue dynamicValue){ + public HighestManaValueCount(final HighestManaValueCount dynamicValue){ super(); this.filter = dynamicValue.filter.copy(); } @@ -38,7 +38,7 @@ public int calculate(Game game, Ability sourceAbility, Effect effect) { } int highCMC = 0; for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, controller.getId(), game)) { - int cmc = permanent.getConvertedManaCost(); + int cmc = permanent.getManaValue(); highCMC = Math.max(highCMC, cmc); } return highCMC; @@ -46,12 +46,12 @@ public int calculate(Game game, Ability sourceAbility, Effect effect) { @Override public DynamicValue copy() { - return new HighestConvertedManaCostValue(this); + return new HighestManaValueCount(this); } @Override public String getMessage() { - return "the highest converted mana cost among permanents you control"; + return "the highest mana value among permanents you control"; } @Override diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/LandsYouControlCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/LandsYouControlCount.java new file mode 100644 index 000000000000..f2439c2c4a33 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/LandsYouControlCount.java @@ -0,0 +1,37 @@ +package mage.abilities.dynamicvalue.common; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.filter.StaticFilters; +import mage.game.Game; + +/** + * @author TheElk801 + */ +public enum LandsYouControlCount implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return game.getBattlefield().count( + StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, + sourceAbility.getSourceId(), sourceAbility.getControllerId(), game + ); + } + + @Override + public LandsYouControlCount copy() { + return instance; + } + + @Override + public String toString() { + return "X"; + } + + @Override + public String getMessage() { + return "lands you control"; + } +} diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/ManaSpentToCastCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/ManaSpentToCastCount.java index 9ca01238b512..7bf9db05fa09 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/ManaSpentToCastCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/ManaSpentToCastCount.java @@ -26,7 +26,7 @@ public int calculate(Game game, Ability sourceAbility, Effect effect) { } if (spell != null) { // NOT the cmc of the spell on the stack - return spell.getSpellAbility().getManaCostsToPay().convertedManaCost(); + return spell.getSpellAbility().getManaCostsToPay().manaValue(); } return 0; } diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/ManacostVariableValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/ManacostVariableValue.java index bade65f54466..fa7d812835f7 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/ManacostVariableValue.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/ManacostVariableValue.java @@ -3,14 +3,20 @@ import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; +import mage.constants.AbilityType; import mage.game.Game; +import mage.watchers.common.ManaSpentToCastWatcher; public enum ManacostVariableValue implements DynamicValue { instance; @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { - return sourceAbility.getManaCostsToPay().getX(); + if (sourceAbility.getAbilityType() == AbilityType.SPELL) { + return sourceAbility.getManaCostsToPay().getX(); + } + ManaSpentToCastWatcher watcher = game.getState().getWatcher(ManaSpentToCastWatcher.class, sourceAbility.getSourceId()); + return watcher != null ? watcher.getAndResetLastXValue() : sourceAbility.getManaCostsToPay().getX(); } @Override diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/MorphManacostVariableValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/MorphManacostVariableValue.java index bc137d87e22f..3331bde77d7e 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/MorphManacostVariableValue.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/MorphManacostVariableValue.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.dynamicvalue.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/NumericSetToEffectValues.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/NumericSetToEffectValues.java index 2248712cc505..876a4f0d7cab 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/NumericSetToEffectValues.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/NumericSetToEffectValues.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.dynamicvalue.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/OpponentsCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/OpponentsCount.java index d8eee3f62765..d66a296b8656 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/OpponentsCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/OpponentsCount.java @@ -5,6 +5,8 @@ import mage.abilities.effects.Effect; import mage.game.Game; +import java.util.Objects; + /** * @author JayDi85 */ @@ -13,7 +15,12 @@ public enum OpponentsCount implements DynamicValue { @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { - return game.getOpponents(sourceAbility.getControllerId()).size(); + return game.getOpponents(sourceAbility.getControllerId()) + .stream() + .map(game::getPlayer) + .map(Objects::nonNull) + .mapToInt(x -> x ? 1 : 0) + .sum(); } @Override @@ -30,4 +37,4 @@ public String getMessage() { public String toString() { return "1"; } -} \ No newline at end of file +} diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/RemovedCountersForCostValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/RemovedCountersForCostValue.java index 2b8620a5611b..815f664b437b 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/RemovedCountersForCostValue.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/RemovedCountersForCostValue.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.dynamicvalue.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/RevealTargetFromHandCostCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/RevealTargetFromHandCostCount.java index f9994726cc92..594da75e9802 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/RevealTargetFromHandCostCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/RevealTargetFromHandCostCount.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.dynamicvalue.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/SacrificeCostConvertedMana.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/SacrificeCostConvertedMana.java index e15fd81dffb9..d12a859e2511 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/SacrificeCostConvertedMana.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/SacrificeCostConvertedMana.java @@ -31,7 +31,7 @@ public int calculate(Game game, Ability sourceAbility, Effect effect) { SacrificeTargetCost sacrificeCost = (SacrificeTargetCost) cost; int totalCMC = 0; for(Permanent permanent : sacrificeCost.getPermanents()) { - totalCMC += permanent.getConvertedManaCost(); + totalCMC += permanent.getManaValue(); } return totalCMC; } @@ -51,6 +51,6 @@ public String toString() { @Override public String getMessage() { - return "the sacrificed " + type + "'s converted mana cost"; + return "the sacrificed " + type + "'s mana value"; } } diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/SacrificeCostCreaturesPower.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/SacrificeCostCreaturesPower.java index 4cdaed1a82ce..2df90e667c6a 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/SacrificeCostCreaturesPower.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/SacrificeCostCreaturesPower.java @@ -11,7 +11,8 @@ * @author LevelX2 */ public enum SacrificeCostCreaturesPower implements DynamicValue { -instance; + instance; + @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { for (Cost cost : sourceAbility.getCosts()) { diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/SourcePermanentPowerCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/SourcePermanentPowerCount.java index c347fbc2c092..89d89b2933a9 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/SourcePermanentPowerCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/SourcePermanentPowerCount.java @@ -12,7 +12,7 @@ */ public class SourcePermanentPowerCount implements DynamicValue { - boolean allowNegativeValues; + private final boolean allowNegativeValues; public SourcePermanentPowerCount() { this(true); diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/SourcePermanentToughnessValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/SourcePermanentToughnessValue.java index 4b13eee1f65f..29c9eab43848 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/SourcePermanentToughnessValue.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/SourcePermanentToughnessValue.java @@ -1,5 +1,3 @@ - - package mage.abilities.dynamicvalue.common; import mage.abilities.Ability; @@ -12,22 +10,24 @@ import java.io.ObjectStreamException; /** - * * @author LevelX2 */ public class SourcePermanentToughnessValue implements DynamicValue { - - private static final SourcePermanentToughnessValue instance = new SourcePermanentToughnessValue(); - + + private static final SourcePermanentToughnessValue instance = new SourcePermanentToughnessValue(); + private Object readResolve() throws ObjectStreamException { return instance; - } - + } + public static SourcePermanentToughnessValue getInstance() { return instance; } - + + private SourcePermanentToughnessValue() { + } + @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { Permanent sourcePermanent = game.getPermanent(sourceAbility.getSourceId()); diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/TargetConvertedManaCost.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/TargetManaValue.java similarity index 75% rename from Mage/src/main/java/mage/abilities/dynamicvalue/common/TargetConvertedManaCost.java rename to Mage/src/main/java/mage/abilities/dynamicvalue/common/TargetManaValue.java index f6a5995854e5..efa3dbb8e88b 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/TargetConvertedManaCost.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/TargetManaValue.java @@ -10,20 +10,20 @@ /** * @author North */ -public enum TargetConvertedManaCost implements DynamicValue { +public enum TargetManaValue implements DynamicValue { instance; @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { Card card = game.getCard(sourceAbility.getFirstTarget()); if (card != null) { - return card.getConvertedManaCost(); + return card.getManaValue(); } return 0; } @Override - public TargetConvertedManaCost copy() { + public TargetManaValue copy() { return instance; } @@ -34,6 +34,6 @@ public String toString() { @Override public String getMessage() { - return "that card's converted mana cost"; + return "that card's mana value"; } } diff --git a/Mage/src/main/java/mage/abilities/effects/AsThoughManaEffect.java b/Mage/src/main/java/mage/abilities/effects/AsThoughManaEffect.java index 54ac888207ce..4f8e7e773b3f 100644 --- a/Mage/src/main/java/mage/abilities/effects/AsThoughManaEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/AsThoughManaEffect.java @@ -1,27 +1,30 @@ - package mage.abilities.effects; -import java.util.UUID; import mage.abilities.Ability; import mage.constants.ManaType; import mage.game.Game; import mage.players.ManaPoolItem; +import java.util.UUID; + /** - * * @author LevelX2 */ public interface AsThoughManaEffect extends AsThoughEffect { - // return a mana type that can be used to pay a mana cost instead of the normally needed mana type /** + * Return a mana type that can be used to pay a mana cost instead of the normally needed mana type + *

+ * Usage instructions: + * - on non applied effect: must return null (example: basic object applied, by you need to check a mana source too, see Draugr Necromancer); + * - on applied effect: must return compatible manaType with ManaPoolItem (e.g. return mana.getFirstAvailable) * - * @param manaType type of mana with which the player wants to pay the cost - * @param mana mana pool item to pay from the cost + * @param manaType type of mana with which the player wants to pay the cost + * @param mana mana pool item to pay from the cost * @param affectedControllerId * @param source * @param game - * @return + * @return null on non applied effect */ ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game); diff --git a/Mage/src/main/java/mage/abilities/effects/CastCardFromGraveyardThenExileItEffect.java b/Mage/src/main/java/mage/abilities/effects/CastCardFromGraveyardThenExileItEffect.java index cf38222a361b..bebbe3b86507 100644 --- a/Mage/src/main/java/mage/abilities/effects/CastCardFromGraveyardThenExileItEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/CastCardFromGraveyardThenExileItEffect.java @@ -20,7 +20,7 @@ public CastCardFromGraveyardThenExileItEffect() { super(Outcome.Benefit); } - CastCardFromGraveyardThenExileItEffect(final CastCardFromGraveyardThenExileItEffect effect) { + protected CastCardFromGraveyardThenExileItEffect(final CastCardFromGraveyardThenExileItEffect effect) { super(effect); } @@ -32,15 +32,15 @@ public CastCardFromGraveyardThenExileItEffect copy() { @Override public boolean apply(Game game, Ability source) { Card card = game.getCard(this.getTargetPointer().getFirst(game, source)); - if (card != null) { - ContinuousEffect effect = new CastCardFromGraveyardEffect(); - effect.setTargetPointer(new FixedTarget(card, game)); - game.addEffect(effect, source); - effect = new ExileReplacementEffect(card.getId()); - game.addEffect(effect, source); - return true; + if (card == null) { + return false; } - return false; + ContinuousEffect effect = new CastCardFromGraveyardEffect(); + effect.setTargetPointer(new FixedTarget(card, game)); + game.addEffect(effect, source); + effect = new ExileReplacementEffect(card.getId()); + game.addEffect(effect, source); + return true; } } @@ -51,7 +51,7 @@ class CastCardFromGraveyardEffect extends AsThoughEffectImpl { this.staticText = "You may cast target card from your graveyard"; } - CastCardFromGraveyardEffect(final CastCardFromGraveyardEffect effect) { + private CastCardFromGraveyardEffect(final CastCardFromGraveyardEffect effect) { super(effect); } @@ -67,7 +67,8 @@ public CastCardFromGraveyardEffect copy() { @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - return objectId.equals(this.getTargetPointer().getFirst(game, source)) && affectedControllerId.equals(source.getControllerId()); + return objectId.equals(this.getTargetPointer().getFirst(game, source)) + && affectedControllerId.equals(source.getControllerId()); } } @@ -81,7 +82,7 @@ class ExileReplacementEffect extends ReplacementEffectImpl { this.staticText = "If that card would be put into your graveyard this turn, exile it instead"; } - ExileReplacementEffect(final ExileReplacementEffect effect) { + private ExileReplacementEffect(final ExileReplacementEffect effect) { super(effect); this.cardId = effect.cardId; } @@ -95,10 +96,9 @@ public ExileReplacementEffect copy() { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player controller = game.getPlayer(source.getControllerId()); Card card = game.getCard(this.cardId); - if (controller != null && card != null) { - return controller.moveCards(card, Zone.EXILED, source, game); - } - return false; + return controller != null + && card != null + && controller.moveCards(card, Zone.EXILED, source, game); } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffect.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffect.java index 11a001c52759..77faf553bfb7 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffect.java @@ -1,9 +1,5 @@ package mage.abilities.effects; -import java.util.EnumSet; -import java.util.List; -import java.util.Set; -import java.util.UUID; import mage.MageObjectReference; import mage.abilities.Ability; import mage.constants.DependencyType; @@ -13,6 +9,11 @@ import mage.game.Game; import mage.target.targetpointer.TargetPointer; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; + /** * @author BetaSteward_at_googlemail.com */ @@ -46,8 +47,6 @@ public interface ContinuousEffect extends Effect { SubLayer getSublayer(); - void overrideRuleText(String text); - List getAffectedObjects(); Set isDependentTo(List allEffectsInLayer); diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java index 4558b0496f55..184d24f65898 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java @@ -275,11 +275,6 @@ public SubLayer getSublayer() { return sublayer; } - @Override - public void overrideRuleText(String text) { - this.staticText = text; - } - protected static boolean isCanKill(DynamicValue toughness) { if (toughness instanceof StaticValue) { return toughness.calculate(null, null, null) < 0; diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java index 2e4c8d115edb..10be750f0c64 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java @@ -532,7 +532,10 @@ public ApprovingObject asThough(UUID objectId, AsThoughEffectType type, Ability } UUID idToCheck; - if (objectToCheck instanceof SplitCardHalf) { + if (!type.needPlayCardAbility() && objectToCheck instanceof SplitCardHalf) { + // each split side uses own characteristics to check for playing, all other cases must use main card + // rules: + // 708.4. In every zone except the stack, the characteristics of a split card are those of its two halves combined. idToCheck = ((SplitCardHalf) objectToCheck).getMainCard().getId(); } else if (!type.needPlayCardAbility() && objectToCheck instanceof AdventureCardSpell) { // adventure spell uses alternative characteristics for spell/stack, all other cases must use main card @@ -609,6 +612,23 @@ public ApprovingObject asThough(UUID objectId, AsThoughEffectType type, Ability return null; } + /** + * Fit paying mana type with current mana pool (if asThoughMana affected) + *

+ * Example: + * - you need to pay {R} as cost + * - asThoughMana effect allows to use {G} as any color; + * - asThoughMana effect must change/fit paying mana type from {R} to {G} + * - after that you can pay {G} as cost + * + * @param manaType paying mana type + * @param mana checking pool item + * @param objectId paying ability's source object + * @param affectedAbility paying ability + * @param controllerId controller who pay + * @param game + * @return corrected paying mana type (same if no asThough effects and different on applied asThough effect) + */ public ManaType asThoughMana(ManaType manaType, ManaPoolItem mana, UUID objectId, Ability affectedAbility, UUID controllerId, Game game) { // First check existing only effects List asThoughEffectsList = getApplicableAsThoughEffects(AsThoughEffectType.SPEND_ONLY_MANA, game); @@ -971,7 +991,13 @@ public synchronized void apply(Game game) { for (ContinuousEffect effect : layer) { Set abilities = layeredEffects.getAbility(effect.getId()); for (Ability ability : abilities) { - effect.apply(Layer.CopyEffects_1, SubLayer.NA, ability, game); + effect.apply(Layer.CopyEffects_1, SubLayer.CopyEffects_1a, ability, game); + } + } + for (ContinuousEffect effect : layer) { + Set abilities = layeredEffects.getAbility(effect.getId()); + for (Ability ability : abilities) { + effect.apply(Layer.CopyEffects_1, SubLayer.FaceDownEffects_1b, ability, game); } } //Reload layerEffect if copy effects were applied @@ -1478,4 +1504,8 @@ public static void traceAddContinuousEffects(Map orderedEffects, ContinuousEffec } } + @Override + public String toString() { + return "Effects: " + allEffectsLists.stream().mapToInt(ContinuousEffectsList::size).sum(); + } } diff --git a/Mage/src/main/java/mage/abilities/effects/Effects.java b/Mage/src/main/java/mage/abilities/effects/Effects.java index dce5556d7239..d3b0948c536a 100644 --- a/Mage/src/main/java/mage/abilities/effects/Effects.java +++ b/Mage/src/main/java/mage/abilities/effects/Effects.java @@ -54,7 +54,11 @@ public String getText(Mode mode) { String concatPrefix = effect.getConcatPrefix(); if (effectNum > 1 && !concatPrefix.isEmpty() && !concatPrefix.equals(".")) { - nextRule = concatPrefix + " " + nextRule; + if (concatPrefix.contains("
")) { + nextRule = concatPrefix + CardUtil.getTextWithFirstCharUpperCase(nextRule); + } else { + nextRule = concatPrefix + " " + nextRule; + } } @@ -181,6 +185,8 @@ public void setTargetPointer(TargetPointer targetPointer) { } public void setValue(String key, Object value) { - this.stream().forEach(effect -> effect.setValue(key, value)); + for (Effect effect : this) { + effect.setValue(key, value); + } } } diff --git a/Mage/src/main/java/mage/abilities/effects/GainAbilitySpellsEffect.java b/Mage/src/main/java/mage/abilities/effects/GainAbilitySpellsEffect.java index 995919c5a941..78cbab355a88 100644 --- a/Mage/src/main/java/mage/abilities/effects/GainAbilitySpellsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/GainAbilitySpellsEffect.java @@ -1,6 +1,7 @@ package mage.abilities.effects; import mage.abilities.Ability; +import mage.abilities.MageSingleton; import mage.cards.Card; import mage.constants.*; import mage.filter.FilterObject; @@ -21,7 +22,7 @@ public GainAbilitySpellsEffect(Ability ability, FilterObject filter) { staticText = filter.getMessage() + " have " + ability.getRule(); } - public GainAbilitySpellsEffect(final GainAbilitySpellsEffect effect) { + private GainAbilitySpellsEffect(final GainAbilitySpellsEffect effect) { super(effect); this.ability = effect.ability; this.filter = effect.filter; @@ -36,47 +37,48 @@ public GainAbilitySpellsEffect copy() { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanent(source.getSourceId()); - if (player != null && permanent != null) { - for (Card card : game.getExile().getAllCards(game)) { - if (card.isOwnedBy(source.getControllerId()) && filter.match(card, game)) { - game.getState().addOtherAbility(card, ability); - } + if (player == null || permanent == null) { + return false; + } + for (Card card : game.getExile().getAllCards(game)) { + if (card.isOwnedBy(source.getControllerId()) && filter.match(card, game)) { + game.getState().addOtherAbility(card, ability); } - for (Card card : player.getLibrary().getCards(game)) { - if (filter.match(card, game)) { - game.getState().addOtherAbility(card, ability); - } + } + for (Card card : player.getLibrary().getCards(game)) { + if (filter.match(card, game)) { + game.getState().addOtherAbility(card, ability); } - for (Card card : player.getHand().getCards(game)) { - if (filter.match(card, game)) { - game.getState().addOtherAbility(card, ability); - } + } + for (Card card : player.getHand().getCards(game)) { + if (filter.match(card, game)) { + game.getState().addOtherAbility(card, ability); } - for (Card card : player.getGraveyard().getCards(game)) { - if (filter.match(card, game)) { - game.getState().addOtherAbility(card, ability); - } + } + for (Card card : player.getGraveyard().getCards(game)) { + if (filter.match(card, game)) { + game.getState().addOtherAbility(card, ability); } + } - // workaround to gain cost reduction abilities to commanders before cast (make it playable) - game.getCommanderCardsFromCommandZone(player, CommanderCardType.ANY).stream() - .filter(card -> filter.match(card, game)) - .forEach(card -> { - game.getState().addOtherAbility(card, ability); - }); + // workaround to gain cost reduction abilities to commanders before cast (make it playable) + game.getCommanderCardsFromCommandZone(player, CommanderCardType.ANY) + .stream() + .filter(card -> filter.match(card, game)) + .forEach(card -> { + game.getState().addOtherAbility(card, ability); + }); - for (StackObject stackObject : game.getStack()) { - if (stackObject.isControlledBy(source.getControllerId())) { - Card card = game.getCard(stackObject.getSourceId()); - if (card != null && filter.match(stackObject, game)) { - if (!card.hasAbility(ability, game)) { - game.getState().addOtherAbility(card, ability); - } - } - } + for (StackObject stackObject : game.getStack()) { + if (!stackObject.isControlledBy(source.getControllerId())) { + continue; + } + Card card = game.getCard(stackObject.getSourceId()); + if (card == null || !filter.match(stackObject, game)) { + continue; } - return true; + game.getState().addOtherAbility(card, ability); } - return false; + return true; } } diff --git a/Mage/src/main/java/mage/abilities/effects/PayCostToAttackBlockEffectImpl.java b/Mage/src/main/java/mage/abilities/effects/PayCostToAttackBlockEffectImpl.java index e000fabd618d..70adc3b8c4fb 100644 --- a/Mage/src/main/java/mage/abilities/effects/PayCostToAttackBlockEffectImpl.java +++ b/Mage/src/main/java/mage/abilities/effects/PayCostToAttackBlockEffectImpl.java @@ -154,7 +154,7 @@ public ManaCosts getManaCostToPay(GameEvent event, Ability source, Game game) { @Override public boolean isCostless(GameEvent event, Ability source, Game game) { ManaCosts currentManaCosts = getManaCostToPay(event, source, game); - if (currentManaCosts != null && currentManaCosts.convertedManaCost() > 0) { + if (currentManaCosts != null && currentManaCosts.manaValue() > 0) { return false; } return getOtherCostToPay(event, source, game) == null; diff --git a/Mage/src/main/java/mage/abilities/effects/PreventDamageAndRemoveCountersEffect.java b/Mage/src/main/java/mage/abilities/effects/PreventDamageAndRemoveCountersEffect.java index 0fa1e3a4395a..14410862e812 100644 --- a/Mage/src/main/java/mage/abilities/effects/PreventDamageAndRemoveCountersEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/PreventDamageAndRemoveCountersEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/effects/PreventionEffectImpl.java b/Mage/src/main/java/mage/abilities/effects/PreventionEffectImpl.java index 80e1a17f976d..b98933c64e38 100644 --- a/Mage/src/main/java/mage/abilities/effects/PreventionEffectImpl.java +++ b/Mage/src/main/java/mage/abilities/effects/PreventionEffectImpl.java @@ -96,9 +96,8 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { @Override public boolean checksEventType(GameEvent event, Game game) { switch (event.getType()) { - case DAMAGE_CREATURE: + case DAMAGE_PERMANENT: case DAMAGE_PLAYER: - case DAMAGE_PLANESWALKER: return true; } return false; diff --git a/Mage/src/main/java/mage/abilities/effects/RedirectionEffect.java b/Mage/src/main/java/mage/abilities/effects/RedirectionEffect.java index d5c0efaeeb2a..404791d020fc 100644 --- a/Mage/src/main/java/mage/abilities/effects/RedirectionEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/RedirectionEffect.java @@ -52,9 +52,8 @@ public RedirectionEffect(final RedirectionEffect effect) { @Override public boolean checksEventType(GameEvent event, Game game) { switch (event.getType()) { - case DAMAGE_CREATURE: + case DAMAGE_PERMANENT: case DAMAGE_PLAYER: - case DAMAGE_PLANESWALKER: return true; } return false; diff --git a/Mage/src/main/java/mage/abilities/effects/common/AmplifyEffect.java b/Mage/src/main/java/mage/abilities/effects/common/AmplifyEffect.java index 808f54303628..6788f5db7659 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/AmplifyEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/AmplifyEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/effects/common/BecomesMonarchTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/BecomesMonarchTargetEffect.java index d4fe9ebeb682..47c07de6da64 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/BecomesMonarchTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/BecomesMonarchTargetEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/effects/common/CantAttackBlockTransformAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CantAttackBlockTransformAttachedEffect.java index ab4726fa4207..72e174748724 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CantAttackBlockTransformAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CantAttackBlockTransformAttachedEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/effects/common/CantBeCounteredControlledEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CantBeCounteredControlledEffect.java index 0098e281b794..1db6300d9f1a 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CantBeCounteredControlledEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CantBeCounteredControlledEffect.java @@ -67,7 +67,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { return true; } else { MageObject sourceObject = game.getObject(event.getSourceId()); - if (sourceObject != null && filterSource.match(sourceObject, game)) { + if (filterSource.match(sourceObject, game)) { return true; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedAllEffect.java index f7392444293e..a2c34d6fc8d0 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedAllEffect.java @@ -76,7 +76,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { } else { sourceObject = stackObject; } - if (sourceObject != null && filterSource.match(sourceObject, game)) { + if (filterSource.match(sourceObject, game)) { return true; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedSourceEffect.java index 8d7fb405e816..69a35c9f4389 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedSourceEffect.java @@ -57,7 +57,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { } else { sourceObject = stackObject; } - if (sourceObject != null && filterSource.match(sourceObject, game)) { + if (filterSource.match(sourceObject, game)) { return true; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedTargetEffect.java index f0789905664a..cdcb1f4080ca 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedTargetEffect.java @@ -69,7 +69,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { } else { sourceObject = stackObject; } - if (sourceObject != null && filterSource.match(sourceObject, game)) { + if (filterSource.match(sourceObject, game)) { return true; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/CastSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/effects/common/CastSourceTriggeredAbility.java index b9b552d762ca..9ab05d3d49f4 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CastSourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CastSourceTriggeredAbility.java @@ -9,7 +9,6 @@ import mage.game.stack.Spell; /** - * * @author Plopman */ public class CastSourceTriggeredAbility extends TriggeredAbilityImpl { @@ -48,19 +47,19 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getSourceId().equals(this.getSourceId())) { - MageObject spellObject = game.getObject(sourceId); - if ((spellObject instanceof Spell)) { - Spell spell = (Spell) spellObject; - if (spell.getSpellAbility() != null) { - for (Effect effect : getEffects()) { - effect.setValue(SOURCE_CAST_SPELL_ABILITY, spell.getSpellAbility()); - } - } - } + if (!event.getSourceId().equals(this.getSourceId())) { + return false; + } + MageObject spellObject = game.getObject(sourceId); + if ((!(spellObject instanceof Spell))) { return true; } - return false; + Spell spell = (Spell) spellObject; + if (spell.getSpellAbility() != null) { + getEffects().setValue(SOURCE_CAST_SPELL_ABILITY, spell.getSpellAbility()); + } + getEffects().setValue("spellCast", spell); + return true; } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/ChangeATargetOfTargetSpellAbilityToSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ChangeATargetOfTargetSpellAbilityToSourceEffect.java index 4354f0904daa..530dc535f382 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ChangeATargetOfTargetSpellAbilityToSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ChangeATargetOfTargetSpellAbilityToSourceEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import java.util.UUID; diff --git a/Mage/src/main/java/mage/abilities/effects/common/ChooseBasicLandTypeEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ChooseBasicLandTypeEffect.java index 739aaaeca461..180b5b08cf91 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ChooseBasicLandTypeEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ChooseBasicLandTypeEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import mage.MageObject; diff --git a/Mage/src/main/java/mage/abilities/effects/common/ChooseLandTypeEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ChooseLandTypeEffect.java index a078a57c80d8..1ea0e9c123b8 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ChooseLandTypeEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ChooseLandTypeEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import mage.MageObject; diff --git a/Mage/src/main/java/mage/abilities/effects/common/ChooseNewTargetsTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ChooseNewTargetsTargetEffect.java index 7748418300de..01c798e12e9c 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ChooseNewTargetsTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ChooseNewTargetsTargetEffect.java @@ -1,58 +1,47 @@ - package mage.abilities.effects.common; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; -import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.stack.StackObject; /** * @author BetaSteward_at_googlemail.com - * */ public class ChooseNewTargetsTargetEffect extends OneShotEffect { - private boolean forceChange; - private boolean onlyOneTarget; - private FilterPermanent filterNewTarget; + private final boolean forceChange; + private final boolean onlyOneTarget; public ChooseNewTargetsTargetEffect() { this(false, false); } - public ChooseNewTargetsTargetEffect(boolean forceChange, boolean onlyOneTarget) { - this(forceChange, onlyOneTarget, null); - } - /** - * - * @param forceChange forces the user to choose another target (only targets - * with maxtargets = 1 supported) - * @param onlyOneTarget only one target can be selected for the change + * @param forceChange forces the user to choose another target (only targets + * with maxtargets = 1 supported) + * @param onlyOneTarget only one target can be selected for the change * @param filterNewTarget restriction to the new target */ - public ChooseNewTargetsTargetEffect(boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget) { + public ChooseNewTargetsTargetEffect(boolean forceChange, boolean onlyOneTarget) { super(Outcome.Benefit); this.forceChange = forceChange; this.onlyOneTarget = onlyOneTarget; - this.filterNewTarget = filterNewTarget; } public ChooseNewTargetsTargetEffect(final ChooseNewTargetsTargetEffect effect) { super(effect); this.forceChange = effect.forceChange; this.onlyOneTarget = effect.onlyOneTarget; - this.filterNewTarget = effect.filterNewTarget; } @Override public boolean apply(Game game, Ability source) { StackObject stackObject = game.getStack().getStackObject(source.getFirstTarget()); if (stackObject != null) { - return stackObject.chooseNewTargets(game, source.getControllerId(), forceChange, onlyOneTarget, filterNewTarget); + return stackObject.chooseNewTargets(game, source.getControllerId(), forceChange, onlyOneTarget, null); } return false; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ChooseOpponentEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ChooseOpponentEffect.java index 8c841e907f70..2b3064ea0267 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ChooseOpponentEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ChooseOpponentEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import mage.MageObject; diff --git a/Mage/src/main/java/mage/abilities/effects/common/ChoosePlayerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ChoosePlayerEffect.java index 7e38385678df..a7e0132e6548 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ChoosePlayerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ChoosePlayerEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import mage.MageObject; diff --git a/Mage/src/main/java/mage/abilities/effects/common/ChooseSecretOpponentEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ChooseSecretOpponentEffect.java index e8b893706cbe..e85b9ec7078c 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ChooseSecretOpponentEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ChooseSecretOpponentEffect.java @@ -4,16 +4,19 @@ import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; +import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetOpponent; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author LevelX2 * @author credman0 + * @author TheElk801 */ public class ChooseSecretOpponentEffect extends OneShotEffect { @@ -36,33 +39,53 @@ public boolean apply(Game game, Ability source) { if (mageObject == null) { mageObject = game.getObject(source.getSourceId()); } - if (controller != null && mageObject != null) { - TargetOpponent targetOpponent = new TargetOpponent(true); - targetOpponent.setTargetName("opponent (secretly)"); - while (!controller.choose(outcome, targetOpponent, source.getSourceId(), game)) { - if (!controller.canRespond()) { - return false; - } - } - if (targetOpponent.getTargets().isEmpty()) { - return false; - } - if (!game.isSimulation()) { - game.informPlayers(mageObject.getName() + ": " + controller.getLogName() + " has secretly chosen an opponent."); - } - game.getState().setValue(mageObject.getId() + SECRET_OPPONENT, targetOpponent.getTargets().get(0)); - game.getState().setValue(mageObject.getId() + SECRET_OWNER, controller.getId()); - if (mageObject instanceof Permanent) { - ((Permanent) mageObject).addInfo(SECRET_OPPONENT, - CardUtil.addToolTipMarkTags(controller.getLogName() + " has secretly chosen an opponent."), game); - } + if (controller == null || mageObject == null) { + return false; + } + TargetOpponent targetOpponent = new TargetOpponent(true); + targetOpponent.setTargetName("opponent (secretly)"); + controller.choose(outcome, targetOpponent, source.getSourceId(), game); + if (targetOpponent.getFirstTarget() == null) { + return false; } - return false; + game.informPlayers(mageObject.getName() + ": " + controller.getLogName() + " has secretly chosen an opponent."); + setChosenPlayer(targetOpponent.getFirstTarget(), source, game); + setSecretOwner(controller.getId(), source, game); + if (!(mageObject instanceof Permanent)) { + return true; + } + ((Permanent) mageObject).addInfo( + SECRET_OPPONENT, CardUtil.addToolTipMarkTags( + controller.getLogName() + " has secretly chosen an opponent." + ), game); + return true; + } + + public static void setChosenPlayer(UUID value, Ability source, Game game) { + game.getState().setValue(getthing(source, game) + ChooseSecretOpponentEffect.SECRET_OPPONENT, value); + } + + public static UUID getChosenPlayer(Ability source, Game game) { + return (UUID) game.getState().getValue(getthing(source, game) + ChooseSecretOpponentEffect.SECRET_OPPONENT); + } + + public static void setSecretOwner(UUID value, Ability source, Game game) { + game.getState().setValue(getthing(source, game) + ChooseSecretOpponentEffect.SECRET_OWNER, value); + } + + public static UUID getSecretOwner(Ability source, Game game) { + return (UUID) game.getState().getValue(getthing(source, game) + ChooseSecretOpponentEffect.SECRET_OWNER); + } + + private static String getthing(Ability source, Game game) { + if (game.getState().getZone(source.getSourceId()) == Zone.BATTLEFIELD) { + return "" + source.getSourceId() + '_' + source.getSourceObjectZoneChangeCounter(); + } + return "" + source.getSourceId() + '_' + (source.getSourceObjectZoneChangeCounter() + 1); } @Override public ChooseSecretOpponentEffect copy() { return new ChooseSecretOpponentEffect(this); } - } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ClashEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ClashEffect.java index 0fb8233495c3..0cfdb3838f0b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ClashEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ClashEffect.java @@ -12,7 +12,6 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.players.Player; import mage.players.PlayerList; import mage.target.Target; @@ -85,100 +84,104 @@ public ClashEffect copy() { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = source.getSourceObject(game); - if (controller != null && sourceObject != null - && !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.CLASH, controller.getId(), source, controller.getId()))) { - // choose opponent - Target target = new TargetOpponent(true); - target.setTargetName("an opponent to clash with"); - if (controller.choose(Outcome.Benefit, target, source.getSourceId(), game)) { - Player opponent = game.getPlayer(target.getFirstTarget()); - if (opponent != null) { - int cmcController = Integer.MIN_VALUE; - Card cardController = null; - boolean topController = true; - int cmcOpponent = Integer.MIN_VALUE; - Card cardOpponent = null; - boolean topOpponent = true; - // Reveal top cards of involved players - StringBuilder message = new StringBuilder("Clash: "); - message.append(controller.getLogName()); - if (controller.getLibrary().hasCards()) { - Cards cards = new CardsImpl(); - cardController = controller.getLibrary().getFromTop(game); - cards.add(cardController); - controller.revealCards(sourceObject.getIdName() + ": Clash card of " + controller.getName(), cards, game); - cmcController = cardController.getConvertedManaCost(); - message.append(" (").append(cmcController).append(')'); - } else { - message.append(" no card"); - } - message.append(" vs. ").append(opponent.getLogName()); - if (opponent.getLibrary().hasCards()) { - Cards cards = new CardsImpl(); - cardOpponent = opponent.getLibrary().getFromTop(game); - cards.add(cardOpponent); - opponent.revealCards(sourceObject.getIdName() + ": Clash card of " + opponent.getName(), cards, game); - cmcOpponent = cardOpponent.getConvertedManaCost(); - message.append(" (").append(cmcOpponent).append(')'); - } else { - message.append(" no card"); - } - message.append(" - "); - if (!game.isSimulation()) { - if (cmcController > cmcOpponent) { - message.append(controller.getLogName()).append(" won the clash"); - game.informPlayer(controller, "You won the clash!"); - } else if (cmcController < cmcOpponent) { - message.append(opponent.getLogName()).append(" won the clash"); - game.informPlayer(controller, opponent.getLogName() + " won the clash!"); - } else { - message.append(" no winner "); - } - game.informPlayers(message.toString()); - } - // decide to put the cards on top or on the buttom of library in turn order beginning with the active player in turn order - PlayerList playerList = game.getPlayerList().copy(); - playerList.setCurrent(game.getActivePlayerId()); - Player nextPlayer; - do { - Player current = playerList.getCurrent(game); - if (cardController != null && current.getId().equals(controller.getId())) { - topController = current.chooseUse(Outcome.Detriment, "Put " + cardController.getLogName() + " back on top of your library? (otherwise it goes to bottom)", source, game); - } - if (cardOpponent != null && current.getId().equals(opponent.getId())) { - topOpponent = current.chooseUse(Outcome.Detriment, "Put " + cardOpponent.getLogName() + " back on top of your library? (otherwise it goes to bottom)", source, game); - } - nextPlayer = playerList.getNext(game, false); - } while (nextPlayer != null && !nextPlayer.getId().equals(game.getActivePlayerId())); - // put the cards back to library - if (cardController != null) { - controller.moveCardToLibraryWithInfo(cardController, source, game, Zone.LIBRARY, topController, true); - } - if (cardOpponent != null) { - opponent.moveCardToLibraryWithInfo(cardOpponent, source, game, Zone.LIBRARY, topOpponent, true); - } - // fire CLASHED event with info about who won - String winner = "draw"; - if (cmcController > cmcOpponent) { - winner = "controller"; - } - if (cmcOpponent > cmcController) { - winner = "opponent"; - } - GameEvent gameEvent = new GameEvent(GameEvent.EventType.CLASHED, opponent.getId(), source, controller.getId()); - gameEvent.setData(winner); - game.fireEvent(gameEvent); + if (controller == null + || sourceObject == null + || game.replaceEvent(GameEvent.getEvent( + GameEvent.EventType.CLASH, controller.getId(), source, controller.getId() + ))) { + return false; + } + // choose opponent + Target target = new TargetOpponent(true); + target.setTargetName("an opponent to clash with"); + target.setNotTarget(true); + if (!controller.choose(Outcome.Benefit, target, source.getSourceId(), game)) { + return false; + } + Player opponent = game.getPlayer(target.getFirstTarget()); + if (opponent == null) { + return false; + } + int cmcController = Integer.MIN_VALUE; + Card cardController = null; + boolean topController = true; + int cmcOpponent = Integer.MIN_VALUE; + Card cardOpponent = null; + boolean topOpponent = true; + // Reveal top cards of involved players + StringBuilder message = new StringBuilder("Clash: "); + message.append(controller.getLogName()); + if (controller.getLibrary().hasCards()) { + Cards cards = new CardsImpl(); + cardController = controller.getLibrary().getFromTop(game); + cards.add(cardController); + controller.revealCards(sourceObject.getIdName() + ": Clash card of " + controller.getName(), cards, game); + cmcController = cardController.getManaValue(); + message.append(" (").append(cmcController).append(')'); + } else { + message.append(" no card"); + } + message.append(" vs. ").append(opponent.getLogName()); + if (opponent.getLibrary().hasCards()) { + Cards cards = new CardsImpl(); + cardOpponent = opponent.getLibrary().getFromTop(game); + cards.add(cardOpponent); + opponent.revealCards(sourceObject.getIdName() + ": Clash card of " + opponent.getName(), cards, game); + cmcOpponent = cardOpponent.getManaValue(); + message.append(" (").append(cmcOpponent).append(')'); + } else { + message.append(" no card"); + } + message.append(" - "); + if (!game.isSimulation()) { + if (cmcController > cmcOpponent) { + message.append(controller.getLogName()).append(" won the clash"); + game.informPlayer(controller, "You won the clash!"); + } else if (cmcController < cmcOpponent) { + message.append(opponent.getLogName()).append(" won the clash"); + game.informPlayer(controller, opponent.getLogName() + " won the clash!"); + } else { + message.append(" no winner "); + } + game.informPlayers(message.toString()); + } + // decide to put the cards on top or on the buttom of library in turn order beginning with the active player in turn order + PlayerList playerList = game.getPlayerList().copy(); + playerList.setCurrent(game.getActivePlayerId()); + Player nextPlayer; + do { + Player current = playerList.getCurrent(game); + if (cardController != null && current.getId().equals(controller.getId())) { + topController = current.chooseUse(Outcome.Detriment, "Put " + cardController.getLogName() + " back on top of your library? (otherwise it goes to bottom)", source, game); + } + if (cardOpponent != null && current.getId().equals(opponent.getId())) { + topOpponent = current.chooseUse(Outcome.Detriment, "Put " + cardOpponent.getLogName() + " back on top of your library? (otherwise it goes to bottom)", source, game); + } + nextPlayer = playerList.getNext(game, false); + } while (nextPlayer != null && !nextPlayer.getId().equals(game.getActivePlayerId())); + // put the cards back to library + if (cardController != null) { + controller.moveCardToLibraryWithInfo(cardController, source, game, Zone.LIBRARY, topController, true); + } + if (cardOpponent != null) { + opponent.moveCardToLibraryWithInfo(cardOpponent, source, game, Zone.LIBRARY, topOpponent, true); + } + // fire CLASHED event with info about who won + game.fireEvent(new GameEvent( + GameEvent.EventType.CLASHED, controller.getId(), source, + opponent.getId(), 0, cmcController > cmcOpponent + )); + game.fireEvent(new GameEvent( + GameEvent.EventType.CLASHED, opponent.getId(), source, + controller.getId(), 0, cmcOpponent > cmcController + )); - // set opponent to DoIfClashWonEffect - for (Effect effect : source.getEffects()) { - if (effect instanceof DoIfClashWonEffect) { - effect.setValue("clashOpponent", opponent); - } - } - return cmcController > cmcOpponent; - } + // set opponent to DoIfClashWonEffect + for (Effect effect : source.getEffects()) { + if (effect instanceof DoIfClashWonEffect) { + effect.setValue("clashOpponent", opponent); } } - return false; + return cmcController > cmcOpponent; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java index 4ff4e4bac943..ca4b712d1bad 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java @@ -32,7 +32,7 @@ public CopyEffect(MageObject copyFromObject, UUID copyToObjectId) { } public CopyEffect(Duration duration, MageObject copyFromObject, UUID copyToObjectId) { - super(duration, Layer.CopyEffects_1, SubLayer.NA, Outcome.BecomeCreature); + super(duration, Layer.CopyEffects_1, SubLayer.CopyEffects_1a, Outcome.BecomeCreature); this.copyFromObject = copyFromObject; this.copyToObjectId = copyToObjectId; } @@ -78,7 +78,7 @@ public boolean apply(Game game, Ability source) { Permanent permanent = affectedObjectList.get(0).getPermanent(game); if (permanent == null) { permanent = (Permanent) game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD, source.getSourceObjectZoneChangeCounter()); - // As long as the permanent is still in the LKI continue to copy to get triggered abilities to TriggeredAbilites for dies events. + // As long as the permanent is still in the LKI continue to copy to get triggered abilities to TriggeredAbilities for dies events. if (permanent == null) { discard(); return false; diff --git a/Mage/src/main/java/mage/abilities/effects/common/CopyPermanentEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CopyPermanentEffect.java index 62b1a840f63f..014f2368ec30 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CopyPermanentEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CopyPermanentEffect.java @@ -53,7 +53,15 @@ public CopyPermanentEffect(FilterPermanent filter, CopyApplier applier, boolean this.applier = applier; this.filter = filter; this.useTargetOfAbility = useTarget; - this.staticText = "as a copy of any " + filter.getMessage() + " on the battlefield"; + + String text = "as a copy of"; + if (filter.getMessage().startsWith("a ") || filter.getMessage().startsWith("an ")) { + text += " " + filter.getMessage(); + } else { + text += " any " + filter.getMessage() + " on the battlefield"; + } + text += applier == null ? "" : applier.getText(); + this.staticText = text; } public CopyPermanentEffect(final CopyPermanentEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/CopySourceSpellEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CopySourceSpellEffect.java new file mode 100644 index 000000000000..7f9fbf115321 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/CopySourceSpellEffect.java @@ -0,0 +1,50 @@ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.players.Player; + +/** + * @author TheElk801 + */ +public class CopySourceSpellEffect extends OneShotEffect { + + private final int amount; + + public CopySourceSpellEffect() { + this(1); + } + + public CopySourceSpellEffect(int amount) { + super(Outcome.Benefit); + staticText = "copy {this}"; + this.amount = amount; + } + + private CopySourceSpellEffect(final CopySourceSpellEffect effect) { + super(effect); + this.amount = effect.amount; + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Spell spell = (Spell) getValue("spellCast"); + if (spell == null) { + spell = game.getSpellOrLKIStack(source.getSourceId()); + } + if (controller == null || spell == null) { + return false; + } + spell.createCopyOnStack(game, source, source.getControllerId(), true, amount); + return true; + } + + @Override + public CopySourceSpellEffect copy() { + return new CopySourceSpellEffect(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/CopySpellForEachItCouldTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CopySpellForEachItCouldTargetEffect.java index 37921cb89f9e..c420de95be1c 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CopySpellForEachItCouldTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CopySpellForEachItCouldTargetEffect.java @@ -1,459 +1,69 @@ package mage.abilities.effects.common; -import mage.MageItem; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterImpl; -import mage.filter.FilterInPlay; -import mage.filter.predicate.mageobject.FromSetPredicate; +import mage.filter.predicate.mageobject.MageObjectReferencePredicate; import mage.game.Game; -import mage.game.events.CopiedStackObjectEvent; -import mage.game.stack.Spell; +import mage.game.stack.StackObject; import mage.players.Player; -import mage.target.Target; -import mage.target.TargetImpl; -import mage.util.TargetAddress; +import mage.util.functions.StackObjectCopyApplier; -import java.util.*; +import java.util.Iterator; +import java.util.List; /** - * @param * @author duncant */ -public abstract class CopySpellForEachItCouldTargetEffect extends OneShotEffect { +public abstract class CopySpellForEachItCouldTargetEffect extends OneShotEffect { - protected final FilterInPlay filter; + private static final class CopyApplier implements StackObjectCopyApplier { - public CopySpellForEachItCouldTargetEffect(FilterInPlay filter) { - super(Outcome.Copy); - this.staticText = "copy the spell for each other " + filter.getMessage() - + " that spell could target. Each copy targets a different one"; - this.filter = filter; - } - - public CopySpellForEachItCouldTargetEffect(final CopySpellForEachItCouldTargetEffect effect) { - super(effect); - this.filter = effect.filter; - } - - protected abstract Spell getSpell(Game game, Ability source); - - protected abstract Player getPlayer(Game game, Ability source); - - protected abstract boolean changeTarget(Target target, Game game, Ability source); + private final Iterator iterator; - protected abstract void modifyCopy(Spell copy, Game game, Ability source); - - protected void modifyCopy(Spell copy, T newTarget, Game game, Ability source) { - modifyCopy(copy, game, source); - } - - protected boolean okUUIDToCopyFor(UUID potentialTarget, Game game, Ability source, Spell spell) { - return true; - } - - @Override - public boolean apply(Game game, Ability source) { - Player actingPlayer = getPlayer(game, source); - if (actingPlayer == null) { - return false; + private CopyApplier(List predicates) { + this.iterator = predicates.iterator(); } - Spell spell = getSpell(game, source); - if (spell != null) { - Set targetsToBeChanged = new HashSet<>(); - boolean madeACopy = false; - for (TargetAddress addr : TargetAddress.walk(spell)) { - Target targetInstance = addr.getTarget(spell); - if (targetInstance.getNumberOfTargets() > 1) { - throw new UnsupportedOperationException("Changing Target instances " - + "with multiple targets is unsupported"); - } - if (changeTarget(targetInstance, game, source)) { - targetsToBeChanged.add(addr); - } - } - - if (targetsToBeChanged.size() < 1) { - return false; - } - - // generate copies for each possible target, but do not put it to stack (use must choose targets in custom order later) - Spell copy = spell.copySpell(source.getControllerId(), game); - modifyCopy(copy, game, source); - Target sampleTarget = targetsToBeChanged.iterator().next().getTarget(copy); - sampleTarget.setNotTarget(true); - - Map> playerTargetCopyMap = new HashMap<>(); - for (UUID objId : sampleTarget.possibleTargets(actingPlayer.getId(), game)) { - MageItem obj = game.getObject(objId); - if (obj == null) { - obj = game.getPlayer(objId); - } - if (obj != null) { - copy = spell.copySpell(source.getControllerId(), game); - try { - modifyCopy(copy, (T) obj, game, source); - if (!filter.match((T) obj, source.getSourceId(), actingPlayer.getId(), game)) { - continue; - } - } catch (ClassCastException e) { - continue; - } - - boolean legal = true; - for (TargetAddress addr : targetsToBeChanged) { - // potential target must be legal for all targets that we're about to change - Target targetInstance = addr.getTarget(copy); - legal &= targetInstance.canTarget(actingPlayer.getId(), objId, addr.getSpellAbility(copy), game); - if (!legal) { - break; - } - - // potential target must not be the thing that was targeted initially - targetInstance = addr.getTarget(spell); - legal &= !targetInstance.getTargets().contains(objId); - if (!legal) { - break; - } - } - legal &= okUUIDToCopyFor(objId, game, source, spell); - if (legal) { - for (TargetAddress addr : targetsToBeChanged) { - Target targetInstance = addr.getTarget(copy); - targetInstance.clearChosen(); - targetInstance.add(objId, game); - } - if (!playerTargetCopyMap.containsKey(copy.getControllerId())) { - playerTargetCopyMap.put(copy.getControllerId(), new HashMap<>()); - } - playerTargetCopyMap.get(copy.getControllerId()).put(objId, copy); - } - } - } - - // allows controller of the copies to choose spells order on stack (by using targeting GUI) - for (Player player : game.getPlayers().values()) { - if (playerTargetCopyMap.containsKey(player.getId())) { - Map targetCopyMap = playerTargetCopyMap.get(player.getId()); - if (targetCopyMap != null) { - while (!targetCopyMap.isEmpty()) { - // all checks must be make for new copied spell, not original (controller can be changed) - Spell spellSample = targetCopyMap.values().stream().findFirst().get(); - FilterInPlay setFilter = filter.copy(); - setFilter.add(new FromSetPredicate(targetCopyMap.keySet())); // allows only unselected targets - Target target = new TargetWithAdditionalFilter(sampleTarget, setFilter); - target.setNotTarget(false); // it is targeted, not chosen - target.setMinNumberOfTargets(0); // if not selected then it uses first target (see below), same for AI - target.setMaxNumberOfTargets(1); - target.setTargetName(filter.getMessage() + " that " + spellSample.getLogName() - + " could target (" + targetCopyMap.size() + " remaining)"); - - if (targetCopyMap.size() > 1 - && target.canChoose(spellSample.getId(), player.getId(), game)) { - // The original "source" is not applicable here due to the spell being a copy. ie: Zada, Hedron Grinder - Outcome outcome = spellSample.getSpellAbility().getAllEffects().getOutcome(spellSample.getSpellAbility()); - player.chooseTarget(outcome, target, spellSample.getSpellAbility(), game); // not source, but the spell that is copied - } + @Override + public void modifySpell(StackObject stackObject, Game game) { + } - Collection chosenIds = target.getTargets(); - if (chosenIds.isEmpty()) { - // uses first target on cancel/non-selected - chosenIds = targetCopyMap.keySet(); - } - List toDelete = new ArrayList<>(); - for (UUID chosenId : chosenIds) { - Spell chosenCopy = targetCopyMap.get(chosenId); - if (chosenCopy != null) { - game.getStack().push(chosenCopy); - game.fireEvent(new CopiedStackObjectEvent(spell, chosenCopy, source.getControllerId())); - toDelete.add(chosenId); - madeACopy = true; - } - } - targetCopyMap.keySet().removeAll((toDelete)); - } - } - } + @Override + public MageObjectReferencePredicate getNextPredicate() { + if (iterator.hasNext()) { + return iterator.next(); } - return madeACopy; + return null; } - return false; - } -} - -class CompoundFilter extends FilterImpl implements FilterInPlay { - - protected final FilterInPlay filterA; - protected final FilterInPlay filterB; - - public CompoundFilter(String name) { - super(name); - this.filterA = null; - this.filterB = null; - } - - public CompoundFilter(FilterInPlay filterA, FilterInPlay filterB, String name) { - super(name); - this.filterA = filterA; - this.filterB = filterB; - } - - @Override - public boolean checkObjectClass(Object object) { - return true; // already checked in the filter classes itself - } - - @Override - public boolean match(T obj, Game game) { - return (filterA == null - || !filterA.match(obj, game)) - && (filterB == null - || !filterB.match(obj, game)); - } - - @Override - public boolean match(T obj, UUID sourceId, UUID playerId, Game game) { - return (filterA == null - || !filterA.match(obj, sourceId, playerId, game)) - && (filterB == null - || !filterB.match(obj, sourceId, playerId, game)); - } - - @Override - public CompoundFilter copy() { - return new CompoundFilter(filterA == null ? null : filterA.copy(), - filterB == null ? null : filterB.copy(), - message); - } -} - -class TargetWithAdditionalFilter extends TargetImpl { - - protected final FilterInPlay additionalFilter; - protected final Target originalTarget; - - public TargetWithAdditionalFilter(final TargetWithAdditionalFilter target) { - this(target.originalTarget, target.additionalFilter, false); - } - - public TargetWithAdditionalFilter(Target originalTarget, FilterInPlay additionalFilter) { - this(originalTarget, additionalFilter, false); - } - - public TargetWithAdditionalFilter(Target originalTarget, FilterInPlay additionalFilter, boolean notTarget) { - this.originalTarget = originalTarget.copy(); - this.originalTarget.clearChosen(); - this.targetName = originalTarget.getFilter().getMessage(); - this.notTarget = notTarget; - this.additionalFilter = additionalFilter; - } - - @Override - public Target getOriginalTarget() { - return originalTarget; - } - - @Override - public int getNumberOfTargets() { - return originalTarget.getNumberOfTargets(); - } - - @Override - public int getMinNumberOfTargets() { - return originalTarget.getMinNumberOfTargets(); } - @Override - public void setMinNumberOfTargets(int minNumberOfTargets) { - originalTarget.setMinNumberOfTargets(minNumberOfTargets); - } - - @Override - public int getMaxNumberOfTargets() { - return originalTarget.getMaxNumberOfTargets(); - } - - @Override - public void setMaxNumberOfTargets(int maxNumberOfTargets) { - originalTarget.setMaxNumberOfTargets(maxNumberOfTargets); + protected CopySpellForEachItCouldTargetEffect() { + super(Outcome.Copy); } - @Override - public Zone getZone() { - return originalTarget.getZone(); + protected CopySpellForEachItCouldTargetEffect(final CopySpellForEachItCouldTargetEffect effect) { + super(effect); } - @Override - public boolean canTarget(UUID id, Game game) { - MageItem obj = game.getObject(id); - if (obj == null) { - obj = game.getPlayer(id); - } - - try { - return obj != null - && originalTarget.canTarget(id, game) - && additionalFilter.match((T) obj, game); - } catch (ClassCastException e) { - return false; - } - } + protected abstract StackObject getStackObject(Game game, Ability source); - @Override - public boolean canTarget(UUID id, Ability source, Game game) { - MageItem obj = game.getObject(id); - if (obj == null) { - obj = game.getPlayer(id); - } + protected abstract Player getPlayer(Game game, Ability source); - try { - return obj != null - && originalTarget.canTarget(id, source, game) - && additionalFilter.match((T) obj, source.getSourceId(), source.getControllerId(), game); - } catch (ClassCastException e) { - return false; - } - } + protected abstract List getPossibleTargets(StackObject stackObject, Player player, Ability source, Game game); @Override - public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) { - MageItem obj = game.getObject(id); - if (obj == null) { - obj = game.getPlayer(id); - } - - try { - return obj != null - && originalTarget.canTarget(controllerId, id, source, game) - && additionalFilter.match((T) obj, source.getSourceId(), controllerId, game); - } catch (ClassCastException e) { + public boolean apply(Game game, Ability source) { + Player actingPlayer = getPlayer(game, source); + StackObject stackObject = getStackObject(game, source); + if (actingPlayer == null || stackObject == null) { return false; } - } - - @Override - public FilterInPlay getFilter() { - return new CompoundFilter((FilterInPlay) originalTarget.getFilter(), - additionalFilter, originalTarget.getFilter().getMessage()); - } - - @Override - public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { - int remainingTargets = getNumberOfTargets() - targets.size(); - if (remainingTargets <= 0) { - return true; - } - - int count = 0; - for (UUID objId : originalTarget.possibleTargets(sourceId, sourceControllerId, game)) { - MageItem obj = game.getObject(objId); - if (obj == null) { - obj = game.getPlayer(objId); - } - try { - if (!targets.containsKey(objId) - && obj != null - && additionalFilter.match((T) obj, sourceId, sourceControllerId, game)) { - count++; - if (count >= remainingTargets) { - return true; - } - } - } catch (ClassCastException e) { - } - } - return false; - } - - @Override - public boolean canChoose(UUID sourceControllerId, Game game) { - int remainingTargets = getNumberOfTargets() - targets.size(); - if (remainingTargets <= 0) { - return true; - } - - int count = 0; - for (UUID objId : originalTarget.possibleTargets(sourceControllerId, game)) { - MageItem obj = game.getObject(objId); - if (obj == null) { - obj = game.getPlayer(objId); - } - try { - if (!targets.containsKey(objId) - && obj != null - && additionalFilter.match((T) obj, game)) { - count++; - if (count >= remainingTargets) { - return true; - } - } - } catch (ClassCastException e) { - } - } - return false; - } - - @Override - public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { - Set ret = new HashSet<>(); - for (UUID id : originalTarget.possibleTargets(sourceId, sourceControllerId, game)) { - MageItem obj = game.getObject(id); - if (obj == null) { - obj = game.getPlayer(id); - } - try { - if (obj != null - && additionalFilter.match((T) obj, sourceId, sourceControllerId, game)) { - ret.add(id); - } - } catch (ClassCastException e) { - } - } - return ret; - } - - @Override - public Set possibleTargets(UUID sourceControllerId, Game game) { - Set ret = new HashSet<>(); - for (UUID id : originalTarget.possibleTargets(sourceControllerId, game)) { - MageItem obj = game.getObject(id); - if (obj == null) { - obj = game.getPlayer(id); - } - try { - if (obj != null - && additionalFilter.match((T) obj, game)) { - ret.add(id); - } - } catch (ClassCastException e) { - } - } - return ret; - } - - @Override - public TargetWithAdditionalFilter copy() { - return new TargetWithAdditionalFilter(this); - } - - @Override - public String getTargetedName(Game game) { - StringBuilder sb = new StringBuilder(); - for (UUID targetId : getTargets()) { - MageObject object = game.getObject(targetId); - if (object != null) { - sb.append(object.getLogName()).append(' '); - } else { - Player player = game.getPlayer(targetId); - if (player != null) { - sb.append(player.getLogName()).append(' '); - } - } - } - return sb.toString().trim(); + List predicates = getPossibleTargets(stackObject, actingPlayer, source, game); + stackObject.createCopyOnStack( + game, source, actingPlayer.getId(), false, + predicates.size(), new CopyApplier(predicates) + ); + return true; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/CopyStackAbilityEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CopyStackAbilityEffect.java new file mode 100644 index 000000000000..d78243bbae3c --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/CopyStackAbilityEffect.java @@ -0,0 +1,39 @@ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.stack.StackAbility; +import mage.players.Player; + +/** + * @author TheElk801 + */ +public class CopyStackAbilityEffect extends OneShotEffect { + + public CopyStackAbilityEffect() { + super(Outcome.Copy); + staticText = "copy that ability. You may choose new targets for the copy"; + } + + private CopyStackAbilityEffect(final CopyStackAbilityEffect effect) { + super(effect); + } + + @Override + public CopyStackAbilityEffect copy() { + return new CopyStackAbilityEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + StackAbility ability = (StackAbility) getValue("stackAbility"); + if (controller == null || ability == null) { + return false; + } + ability.createCopyOnStack(game, source, source.getControllerId(), true); + return true; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/CopyTargetSpellEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CopyTargetSpellEffect.java index 4269829f4e74..734a639171ac 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CopyTargetSpellEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CopyTargetSpellEffect.java @@ -65,17 +65,7 @@ public boolean apply(Game game, Ability source) { spell = (Spell) game.getLastKnownInformation(targetPointer.getFirst(game, source), Zone.STACK); } if (spell != null) { - StackObject newStackObject = spell.createCopyOnStack(game, source, useController ? spell.getControllerId() : source.getControllerId(), chooseTargets); - Player player = game.getPlayer(source.getControllerId()); - if (player != null && newStackObject instanceof Spell) { - String activateMessage = ((Spell) newStackObject).getActivatedMessage(game); - if (activateMessage.startsWith(" casts ")) { - activateMessage = activateMessage.substring(6); - } - if (!game.isSimulation()) { - game.informPlayers(player.getLogName() + activateMessage); - } - } + spell.createCopyOnStack(game, source, useController ? spell.getControllerId() : source.getControllerId(), chooseTargets); return true; } return false; diff --git a/Mage/src/main/java/mage/abilities/effects/common/CopyTokenEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CopyTokenEffect.java index 4f7894c997a6..f50173c6a9e6 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CopyTokenEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CopyTokenEffect.java @@ -12,7 +12,7 @@ public class CopyTokenEffect extends ContinuousEffectImpl { protected Token token; public CopyTokenEffect(Token token) { - super(Duration.WhileOnBattlefield, Layer.CopyEffects_1, SubLayer.NA, Outcome.BecomeCreature); + super(Duration.WhileOnBattlefield, Layer.CopyEffects_1, SubLayer.CopyEffects_1a, Outcome.BecomeCreature); this.token = token.copy(); staticText = "You may have {this} enter the battlefield as a copy of " + token.getDescription() + " on the battlefield"; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/CouncilsDilemmaVoteEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CouncilsDilemmaVoteEffect.java deleted file mode 100644 index dbd6263b5b13..000000000000 --- a/Mage/src/main/java/mage/abilities/effects/common/CouncilsDilemmaVoteEffect.java +++ /dev/null @@ -1,42 +0,0 @@ -package mage.abilities.effects.common; - -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.constants.Outcome; -import mage.game.Game; -import mage.players.Player; - -/** - * @author JRHerlehy - */ -public abstract class CouncilsDilemmaVoteEffect extends OneShotEffect { - - protected int voteOneCount = 0, voteTwoCount = 0; - - public CouncilsDilemmaVoteEffect(Outcome outcome) { - super(outcome); - } - - public CouncilsDilemmaVoteEffect(final CouncilsDilemmaVoteEffect effect) { - super(effect); - } - - protected void vote(String choiceOne, String choiceTwo, Player controller, Game game, Ability source) { - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - if (player.chooseUse(Outcome.Vote, - "Choose " + choiceOne + " or " + choiceTwo + "?", - source.getRule(), choiceOne, choiceTwo, source, game)) { - voteOneCount++; - game.informPlayers(player.getLogName() + " has voted for " + choiceOne); - } else { - voteTwoCount++; - game.informPlayers(player.getLogName() + " has voted for " + choiceTwo); - } - } - } - } - -} diff --git a/Mage/src/main/java/mage/abilities/effects/common/CounterUnlessPaysEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CounterUnlessPaysEffect.java index b89b75a67935..d8534377cffd 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CounterUnlessPaysEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CounterUnlessPaysEffect.java @@ -7,6 +7,8 @@ import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; +import mage.constants.Zone; +import mage.constants.ZoneDetail; import mage.game.Game; import mage.game.stack.StackObject; import mage.players.Player; @@ -19,15 +21,26 @@ public class CounterUnlessPaysEffect extends OneShotEffect { protected Cost cost; protected DynamicValue genericMana; + private final boolean exile; public CounterUnlessPaysEffect(Cost cost) { + this(cost, false); + } + + public CounterUnlessPaysEffect(Cost cost, boolean exile) { super(Outcome.Detriment); this.cost = cost; + this.exile = exile; } public CounterUnlessPaysEffect(DynamicValue genericMana) { + this(genericMana, false); + } + + public CounterUnlessPaysEffect(DynamicValue genericMana, boolean exile) { super(Outcome.Detriment); this.genericMana = genericMana; + this.exile = exile; } public CounterUnlessPaysEffect(final CounterUnlessPaysEffect effect) { @@ -38,6 +51,7 @@ public CounterUnlessPaysEffect(final CounterUnlessPaysEffect effect) { if (effect.genericMana != null) { this.genericMana = effect.genericMana.copy(); } + this.exile = effect.exile; } @Override @@ -48,36 +62,40 @@ public CounterUnlessPaysEffect copy() { @Override public boolean apply(Game game, Ability source) { StackObject spell = game.getStack().getStackObject(targetPointer.getFirst(game, source)); - if (spell != null) { - Player player = game.getPlayer(spell.getControllerId()); - if (player != null) { - Cost costToPay; - String costValueMessage; - if (cost != null) { - costToPay = cost.copy(); - costValueMessage = costToPay.getText(); - } else { - costToPay = ManaUtil.createManaCost(genericMana, game, source, this); - costValueMessage = "{" + genericMana.calculate(game, source, this) + "}"; - } - String message; - if (costToPay instanceof ManaCost) { - message = "Would you like to pay " + costValueMessage + " to prevent counter effect?"; - } else { - message = costValueMessage + " to prevent counter effect?"; - } + if (spell == null) { + return false; + } + Player player = game.getPlayer(spell.getControllerId()); + if (player == null) { + return false; + } + Cost costToPay; + String costValueMessage; + if (cost != null) { + costToPay = cost.copy(); + costValueMessage = costToPay.getText(); + } else { + costToPay = ManaUtil.createManaCost(genericMana, game, source, this); + costValueMessage = "{" + genericMana.calculate(game, source, this) + "}"; + } + String message = ""; + if (costToPay instanceof ManaCost) { + message += "Pay "; + } + message += costValueMessage + '?'; - costToPay.clearPaid(); - if (!(player.chooseUse(Outcome.Benefit, message, source, game) - && costToPay.pay(source, game, source, spell.getControllerId(), false, null))) { - game.informPlayers(player.getLogName() + " chooses not to pay " + costValueMessage + " to prevent the counter effect"); - return game.getStack().counter(spell.getId(), source, game); - } - game.informPlayers(player.getLogName() + " chooses to pay " + costValueMessage + " to prevent the counter effect"); - return true; + costToPay.clearPaid(); + if (!(player.chooseUse(Outcome.Benefit, message, source, game) + && costToPay.pay(source, game, source, spell.getControllerId(), false, null))) { + game.informPlayers(player.getLogName() + " chooses not to pay " + costValueMessage + " to prevent the counter effect"); + if (exile) { + game.getStack().counter(spell.getId(), source, game, Zone.EXILED, false, ZoneDetail.NONE); + } else { + return game.getStack().counter(spell.getId(), source, game); } } - return false; + game.informPlayers(player.getLogName() + " chooses to pay " + costValueMessage + " to prevent the counter effect"); + return true; } @Override @@ -102,6 +120,9 @@ public String getText(Mode mode) { sb.append(", where X is "); sb.append(genericMana.getMessage()); } + if (exile) { + sb.append(". If that spell is countered this way, exile it instead of putting it into its owner's graveyard."); + } return sb.toString(); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenTargetEffect.java index ce3992cb4178..49384ac7d2ce 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenTargetEffect.java @@ -10,6 +10,8 @@ import mage.game.permanent.token.Token; import mage.util.CardUtil; +import java.util.UUID; + /** * @author Loki */ @@ -57,7 +59,7 @@ public CreateTokenTargetEffect copy() { public boolean apply(Game game, Ability source) { int value = amount.calculate(game, source, this); if (value > 0) { - return token.putOntoBattlefield(value, game, source, targetPointer.getFirst(game, source), tapped, attacking); + return token.putOntoBattlefield(value, game, source, targetPointer.getFirst(game, source), tapped, attacking, (UUID) getValue("playerToAttack")); } return true; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/DamageWithExcessEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DamageWithExcessEffect.java new file mode 100644 index 000000000000..fcd7cef2fcc2 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/DamageWithExcessEffect.java @@ -0,0 +1,51 @@ +package mage.abilities.effects.common; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * @author TheElk801 + */ +public class DamageWithExcessEffect extends OneShotEffect { + + private final int amount; + + public DamageWithExcessEffect(int amount) { + super(Outcome.Damage); + this.amount = amount; + this.staticText = "{this} deals " + amount + " damage to target creature. " + + "Excess damage is dealt to that creature's controller instead"; + } + + private DamageWithExcessEffect(final DamageWithExcessEffect effect) { + super(effect); + this.amount = effect.amount; + } + + @Override + public DamageWithExcessEffect copy() { + return new DamageWithExcessEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source)); + MageObject sourceObject = source.getSourceObject(game); + if (permanent == null || sourceObject == null) { + return false; + } + int lethal = permanent.getLethalDamage(source.getSourceId(), game); + lethal = Math.min(lethal, amount); + permanent.damage(lethal, source.getSourceId(), source, game); + Player player = game.getPlayer(permanent.getControllerId()); + if (player != null && lethal < amount) { + player.damage(amount - lethal, source.getSourceId(), source, game); + } + return true; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/DestroyEquippedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DestroyEquippedEffect.java index c5441b28ff80..c5464ee044c1 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DestroyEquippedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DestroyEquippedEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import java.util.UUID; diff --git a/Mage/src/main/java/mage/abilities/effects/common/DevourEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DevourEffect.java index 42afec4095a0..459df5ba0646 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DevourEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DevourEffect.java @@ -7,7 +7,7 @@ import mage.constants.Outcome; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; diff --git a/Mage/src/main/java/mage/abilities/effects/common/DoubleCountersSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DoubleCountersSourceEffect.java new file mode 100644 index 000000000000..2879e39716e5 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/DoubleCountersSourceEffect.java @@ -0,0 +1,43 @@ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * @author TheElk801 + */ +public class DoubleCountersSourceEffect extends OneShotEffect { + + private final CounterType counterType; + + public DoubleCountersSourceEffect(CounterType counterType) { + super(Outcome.Benefit); + staticText = "double the number of " + counterType.getName() + " counters on {this}"; + this.counterType = counterType; + } + + private DoubleCountersSourceEffect(final DoubleCountersSourceEffect effect) { + super(effect); + this.counterType = effect.counterType; + } + + @Override + public DoubleCountersSourceEffect copy() { + return new DoubleCountersSourceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent == null) { + return false; + } + return permanent.addCounters(counterType.createInstance( + permanent.getCounters(game).getCount(counterType) + ), source.getControllerId(), source, game); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/EntersBattlefieldUnderControlOfOpponentOfChoiceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/EntersBattlefieldUnderControlOfOpponentOfChoiceEffect.java index 5b1b4f4e94e3..383b7e7ed5f4 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/EntersBattlefieldUnderControlOfOpponentOfChoiceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/EntersBattlefieldUnderControlOfOpponentOfChoiceEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/effects/common/EpicEffect.java b/Mage/src/main/java/mage/abilities/effects/common/EpicEffect.java index 3bf0e41e884c..87f7eb6a92c1 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/EpicEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/EpicEffect.java @@ -1,26 +1,17 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfYourNextUpkeepDelayedTriggeredAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.constants.Duration; import mage.constants.Outcome; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; -import mage.game.stack.StackObject; import mage.players.Player; -import java.util.Objects; - /** * @author jeffwadsworth *

@@ -35,42 +26,36 @@ */ public class EpicEffect extends OneShotEffect { - static final String rule = "
Epic (For the rest of the game, you can't cast spells. At the beginning of each of your upkeeps for the rest of the game, copy this spell except for its epic ability. If the spell has targets, you may choose new targets for the copy)"; + private static final String rule = "Epic (For the rest of the game, you can't cast spells. " + + "At the beginning of each of your upkeeps for the rest of the game, copy this spell " + + "except for its epic ability. If the spell has targets, you may choose new targets for the copy)"; public EpicEffect() { super(Outcome.Benefit); - staticText = rule; + staticText = "
" + rule; } - public EpicEffect(final EpicEffect effect) { + private EpicEffect(final EpicEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - StackObject stackObject = game.getStack().getStackObject(source.getId()); - Spell spell = (Spell) stackObject; - if (spell == null) { - return false; - } - spell = spell.copySpell(source.getControllerId(), game); - // Remove Epic effect from the spell - Effect epicEffect = null; - for (Effect effect : spell.getSpellAbility().getEffects()) { - if (effect instanceof EpicEffect) { - epicEffect = effect; - break; - } - } - spell.getSpellAbility().getEffects().remove(epicEffect); - DelayedTriggeredAbility ability = new AtTheBeginOfYourNextUpkeepDelayedTriggeredAbility(new EpicPushEffect(spell, rule), Duration.EndOfGame, false); - game.addDelayedTriggeredAbility(ability, source); - game.addEffect(new EpicReplacementEffect(), source); - return true; + if (controller == null) { + return false; } - return false; + Spell spell = (Spell) source.getSourceObject(game); + if (spell == null) { + return false; + } + spell = spell.copy(); + spell.getSpellAbility().getEffects().removeIf(EpicEffect.class::isInstance); + game.addDelayedTriggeredAbility(new AtTheBeginOfYourNextUpkeepDelayedTriggeredAbility( + new EpicPushEffect(spell, rule), Duration.EndOfGame, false + ), source); + game.addEffect(new EpicReplacementEffect(), source); + return true; } @Override @@ -81,12 +66,12 @@ public EpicEffect copy() { class EpicReplacementEffect extends ContinuousRuleModifyingEffectImpl { - public EpicReplacementEffect() { + EpicReplacementEffect() { super(Duration.EndOfGame, Outcome.Neutral); staticText = "For the rest of the game, you can't cast spells"; } - public EpicReplacementEffect(final EpicReplacementEffect effect) { + private EpicReplacementEffect(final EpicReplacementEffect effect) { super(effect); } @@ -116,25 +101,22 @@ public boolean checksEventType(GameEvent event, Game game) { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (Objects.equals(source.getControllerId(), event.getPlayerId())) { - MageObject object = game.getObject(event.getSourceId()); - return object != null; - } - return false; + return source.isControlledBy(event.getPlayerId()) + && game.getObject(event.getSourceId()) != null; } } class EpicPushEffect extends OneShotEffect { - final private Spell spell; + private final Spell spell; - public EpicPushEffect(Spell spell, String ruleText) { + EpicPushEffect(Spell spell, String ruleText) { super(Outcome.Copy); this.spell = spell; staticText = ruleText; } - public EpicPushEffect(final EpicPushEffect effect) { + private EpicPushEffect(final EpicPushEffect effect) { super(effect); this.spell = effect.spell; } @@ -145,7 +127,6 @@ public boolean apply(Game game, Ability source) { spell.createCopyOnStack(game, source, source.getControllerId(), true); return true; } - return false; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExchangeLifeControllerTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExchangeLifeControllerTargetEffect.java new file mode 100644 index 000000000000..843a45dddb41 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/ExchangeLifeControllerTargetEffect.java @@ -0,0 +1,44 @@ + +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; + +/** + * @author Styxo + */ +public class ExchangeLifeControllerTargetEffect extends OneShotEffect { + + public ExchangeLifeControllerTargetEffect() { + super(Outcome.Neutral); + } + + private ExchangeLifeControllerTargetEffect(final ExchangeLifeControllerTargetEffect effect) { + super(effect); + } + + @Override + public ExchangeLifeControllerTargetEffect copy() { + return new ExchangeLifeControllerTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player player = game.getPlayer(source.getFirstTarget()); + if (controller == null || player == null) { + return false; + } + controller.exchangeLife(player, source, game); + return true; + } + + @Override + public String getText(Mode mode) { + return "Exchange life totals with target " + mode.getTargets().get(0).getTargetName(); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExchangeLifeTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExchangeLifeTargetEffect.java deleted file mode 100644 index 725072c7dc5d..000000000000 --- a/Mage/src/main/java/mage/abilities/effects/common/ExchangeLifeTargetEffect.java +++ /dev/null @@ -1,65 +0,0 @@ - -package mage.abilities.effects.common; - -import mage.abilities.Ability; -import mage.abilities.Mode; -import mage.abilities.effects.OneShotEffect; -import mage.constants.Outcome; -import mage.game.Game; -import mage.players.Player; - -/** - * - * @author Styxo - */ -public class ExchangeLifeTargetEffect extends OneShotEffect { - - public ExchangeLifeTargetEffect() { - super(Outcome.Neutral); - } - - public ExchangeLifeTargetEffect(final ExchangeLifeTargetEffect effect) { - super(effect); - } - - @Override - public ExchangeLifeTargetEffect copy() { - return new ExchangeLifeTargetEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Player player = game.getPlayer(source.getFirstTarget()); - if (controller != null && player != null) { - int lifeController = controller.getLife(); - int lifePlayer = player.getLife(); - - if (lifeController == lifePlayer) { - return false; - } - - if (!controller.isLifeTotalCanChange() || !player.isLifeTotalCanChange()) { - return false; - } - - if (lifeController < lifePlayer && (!controller.isCanGainLife() || !player.isCanLoseLife())) { - return false; - } - - if (lifeController > lifePlayer && (!controller.isCanLoseLife() || !player.isCanGainLife())) { - return false; - } - - controller.setLife(lifePlayer, game, source); - player.setLife(lifeController, game, source); - return true; - } - return false; - } - - @Override - public String getText(Mode mode) { - return "Exchange life totals with target " + mode.getTargets().get(0).getTargetName(); - } -} diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExchangeLifeTwoTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExchangeLifeTwoTargetEffect.java new file mode 100644 index 000000000000..3e6d593fa97e --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/ExchangeLifeTwoTargetEffect.java @@ -0,0 +1,41 @@ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; + +/** + * @author TheElk801 + */ +public class ExchangeLifeTwoTargetEffect extends OneShotEffect { + + public ExchangeLifeTwoTargetEffect() { + super(Outcome.Neutral); + staticText = "two target players exchange life totals"; + } + + private ExchangeLifeTwoTargetEffect(final ExchangeLifeTwoTargetEffect effect) { + super(effect); + } + + @Override + public ExchangeLifeTwoTargetEffect copy() { + return new ExchangeLifeTwoTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (source.getTargets().get(0).getTargets().size() < 2) { + return false; + } + Player player1 = game.getPlayer(source.getTargets().get(0).getTargets().get(0)); + Player player2 = game.getPlayer(source.getTargets().get(0).getTargets().get(1)); + if (player1 == null || player2 == null) { + return false; + } + player1.exchangeLife(player2, source, game); + return true; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileAdventureSpellEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileAdventureSpellEffect.java index d6a5c84953de..d2a7dd9b2ee9 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileAdventureSpellEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileAdventureSpellEffect.java @@ -1,6 +1,5 @@ package mage.abilities.effects.common; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.MageSingleton; import mage.abilities.effects.AsThoughEffectImpl; @@ -18,6 +17,8 @@ import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; +import java.util.UUID; + /** * @author phulin */ @@ -48,7 +49,7 @@ public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { Spell spell = game.getStack().getSpell(source.getId()); - if (spell != null && !spell.isCopy()) { + if (spell != null) { Card spellCard = spell.getCard(); if (spellCard instanceof AdventureCardSpell) { UUID exileId = adventureExileId(controller.getId(), game); diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileAllEffect.java index 8f4236e14eca..ca1e864d1dd4 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileAllEffect.java @@ -1,44 +1,40 @@ - package mage.abilities.effects.common; -import java.util.List; -import java.util.UUID; -import mage.constants.Outcome; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.players.Player; +import mage.util.CardUtil; /** - * * @author LevelX2 */ public class ExileAllEffect extends OneShotEffect { - private FilterPermanent filter; - private String exileZone = null; - private UUID exileId = null; + private final FilterPermanent filter; + private final boolean forSource; public ExileAllEffect(FilterPermanent filter) { - this(filter, null, null); + this(filter, false); } - public ExileAllEffect(FilterPermanent filter, UUID exileId, String exileZone) { + public ExileAllEffect(FilterPermanent filter, boolean forSource) { super(Outcome.Exile); this.filter = filter; - this.exileZone = exileZone; - this.exileId = exileId; + this.forSource = forSource; setText(); } public ExileAllEffect(final ExileAllEffect effect) { super(effect); this.filter = effect.filter.copy(); - this.exileZone = effect.exileZone; - this.exileId = effect.exileId; + this.forSource = effect.forSource; } @Override @@ -49,14 +45,18 @@ public ExileAllEffect copy() { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - List permanents = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game); - for (Permanent permanent : permanents) { - controller.moveCardToExileWithInfo(permanent, exileId, exileZone, source, game, Zone.BATTLEFIELD, true); - } - return true; + MageObject sourceObject = source.getSourceObject(game); + if (controller == null || sourceObject == null) { + return false; + } + Cards cards = new CardsImpl(); + game.getBattlefield().getActivePermanents( + filter, source.getControllerId(), source.getSourceId(), game + ).stream().forEach(cards::add); + if (forSource) { + return controller.moveCardsToExile(cards.getCards(game), source, game, true, CardUtil.getExileZoneId(game, source), sourceObject.getName()); } - return false; + return controller.moveCards(cards, Zone.EXILED, source, game); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileAndGainLifeEqualPowerTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileAndGainLifeEqualPowerTargetEffect.java index b208f8472f62..26ef9df800bc 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileAndGainLifeEqualPowerTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileAndGainLifeEqualPowerTargetEffect.java @@ -4,6 +4,7 @@ import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; +import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -18,7 +19,7 @@ public ExileAndGainLifeEqualPowerTargetEffect() { staticText = "Exile target creature. Its controller gains life equal to its power"; } - public ExileAndGainLifeEqualPowerTargetEffect(final ExileAndGainLifeEqualPowerTargetEffect effect) { + private ExileAndGainLifeEqualPowerTargetEffect(final ExileAndGainLifeEqualPowerTargetEffect effect) { super(effect); } @@ -30,16 +31,17 @@ public ExileAndGainLifeEqualPowerTargetEffect copy() { @Override public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); - if (permanent != null) { - Player player = game.getPlayer(permanent.getControllerId()); - if (player != null) { - int creaturePower = permanent.getPower().getValue(); - permanent.moveToExile(null, null, source, game); - game.getState().processAction(game); - player.gainLife(creaturePower, game, source); - } + if (permanent == null) { + return false; + } + Player controller = game.getPlayer(source.getControllerId()); + Player player = game.getPlayer(permanent.getControllerId()); + if (controller == null || player == null) { return true; } - return false; + int creaturePower = permanent.getPower().getValue(); + controller.moveCards(permanent, Zone.EXILED, source, game); + player.gainLife(creaturePower, game, source); + return true; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileAndReturnTransformedSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileAndReturnTransformedSourceEffect.java index 4201f8e84ae2..075e7910e045 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileAndReturnTransformedSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileAndReturnTransformedSourceEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileAttachedEffect.java index 040fb241a990..9b2c2644830f 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileAttachedEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileCardEnteringGraveyardReplacementEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileCardEnteringGraveyardReplacementEffect.java new file mode 100644 index 000000000000..cdf595971c7b --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileCardEnteringGraveyardReplacementEffect.java @@ -0,0 +1,50 @@ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; + +import java.util.UUID; + +public class ExileCardEnteringGraveyardReplacementEffect extends ReplacementEffectImpl { + + private final UUID cardId; + + public ExileCardEnteringGraveyardReplacementEffect(UUID cardId) { + super(Duration.EndOfTurn, Outcome.Exile); + this.cardId = cardId; + } + + ExileCardEnteringGraveyardReplacementEffect(final ExileCardEnteringGraveyardReplacementEffect effect) { + super(effect); + this.cardId = effect.cardId; + } + + @Override + public ExileCardEnteringGraveyardReplacementEffect copy() { + return new ExileCardEnteringGraveyardReplacementEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + ((ZoneChangeEvent) event).setToZone(Zone.EXILED); + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + return zEvent.getToZone() == Zone.GRAVEYARD + && zEvent.getTargetId().equals(this.cardId); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileCardFromOwnGraveyardControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileCardFromOwnGraveyardControllerEffect.java index 45f9846bc480..74d7260b6ef1 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileCardFromOwnGraveyardControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileCardFromOwnGraveyardControllerEffect.java @@ -2,10 +2,11 @@ import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; @@ -17,7 +18,8 @@ * @author LevelX2 */ public class ExileCardFromOwnGraveyardControllerEffect extends OneShotEffect { - private int amount; + + private final int amount; public ExileCardFromOwnGraveyardControllerEffect(int amount) { super(Outcome.Exile); @@ -25,7 +27,7 @@ public ExileCardFromOwnGraveyardControllerEffect(int amount) { this.staticText = setText(); } - public ExileCardFromOwnGraveyardControllerEffect(final ExileCardFromOwnGraveyardControllerEffect effect) { + private ExileCardFromOwnGraveyardControllerEffect(final ExileCardFromOwnGraveyardControllerEffect effect) { super(effect); this.amount = effect.amount; } @@ -38,19 +40,21 @@ public ExileCardFromOwnGraveyardControllerEffect copy() { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - TargetCardInYourGraveyard target = new TargetCardInYourGraveyard(Math.min(amount, player.getGraveyard().size()), new FilterCard()); - if (player.chooseTarget(outcome, target, source, game)) { - for (UUID targetId : target.getTargets()) { - Card card = player.getGraveyard().get(targetId, game); - if (card != null) { - card.moveToZone(Zone.EXILED, source, game, false); - } - } - } + if (player == null || player.getGraveyard().isEmpty()) { + return false; + } + TargetCardInYourGraveyard target = new TargetCardInYourGraveyard(Math.min( + amount, player.getGraveyard().size() + ), StaticFilters.FILTER_CARD); + target.setNotTarget(true); + if (!player.chooseTarget(outcome, target, source, game)) { return true; } - return false; + Cards cards = new CardsImpl(); + for (UUID targetId : target.getTargets()) { + cards.add(player.getGraveyard().get(targetId, game)); + } + return player.moveCards(cards, Zone.EXILED, source, game); } private String setText() { @@ -63,5 +67,4 @@ private String setText() { sb.append("from your graveyard"); return sb.toString(); } - } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileCardsFromTopOfLibraryTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileCardsFromTopOfLibraryTargetEffect.java index d51a986955d3..358d96171c56 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileCardsFromTopOfLibraryTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileCardsFromTopOfLibraryTargetEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileFromZoneTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileFromZoneTargetEffect.java index 8f4aed65f264..ef354c75f446 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileFromZoneTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileFromZoneTargetEffect.java @@ -1,14 +1,16 @@ package mage.abilities.effects.common; -import java.util.Locale; -import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; +import mage.abilities.Mode; import mage.abilities.effects.OneShotEffect; +import mage.cards.Cards; import mage.cards.CardsImpl; import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.Target; @@ -17,62 +19,65 @@ import mage.util.CardUtil; /** - * * @author BetaSteward_at_googlemail.com */ public class ExileFromZoneTargetEffect extends OneShotEffect { - private Zone zone; - private FilterCard filter; - private UUID exileId; - private String exileName; - private int amount; + private final Zone zone; + private final FilterCard filter; + private final int amount; + private final boolean withSource; - public ExileFromZoneTargetEffect(Zone zone, UUID exileId, String exileName, FilterCard filter) { - this(zone, exileId, exileName, filter, 1); + public ExileFromZoneTargetEffect(Zone zone, boolean withSource) { + this(zone, StaticFilters.FILTER_CARD, withSource); } - public ExileFromZoneTargetEffect(Zone zone, UUID exileId, String exileName, FilterCard filter, int amount) { + public ExileFromZoneTargetEffect(Zone zone, FilterCard filter, boolean withSource) { + this(zone, filter, 1, withSource); + } + + public ExileFromZoneTargetEffect(Zone zone, FilterCard filter, int amount, boolean withSource) { super(Outcome.Exile); this.zone = zone; this.filter = filter; - this.exileId = exileId; - this.exileName = exileName; this.amount = amount; - setText(); + this.withSource = withSource; } public ExileFromZoneTargetEffect(final ExileFromZoneTargetEffect effect) { super(effect); this.zone = effect.zone; this.filter = effect.filter.copy(); - this.exileId = effect.exileId; - this.exileName = effect.exileName; this.amount = effect.amount; + this.withSource = effect.withSource; } @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(targetPointer.getFirst(game, source)); - if (player != null) { - Target target = null; - switch (zone) { - case HAND: - target = new TargetCardInHand(Math.min(player.getHand().count(filter, game), amount), filter); - break; - case GRAVEYARD: - target = new TargetCardInYourGraveyard(Math.min(player.getGraveyard().count(filter, game), amount), filter); - break; - default: - } - if (target != null && target.canChoose(source.getSourceId(), player.getId(), game)) { - if (target.chooseTarget(Outcome.Exile, player.getId(), source, game)) { - player.moveCardsToExile(new CardsImpl(target.getTargets()).getCards(game), source, game, true, exileId, exileName); - } - } + MageObject mageObject = source.getSourceObject(game); + if (player == null) { + return false; + } + Target target = null; + switch (zone) { + case HAND: + target = new TargetCardInHand(Math.min(player.getHand().count(filter, game), amount), filter); + break; + case GRAVEYARD: + target = new TargetCardInYourGraveyard(Math.min(player.getGraveyard().count(filter, game), amount), filter); + break; + default: + } + if (target == null || !target.canChoose(source.getSourceId(), player.getId(), game)) { return true; } - return false; + target.chooseTarget(Outcome.Exile, player.getId(), source, game); + Cards cards = new CardsImpl(target.getTargets()); + if (withSource) { + return player.moveCardsToExile(cards.getCards(game), source, game, true, CardUtil.getExileZoneId(game, source), mageObject.getName()); + } + return player.moveCards(cards, Zone.EXILED, source, game); } @Override @@ -80,7 +85,10 @@ public ExileFromZoneTargetEffect copy() { return new ExileFromZoneTargetEffect(this); } - private void setText() { - staticText = "target player exiles " + CardUtil.numberToText(amount, "a") + ' ' + filter.getMessage() + " from their " + zone.toString().toLowerCase(Locale.ENGLISH); + @Override + public String getText(Mode mode) { + return "target " + (mode.getTargets().isEmpty() ? "player" : mode.getTargets().get(0).getTargetName()) + + " exiles " + CardUtil.numberToText(amount, "a") + ' ' + filter.getMessage() + + " from their " + zone.toString().toLowerCase(); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileSpellEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileSpellEffect.java index f828e713ef89..d110f15104a7 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileSpellEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileSpellEffect.java @@ -1,9 +1,7 @@ package mage.abilities.effects.common; import mage.abilities.Ability; -import mage.abilities.MageSingleton; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; @@ -13,37 +11,29 @@ /** * @author BetaSteward_at_googlemail.com */ -public class ExileSpellEffect extends OneShotEffect implements MageSingleton { +public class ExileSpellEffect extends OneShotEffect { - private static final ExileSpellEffect instance = new ExileSpellEffect(); - - public static ExileSpellEffect getInstance() { - return instance; - } - - private ExileSpellEffect() { + public ExileSpellEffect() { super(Outcome.Exile); staticText = "Exile {this}"; } + private ExileSpellEffect(final ExileSpellEffect effect) { + super(effect); + } + @Override public ExileSpellEffect copy() { - return instance; + return new ExileSpellEffect(); } @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Spell spell = game.getStack().getSpell(source.getId()); - if (spell != null && !spell.isCopy()) { - Card spellCard = spell.getCard(); - if (spellCard != null) { - controller.moveCards(spellCard, Zone.EXILED, source, game); - } - } + Spell spell = game.getStack().getSpell(source.getId()); + if (controller == null || spell == null) { return true; } - return false; + return controller.moveCards(spell.getCard(), Zone.EXILED, source, game); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileTargetAndSearchGraveyardHandLibraryEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileTargetAndSearchGraveyardHandLibraryEffect.java index fa2717c47760..811987d0cae3 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileTargetAndSearchGraveyardHandLibraryEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileTargetAndSearchGraveyardHandLibraryEffect.java @@ -1,17 +1,18 @@ - package mage.abilities.effects.common; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.common.search.SearchTargetGraveyardHandLibraryForCardNameAndExileEffect; +import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.Target; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public class ExileTargetAndSearchGraveyardHandLibraryEffect extends SearchTargetGraveyardHandLibraryForCardNameAndExileEffect { @@ -20,7 +21,7 @@ public ExileTargetAndSearchGraveyardHandLibraryEffect(boolean graveyardExileOpti super(graveyardExileOptional, searchWhatText, searchForText); } - public ExileTargetAndSearchGraveyardHandLibraryEffect(final ExileTargetAndSearchGraveyardHandLibraryEffect effect) { + private ExileTargetAndSearchGraveyardHandLibraryEffect(final ExileTargetAndSearchGraveyardHandLibraryEffect effect) { super(effect); } @@ -36,13 +37,15 @@ public boolean apply(Game game, Ability source) { break; } } - if (exileTarget != null) { - Permanent permanentToExile = game.getPermanent(exileTarget.getFirstTarget()); - if (permanentToExile != null) { - targetPlayerId = permanentToExile.getControllerId(); - result = permanentToExile.moveToExile(null, "", source, game); - this.applySearchAndExile(game, source, permanentToExile.getName(), targetPlayerId); - } + Player player = game.getPlayer(source.getControllerId()); + if (player == null || exileTarget == null) { + return result; + } + Permanent permanentToExile = game.getPermanent(exileTarget.getFirstTarget()); + if (permanentToExile != null) { + targetPlayerId = permanentToExile.getControllerId(); + result = player.moveCards(permanentToExile, Zone.EXILED, source, game); + this.applySearchAndExile(game, source, permanentToExile.getName(), targetPlayerId); } return result; diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileUntilSourceLeavesEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileUntilSourceLeavesEffect.java index 22a73d8e0e91..fb0acb195feb 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileUntilSourceLeavesEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileUntilSourceLeavesEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/effects/common/GainActivatedAbilitiesOfTopCardEffect.java b/Mage/src/main/java/mage/abilities/effects/common/GainActivatedAbilitiesOfTopCardEffect.java index 2ea7fe132cee..decdcd9ac5e9 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/GainActivatedAbilitiesOfTopCardEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/GainActivatedAbilitiesOfTopCardEffect.java @@ -38,7 +38,7 @@ public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { Card card = player.getLibrary().getFromTop(game); - if (card != null && filter.match(card, game)) { + if (filter.match(card, game)) { Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null) { for (Ability ability : card.getAbilities(game)) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/LearnEffect.java b/Mage/src/main/java/mage/abilities/effects/common/LearnEffect.java new file mode 100644 index 000000000000..385f25e2bb50 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/LearnEffect.java @@ -0,0 +1,57 @@ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * @author TheElk801 + */ +public class LearnEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterCard("Lesson card"); + + static { + filter.add(SubType.LESSON.getPredicate()); + } + + private static final String defaultText = "learn. (You may reveal a Lesson card you own from outside the game " + + "and put it into your hand, or discard a card to draw a card.)"; + + public LearnEffect() { + super(Outcome.Neutral); + staticText = defaultText; + } + + private LearnEffect(final LearnEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + if (game.replaceEvent(GameEvent.getEvent( + GameEvent.EventType.LEARN, source.getSourceId(), + source, source.getControllerId() + ))) { + return false; + } + return new WishEffect(filter, true).apply(game, source) + || new DoIfCostPaid( + new DrawCardSourceControllerEffect(1), new DiscardCardCost() + ).apply(game, source); + } + + @Override + public LearnEffect copy() { + return new LearnEffect(this); + } + + public static String getDefaultText() { + return defaultText; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/LookLibraryAndPickControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/LookLibraryAndPickControllerEffect.java index 9dca85f40648..3427d5c3e612 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/LookLibraryAndPickControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/LookLibraryAndPickControllerEffect.java @@ -233,20 +233,20 @@ protected void actionWithSelectedCards(Cards cards, Game game, Ability source) { } private String getMayText() { - StringBuilder sb = new StringBuilder("Do you wish to "); + StringBuilder sb = new StringBuilder(); switch (targetPickedCards) { case HAND: if (revealPickedCards) { - sb.append("reveal ").append(filter.getMessage()).append(" and put into your hand"); + sb.append("Reveal ").append(filter.getMessage()).append(" and put into your hand"); } else { - sb.append("put ").append(filter.getMessage()).append(" into your hand"); + sb.append("Put ").append(filter.getMessage()).append(" into your hand"); } break; case BATTLEFIELD: - sb.append("put ").append(filter.getMessage()).append(" onto the battlefield"); + sb.append("Put ").append(filter.getMessage()).append(" onto the battlefield"); break; case GRAVEYARD: - sb.append("put ").append(filter.getMessage()).append(" into your graveyard"); + sb.append("Put ").append(filter.getMessage()).append(" into your graveyard"); break; } return sb.append('?').toString(); diff --git a/Mage/src/main/java/mage/abilities/effects/common/LookLibraryControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/LookLibraryControllerEffect.java index fdacd31698ff..020f3f167315 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/LookLibraryControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/LookLibraryControllerEffect.java @@ -195,7 +195,7 @@ public String setText(Mode mode, String middleText) { } } if (this.mayShuffleAfter) { - sb.append(". You may shuffle your library"); + sb.append(". You may shuffle"); } return sb.toString(); diff --git a/Mage/src/main/java/mage/abilities/effects/common/LookLibraryMayPutToBottomEffect.java b/Mage/src/main/java/mage/abilities/effects/common/LookLibraryMayPutToBottomEffect.java index 64829f47ca0b..45422fca4ba2 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/LookLibraryMayPutToBottomEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/LookLibraryMayPutToBottomEffect.java @@ -1,9 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - package mage.abilities.effects.common; import mage.MageObject; diff --git a/Mage/src/main/java/mage/abilities/effects/common/LookLibraryTopCardTargetPlayerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/LookLibraryTopCardTargetPlayerEffect.java index c45555bcbc07..996fcfb5e08a 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/LookLibraryTopCardTargetPlayerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/LookLibraryTopCardTargetPlayerEffect.java @@ -74,7 +74,7 @@ public boolean apply(Game game, Ability source) { player.lookAtCards(sourceObject.getIdName(), cards, game); if (putToGraveyard) { for (Card card : cards.getCards(game)) { - if (player.chooseUse(outcome, "Do you wish to put card into the player's graveyard?", source, game)) { + if (player.chooseUse(outcome, "Put that card into its owner's graveyard?", source, game)) { player.moveCardToGraveyardWithInfo(card, source, game, Zone.LIBRARY); } else { game.informPlayers(player.getLogName() + " puts the card back on top of the library."); @@ -82,7 +82,7 @@ public boolean apply(Game game, Ability source) { } } if (mayShuffleAfter) { - if (player.chooseUse(Outcome.Benefit, (player == targetPlayer ? "Shuffle your library?" : "Do you want the chosen player to shuffle their library?"), source, game)) { + if (player.chooseUse(Outcome.Benefit, (player == targetPlayer ? "Shuffle your library?" : "Do you want the chosen player to shuffle?"), source, game)) { targetPlayer.shuffleLibrary(source, game); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/MistmeadowWitchEffect.java b/Mage/src/main/java/mage/abilities/effects/common/MistmeadowWitchEffect.java index a3a5c682c888..66e98bc9b787 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/MistmeadowWitchEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/MistmeadowWitchEffect.java @@ -1,5 +1,6 @@ package mage.abilities.effects.common; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; @@ -7,6 +8,8 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.util.CardUtil; /** * Created by Eric on 9/24/2016. @@ -18,22 +21,21 @@ public MistmeadowWitchEffect() { staticText = "Exile target creature. Return that card to the battlefield under its owner's control at the beginning of the next end step"; } - public MistmeadowWitchEffect(final MistmeadowWitchEffect effect) { + private MistmeadowWitchEffect(final MistmeadowWitchEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent != null) { - if (permanent.moveToExile(source.getSourceId(), "Mistmeadow Witch Exile", source, game)) { - //create delayed triggered ability - AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnFromExileEffect(source.getSourceId(), Zone.BATTLEFIELD)); - game.addDelayedTriggeredAbility(delayedAbility, source); - return true; - } + MageObject sourceObject = source.getSourceObject(game); + if (player == null || permanent == null || sourceObject == null) { + return false; } - return false; + player.moveCardsToExile(permanent, source, game, true, CardUtil.getExileZoneId(game, source), sourceObject.getName()); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnFromExileEffect(Zone.BATTLEFIELD, "return the exiled card to the battlefield under its owner's control")), source); + return true; } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/OpponentsCantCastChosenUntilNextTurnEffect.java b/Mage/src/main/java/mage/abilities/effects/common/OpponentsCantCastChosenUntilNextTurnEffect.java new file mode 100644 index 000000000000..2eba8953dcda --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/OpponentsCantCastChosenUntilNextTurnEffect.java @@ -0,0 +1,55 @@ +package mage.abilities.effects.common; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.util.CardUtil; + +/** + * This effect must be used in tandem with ChooseACardNameEffect + */ +public class OpponentsCantCastChosenUntilNextTurnEffect extends ContinuousRuleModifyingEffectImpl { + + public OpponentsCantCastChosenUntilNextTurnEffect() { + super(Duration.UntilYourNextTurn, Outcome.Benefit); + staticText = "Until your next turn, your opponents can't cast spells with the chosen name"; + } + + public OpponentsCantCastChosenUntilNextTurnEffect(final OpponentsCantCastChosenUntilNextTurnEffect effect) { + super(effect); + } + + @Override + public OpponentsCantCastChosenUntilNextTurnEffect copy() { + return new OpponentsCantCastChosenUntilNextTurnEffect(this); + } + + @Override + public String getInfoMessage(Ability source, GameEvent event, Game game) { + MageObject mageObject = game.getObject(source.getSourceId()); + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); + if (mageObject != null && cardName != null) { + return "You can't cast a card named " + cardName + " (" + mageObject.getIdName() + ")."; + } + return null; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.CAST_SPELL_LATE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); + if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { + MageObject object = game.getObject(event.getSourceId()); + return object != null && CardUtil.haveSameNames(object, cardName, game); + } + return false; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/PermanentsEnterBattlefieldTappedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PermanentsEnterBattlefieldTappedEffect.java index a9b0879594b4..65e815fdd312 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PermanentsEnterBattlefieldTappedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PermanentsEnterBattlefieldTappedEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import mage.abilities.Ability; @@ -60,7 +55,7 @@ public boolean checksEventType(GameEvent event, Game game) { public boolean applies(GameEvent event, Ability source, Game game) { Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); - return permanent != null && filter.match(permanent, source.getSourceId(), event.getPlayerId(), game); + return filter.match(permanent, source.getSourceId(), event.getPlayerId(), game); } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/PhaseOutSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PhaseOutSourceEffect.java index 3672fd66b1e7..d1669ad7a0ee 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PhaseOutSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PhaseOutSourceEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/effects/common/PhaseOutTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PhaseOutTargetEffect.java index 79dce240e23b..41d86f6f64e7 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PhaseOutTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PhaseOutTargetEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import java.util.UUID; diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageByAllPermanentsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageByAllPermanentsEffect.java index 24922cae10c4..58d7564d4de7 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageByAllPermanentsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageByAllPermanentsEffect.java @@ -55,7 +55,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { return true; } Permanent permanent = game.getPermanent(damageEvent.getSourceId()); - if (permanent != null && filter.match(permanent, source.getSourceId(), source.getControllerId(), game)) { + if (filter.match(permanent, source.getSourceId(), source.getControllerId(), game)) { return true; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventDamageByTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventDamageByTargetEffect.java index 772b9ad0ade2..fed07057b687 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PreventDamageByTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PreventDamageByTargetEffect.java @@ -46,7 +46,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { if (!this.used && super.applies(event, source, game)) { MageObject mageObject = game.getObject(event.getSourceId()); if (mageObject != null - && (mageObject.isInstant() || mageObject.isSorcery())) { + && mageObject.isInstantOrSorcery()) { for (Target target : source.getTargets()) { if (target instanceof TargetSpell) { if (((TargetSpell) target).getSourceIds().contains(event.getSourceId())) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutOnLibrarySourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutOnLibrarySourceEffect.java index f1ec5d8c0365..68439c1e4861 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PutOnLibrarySourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PutOnLibrarySourceEffect.java @@ -1,4 +1,3 @@ - package mage.abilities.effects.common; import mage.MageObject; @@ -7,19 +6,16 @@ import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.players.Player; /** - * * @author LevelX2 */ public class PutOnLibrarySourceEffect extends OneShotEffect { - boolean onTop; + private final boolean onTop; public PutOnLibrarySourceEffect(boolean onTop) { super(Outcome.ReturnToHand); @@ -44,22 +40,15 @@ public PutOnLibrarySourceEffect copy() { @Override public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); MageObject sourceObject = source.getSourceObjectIfItStillExists(game); - if (sourceObject == null) { + if (player == null || !(sourceObject instanceof Card)) { return false; } - if (sourceObject instanceof Permanent) { - ((Permanent) sourceObject).moveToZone(Zone.LIBRARY, source, game, onTop); - return true; - } else if (sourceObject instanceof Card && game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD) { - for (Player player : game.getPlayers().values()) { - if (player.getGraveyard().contains(sourceObject.getId())) { - ((Card) sourceObject).moveToZone(Zone.LIBRARY, source, game, onTop); - return true; - } - } + if (onTop) { + return player.putCardsOnTopOfLibrary((Card) sourceObject, game, source, false); } - return false; + return player.putCardsOnBottomOfLibrary((Card) sourceObject, game, source, false); } @Override @@ -73,6 +62,5 @@ public String getText(Mode mode) { sb.append(onTop ? "top" : "the bottom").append(" of its owner's library"); } return sb.toString(); - } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutOnLibraryTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutOnLibraryTargetEffect.java index 8f1455f2ab2d..e4746b0dd7bd 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PutOnLibraryTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PutOnLibraryTargetEffect.java @@ -12,6 +12,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; +import mage.target.common.TargetCardInYourGraveyard; import mage.util.CardUtil; import java.util.ArrayList; @@ -138,7 +139,7 @@ public String getText(Mode mode) { sb.append(CardUtil.numberToText(target.getMaxNumberOfTargets())).append(' '); } sb.append("target ").append(mode.getTargets().get(0).getTargetName()).append(" on "); - sb.append(onTop ? "top" : "the bottom").append(" of its owner's library"); + sb.append(onTop ? "top" : "the bottom").append(" of ").append(mode.getTargets().get(0) instanceof TargetCardInYourGraveyard ? "your" : "its owner's").append(" library"); return sb.toString(); diff --git a/Mage/src/main/java/mage/abilities/effects/common/RecruiterEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RecruiterEffect.java index 1327e855497a..f72d478fec6e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/RecruiterEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/RecruiterEffect.java @@ -60,7 +60,7 @@ public String getText(Mode mode) { if (staticText != null && !staticText.isEmpty()) { return staticText; } - return "search your library for any number of " + filter.getMessage() - + " and reveal those cards. Shuffle your library, then put them on top of it in any order."; + return "search your library for any number of " + + filter.getMessage() + ", reveal them, then shuffle and put those cards on top in any order"; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/RedirectDamageFromSourceToTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RedirectDamageFromSourceToTargetEffect.java index 76e56084f800..793896620f9b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/RedirectDamageFromSourceToTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/RedirectDamageFromSourceToTargetEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/effects/common/ReplaceOpponentCardsInHandWithSelectedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ReplaceOpponentCardsInHandWithSelectedEffect.java index af77e9b1c3ec..d59e0422bbac 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ReplaceOpponentCardsInHandWithSelectedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ReplaceOpponentCardsInHandWithSelectedEffect.java @@ -21,7 +21,7 @@ public class ReplaceOpponentCardsInHandWithSelectedEffect extends OneShotEffect public ReplaceOpponentCardsInHandWithSelectedEffect() { super(Outcome.Detriment); - this.staticText = "Target opponent puts the cards from their hand on top of their library. Search that player's library for that many cards. The player puts those cards into their hand, then shuffles their library."; + this.staticText = "Target opponent puts the cards from their hand on top of their library. Search that player's library for that many cards. The player puts those cards into their hand, then shuffles."; } public ReplaceOpponentCardsInHandWithSelectedEffect(final ReplaceOpponentCardsInHandWithSelectedEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/ReturnFromExileEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ReturnFromExileEffect.java index 8cceded80f7d..b3135a365821 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ReturnFromExileEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ReturnFromExileEffect.java @@ -1,7 +1,6 @@ package mage.abilities.effects.common; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; @@ -9,29 +8,27 @@ import mage.game.ExileZone; import mage.game.Game; import mage.players.Player; +import mage.util.CardUtil; /** - * * @author BetaSteward_at_googlemail.com */ public class ReturnFromExileEffect extends OneShotEffect { - private UUID exileId; - private Zone zone; - private boolean tapped; + private final Zone zone; + private final boolean tapped; - public ReturnFromExileEffect(UUID exileId, Zone zone) { - this(exileId, zone, false); + public ReturnFromExileEffect(Zone zone) { + this(zone, false); } - public ReturnFromExileEffect(UUID exileId, Zone zone, String text) { - this(exileId, zone, false); + public ReturnFromExileEffect(Zone zone, String text) { + this(zone, false); staticText = text; } - public ReturnFromExileEffect(UUID exileId, Zone zone, boolean tapped) { + public ReturnFromExileEffect(Zone zone, boolean tapped) { super(Outcome.PutCardInPlay); - this.exileId = exileId; this.zone = zone; this.tapped = tapped; setText(); @@ -39,7 +36,6 @@ public ReturnFromExileEffect(UUID exileId, Zone zone, boolean tapped) { public ReturnFromExileEffect(final ReturnFromExileEffect effect) { super(effect); - this.exileId = effect.exileId; this.zone = effect.zone; this.tapped = effect.tapped; } @@ -51,19 +47,15 @@ public ReturnFromExileEffect copy() { @Override public boolean apply(Game game, Ability source) { - ExileZone exile = game.getExile().getExileZone(exileId); + ExileZone exile = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); Player controller = game.getPlayer(source.getControllerId()); - if (controller != null && exile != null) { - switch (zone) { - case LIBRARY: - controller.putCardsOnTopOfLibrary(exile, game, source, false); - break; - default: - controller.moveCards(exile.getCards(game), zone, source, game, tapped, false, true, null); - } - return true; + if (controller == null || exile == null) { + return false; } - return false; + if (zone == Zone.LIBRARY) { + return controller.putCardsOnTopOfLibrary(exile, game, source, false); + } + return controller.moveCards(exile.getCards(game), zone, source, game, tapped, false, true, null); } private void setText() { diff --git a/Mage/src/main/java/mage/abilities/effects/common/ReturnFromGraveyardToBattlefieldTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ReturnFromGraveyardToBattlefieldTargetEffect.java index b86191c51550..860bfef7ed28 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ReturnFromGraveyardToBattlefieldTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ReturnFromGraveyardToBattlefieldTargetEffect.java @@ -9,6 +9,7 @@ import mage.game.Game; import mage.players.Player; import mage.target.Target; +import mage.target.common.TargetCardInYourGraveyard; import mage.util.CardUtil; import java.util.HashSet; @@ -20,27 +21,20 @@ */ public class ReturnFromGraveyardToBattlefieldTargetEffect extends OneShotEffect { - private boolean tapped; - private boolean showUnderControlText = false; + private final boolean tapped; public ReturnFromGraveyardToBattlefieldTargetEffect() { this(false); } public ReturnFromGraveyardToBattlefieldTargetEffect(boolean tapped) { - this(tapped, true); - } - - public ReturnFromGraveyardToBattlefieldTargetEffect(boolean tapped, boolean showUnderControlText) { super(Outcome.PutCreatureInPlay); this.tapped = tapped; - this.showUnderControlText = showUnderControlText; } - public ReturnFromGraveyardToBattlefieldTargetEffect(final ReturnFromGraveyardToBattlefieldTargetEffect effect) { + protected ReturnFromGraveyardToBattlefieldTargetEffect(final ReturnFromGraveyardToBattlefieldTargetEffect effect) { super(effect); this.tapped = effect.tapped; - this.showUnderControlText = effect.showUnderControlText; } @Override @@ -71,24 +65,27 @@ public String getText(Mode mode) { return staticText; } StringBuilder sb = new StringBuilder(); - + boolean yourGrave = !mode.getTargets().isEmpty() + && mode.getTargets().get(0) instanceof TargetCardInYourGraveyard; + sb.append(yourGrave ? "return " : "put "); if (mode.getTargets().isEmpty()) { - sb.append("return target creature to the battlefield"); + sb.append("target creature"); } else { Target target = mode.getTargets().get(0); - sb.append("return "); if (target.getMaxNumberOfTargets() > 1) { if (target.getMaxNumberOfTargets() != target.getNumberOfTargets()) { sb.append("up to "); } sb.append(CardUtil.numberToText(target.getMaxNumberOfTargets())).append(' '); } - sb.append("target ").append(mode.getTargets().get(0).getTargetName()).append(" to the battlefield"); - if (tapped) { - sb.append(" tapped"); - } + sb.append("target ").append(mode.getTargets().get(0).getTargetName()); + } + sb.append(yourGrave ? " to" : " onto"); + sb.append(" the battlefield"); + if (tapped) { + sb.append(" tapped"); } - if (showUnderControlText) { + if (!yourGrave) { sb.append(" under your control"); } return sb.toString(); diff --git a/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandAttachedEffect.java index 8b3d1573eaed..610f3a05cfd4 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandAttachedEffect.java @@ -1,15 +1,15 @@ package mage.abilities.effects.common; -import mage.constants.Outcome; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; +import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; /** - * * @author jeff */ public class ReturnToHandAttachedEffect extends OneShotEffect { @@ -30,17 +30,15 @@ public ReturnToHandAttachedEffect copy() { @Override public boolean apply(Game game, Ability source) { - Object object = getValue("attachedTo"); - if (object instanceof Permanent) { - Card card = game.getCard(((Permanent) object).getId()); - if (card != null - && getValue("zcc").equals(game.getState().getZoneChangeCounter(card.getId()))) { // Necrogenesis, etc. - if (card.moveToZone(Zone.HAND, source, game, false)) { - return true; - } - } + Player player = game.getPlayer(source.getSourceId()); + Permanent permanent = (Permanent) getValue("attachedTo"); + if (player == null || permanent == null) { + return false; } - return false; + Card card = permanent.getMainCard(); + if (permanent.getZoneChangeCounter(game) != card.getZoneChangeCounter(game) + 1) { + return false; + } + return player.moveCards(card, Zone.HAND, source, game); } - } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandFromGraveyardAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandFromGraveyardAllEffect.java index 9d89562071fa..33b9c52c2d01 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandFromGraveyardAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandFromGraveyardAllEffect.java @@ -1,53 +1,89 @@ package mage.abilities.effects.common; -import java.util.UUID; import mage.abilities.Ability; +import mage.abilities.Mode; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; +import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterCard; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author LevelX2 */ public class ReturnToHandFromGraveyardAllEffect extends OneShotEffect { private final FilterCard filter; + private final TargetController targetController; public ReturnToHandFromGraveyardAllEffect(FilterCard filter) { + this(filter, TargetController.EACH_PLAYER); + } + + public ReturnToHandFromGraveyardAllEffect(FilterCard filter, TargetController targetController) { super(Outcome.ReturnToHand); this.filter = filter; - staticText = "Each player returns all " + filter.getMessage() + " from their graveyard to their hand"; + this.targetController = targetController; } - public ReturnToHandFromGraveyardAllEffect(final ReturnToHandFromGraveyardAllEffect effect) { + private ReturnToHandFromGraveyardAllEffect(final ReturnToHandFromGraveyardAllEffect effect) { super(effect); this.filter = effect.filter; + this.targetController = effect.targetController; } @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - player.moveCards(player.getGraveyard() - .getCards(filter, source.getSourceId(), player.getId(), game), - Zone.HAND, source, game); - } - + if (controller == null) { + return false; + } + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + switch (targetController) { + case ANY: + case EACH_PLAYER: + break; + case OPPONENT: + if (!controller.hasOpponent(playerId, game)) { + continue; + } + case YOU: + if (!controller.getId().equals(playerId)) { + continue; + } } - return true; + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + player.moveCards(player.getGraveyard().getCards( + filter, source.getSourceId(), player.getId(), game + ), Zone.HAND, source, game); } - return false; + return true; } @Override public ReturnToHandFromGraveyardAllEffect copy() { return new ReturnToHandFromGraveyardAllEffect(this); } + + @Override + public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } + switch (targetController) { + case OPPONENT: + return "each opponent returns each " + filter.getMessage() + " from their graveyard to their hand"; + case YOU: + return "return each " + filter.getMessage() + " from your graveyard to your hand"; + default: + return "each player returns each " + filter.getMessage() + " from their graveyard to their hand"; + } + } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ReturnToLibrarySpellEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ReturnToLibrarySpellEffect.java index ee660ee855c0..b1df8205ab50 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ReturnToLibrarySpellEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ReturnToLibrarySpellEffect.java @@ -11,7 +11,6 @@ import mage.players.Player; /** - * * @author LevelX2 */ public class ReturnToLibrarySpellEffect extends OneShotEffect { @@ -20,7 +19,7 @@ public class ReturnToLibrarySpellEffect extends OneShotEffect { public ReturnToLibrarySpellEffect(boolean top) { super(Outcome.Neutral); - staticText = "Put {this} on "+ (top ? "top":"the bottom") + " of its owner's library"; + staticText = "Put {this} on " + (top ? "top" : "the bottom") + " of its owner's library"; this.toTop = top; } @@ -35,7 +34,7 @@ public boolean apply(Game game, Ability source) { if (controller != null) { Spell spell = game.getStack().getSpell(source.getSourceId()); if (spell != null) { - Card spellCard = game.getStack().getSpell(source.getSourceId()).getCard(); + Card spellCard = spell.getCard(); if (spellCard != null) { controller.moveCardToLibraryWithInfo(spellCard, source, game, Zone.STACK, toTop, true); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/RevealCardsFromLibraryUntilEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RevealCardsFromLibraryUntilEffect.java index 533c336d4e04..dddb343176c9 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/RevealCardsFromLibraryUntilEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/RevealCardsFromLibraryUntilEffect.java @@ -72,7 +72,7 @@ public boolean apply(Game game, Ability source) { if (card != null) { cards.add(card); } - } while (library.hasCards() && card != null && !filter.match(card, game)); + } while (library.hasCards() && !filter.match(card, game)); // reveal cards if (!cards.isEmpty()) { controller.revealCards(sourceObject.getIdName(), cards, game); diff --git a/Mage/src/main/java/mage/abilities/effects/common/RevealTargetPlayerLibraryEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RevealTargetPlayerLibraryEffect.java deleted file mode 100644 index afb199ca969a..000000000000 --- a/Mage/src/main/java/mage/abilities/effects/common/RevealTargetPlayerLibraryEffect.java +++ /dev/null @@ -1,70 +0,0 @@ - -package mage.abilities.effects.common; - -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Cards; -import mage.cards.CardsImpl; -import mage.constants.Outcome; -import mage.game.Game; -import mage.players.Player; -import mage.util.CardUtil; - -/** - * - * @author Luna Skyrise - */ -public class RevealTargetPlayerLibraryEffect extends OneShotEffect { - - private DynamicValue amountCards; - - public RevealTargetPlayerLibraryEffect(int amountCards) { - this(StaticValue.get(amountCards)); - } - - public RevealTargetPlayerLibraryEffect(DynamicValue amountCards) { - super(Outcome.Neutral); - this.amountCards = amountCards; - this.staticText = setText(); - } - - public RevealTargetPlayerLibraryEffect(final RevealTargetPlayerLibraryEffect effect) { - super(effect); - this.amountCards = effect.amountCards; - } - - @Override - public RevealTargetPlayerLibraryEffect copy() { - return new RevealTargetPlayerLibraryEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - Player targetPlayer = game.getPlayer(source.getFirstTarget()); - MageObject sourceObject = source.getSourceObject(game); - if (player == null || targetPlayer == null || sourceObject == null) { - return false; - } - - Cards cards = new CardsImpl(); - cards.addAll(player.getLibrary().getTopCards(game, amountCards.calculate(game, source, this))); - player.revealCards(sourceObject.getIdName() + " - Top " + amountCards.toString() + "cards of " + targetPlayer.getName() + "\'s library", cards, game); - return true; - } - - private String setText() { - String number = amountCards.toString(); - StringBuilder sb = new StringBuilder("Reveal the top "); - if ("1".equals(number)) { - sb.append("card"); - } else { - sb.append(CardUtil.numberToText(number)).append(" cards"); - } - sb.append(" of target player's library."); - return sb.toString(); - } -} diff --git a/Mage/src/main/java/mage/abilities/effects/common/SacrificeEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SacrificeEffect.java index 01ae1dd17026..9115e6ba862a 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/SacrificeEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/SacrificeEffect.java @@ -98,7 +98,9 @@ private void setText() { sb.append(" sacrifice "); } } - if (!filter.getMessage().startsWith("another")) { + if (!filter.getMessage().startsWith("another") + && !filter.getMessage().startsWith("a ") + && !filter.getMessage().startsWith("an ")) { sb.append(CardUtil.numberToText(count.toString(), "a")).append(' '); } sb.append(filter.getMessage()); diff --git a/Mage/src/main/java/mage/abilities/effects/common/SacrificeOpponentsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SacrificeOpponentsEffect.java index f866c26fc76a..279f55625476 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/SacrificeOpponentsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/SacrificeOpponentsEffect.java @@ -1,8 +1,5 @@ package mage.abilities.effects.common; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; @@ -16,6 +13,10 @@ import mage.target.TargetPermanent; import mage.util.CardUtil; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + /** * All opponents have to sacrifice [amount] permanents that match the [filter]. * @@ -81,20 +82,16 @@ public boolean apply(Game game, Ability source) { private void setText() { StringBuilder sb = new StringBuilder(); sb.append("each opponent sacrifices "); - if (amount.toString().equals("X")) { - sb.append(amount.toString()); - } else { - if (amount.toString().equals("1")) { - if (!filter.getMessage().startsWith("a ") && !filter.getMessage().startsWith("an ")) { - sb.append('a'); - } - } else { - sb.append(CardUtil.numberToText(amount.toString())); - } + switch (amount.toString()) { + case "X": + sb.append(amount.toString()).append(' '); + break; + case "1": + sb.append(CardUtil.addArticle(filter.getMessage())); + break; + default: + sb.append(CardUtil.numberToText(amount.toString())).append(' ').append(filter.getMessage()); } - sb.append(' '); - sb.append(filter.getMessage()); staticText = sb.toString(); } - } diff --git a/Mage/src/main/java/mage/abilities/effects/common/SacrificeOpponentsUnlessPayEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SacrificeOpponentsUnlessPayEffect.java index 11998312f63e..f7d199659afd 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/SacrificeOpponentsUnlessPayEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/SacrificeOpponentsUnlessPayEffect.java @@ -113,12 +113,11 @@ public boolean apply(Game game, Ability source) { costToPay = ManaUtil.createManaCost(genericMana, game, source, this); costValueMessage = "{" + genericMana.calculate(game, source, this) + "}"; } - String message; + String message = ""; if (costToPay instanceof ManaCost) { - message = "Would you like to pay " + costValueMessage + " to prevent sacrifice effect?"; - } else { - message = costValueMessage + " to prevent sacrifice effect?"; + message += "Pay "; } + message += costValueMessage + '?'; costToPay.clearPaid(); if (!(player.chooseUse(Outcome.Benefit, message, source, game) diff --git a/Mage/src/main/java/mage/abilities/effects/common/SacrificeSourceUnlessPaysEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SacrificeSourceUnlessPaysEffect.java index 1ac7974e8ad1..488c627d0970 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/SacrificeSourceUnlessPaysEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/SacrificeSourceUnlessPaysEffect.java @@ -58,12 +58,11 @@ public boolean apply(Game game, Ability source) { costToPay = ManaUtil.createManaCost(genericMana, game, source, this); costValueMessage = "{" + genericMana.calculate(game, source, this) + "}"; } - String message; + String message = ""; if (costToPay instanceof ManaCost) { - message = "Would you like to pay " + costValueMessage + " to prevent sacrifice effect?"; - } else { - message = costValueMessage + " to prevent sacrifice effect?"; + message += "Pay "; } + message += costValueMessage + '?'; costToPay.clearPaid(); if (costToPay.canPay(source, source, source.getControllerId(), game) diff --git a/Mage/src/main/java/mage/abilities/effects/common/SetPlayerLifeAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SetPlayerLifeAllEffect.java index cb691a322ad4..d3ce6830ff1d 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/SetPlayerLifeAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/SetPlayerLifeAllEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import java.util.UUID; diff --git a/Mage/src/main/java/mage/abilities/effects/common/SetPlayerLifeSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SetPlayerLifeSourceEffect.java index 17ca7f5981a9..0f2ee5ba194d 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/SetPlayerLifeSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/SetPlayerLifeSourceEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/effects/common/ShuffleIntoLibraryTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ShuffleIntoLibraryTargetEffect.java index 1ff0f8b936ed..f75be6580bc5 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ShuffleIntoLibraryTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ShuffleIntoLibraryTargetEffect.java @@ -42,7 +42,7 @@ public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (cardObject != null && controller != null && cardObject instanceof Card) { if (!optional - || controller.chooseUse(Outcome.Benefit, "Do you wish to shuffle " + cardObject.getIdName() + " into " + || controller.chooseUse(Outcome.Benefit, "Shuffle " + cardObject.getIdName() + " into " + (((Card) cardObject).getOwnerId().equals(source.getControllerId()) ? "your" : "its owners") + " library?", source, game)) { Player owner = game.getPlayer(((Card) cardObject).getOwnerId()); diff --git a/Mage/src/main/java/mage/abilities/effects/common/ShuffleSpellEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ShuffleSpellEffect.java index 105b9c7de994..933ba1be0cd6 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ShuffleSpellEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ShuffleSpellEffect.java @@ -1,4 +1,3 @@ - package mage.abilities.effects.common; import mage.abilities.Ability; @@ -11,7 +10,6 @@ import mage.players.Player; /** - * * @author nantuko */ public class ShuffleSpellEffect extends OneShotEffect implements MageSingleton { @@ -34,7 +32,7 @@ public boolean apply(Game game, Ability source) { // We have to use the spell id because in case of copied spells, the sourceId can be multiple times on the stack Spell spell = game.getStack().getSpell(source.getId()); if (spell != null) { - if (controller.moveCards(spell, Zone.LIBRARY, source, game) && !spell.isCopy()) { + if (controller.moveCards(spell, Zone.LIBRARY, source, game)) { Player owner = game.getPlayer(spell.getCard().getOwnerId()); if (owner != null) { owner.shuffleLibrary(source, game); diff --git a/Mage/src/main/java/mage/abilities/effects/common/SkipCombatStepEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SkipCombatStepEffect.java index dee8ec1f4503..3567a10406ab 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/SkipCombatStepEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/SkipCombatStepEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/effects/common/SkipNextCombatEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SkipNextCombatEffect.java index 20964c43aaec..d4fa52bbbb8d 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/SkipNextCombatEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/SkipNextCombatEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/effects/common/SkipNextDrawStepTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SkipNextDrawStepTargetEffect.java index 17dc0aba8adb..0e17f71d13f3 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/SkipNextDrawStepTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/SkipNextDrawStepTargetEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/effects/common/TargetPlayerShufflesTargetCardsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/TargetPlayerShufflesTargetCardsEffect.java new file mode 100644 index 000000000000..fa1c742c297a --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/TargetPlayerShufflesTargetCardsEffect.java @@ -0,0 +1,55 @@ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; +import mage.util.CardUtil; + +/** + * @author TheElk801 + */ +public class TargetPlayerShufflesTargetCardsEffect extends OneShotEffect { + + public TargetPlayerShufflesTargetCardsEffect() { + super(Outcome.Neutral); + } + + private TargetPlayerShufflesTargetCardsEffect(final TargetPlayerShufflesTargetCardsEffect effect) { + super(effect); + } + + @Override + public TargetPlayerShufflesTargetCardsEffect copy() { + return new TargetPlayerShufflesTargetCardsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player targetPlayer = game.getPlayer(source.getFirstTarget()); + Cards cards = new CardsImpl(source.getTargets().get(1).getTargets()); + if (targetPlayer != null && !cards.isEmpty()) { + return targetPlayer.shuffleCardsToLibrary(cards, game, source); + } + return false; + } + + @Override + public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } + String rule = "target player shuffles "; + int targetNumber = mode.getTargets().get(1).getMaxNumberOfTargets(); + if (targetNumber == Integer.MAX_VALUE) { + rule += "any number of target cards"; + } else { + rule += "up to " + CardUtil.numberToText(targetNumber, "one") + " target card" + (targetNumber > 1 ? "s" : ""); + } + return rule + " from their graveyard into their library"; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/TransformTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/TransformTargetEffect.java index e23df12250af..b128ae01081e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/TransformTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/TransformTargetEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/effects/common/UntapAllLandsControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/UntapAllLandsControllerEffect.java index 595fed8dc69e..f99eee146fae 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/UntapAllLandsControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/UntapAllLandsControllerEffect.java @@ -30,7 +30,7 @@ public UntapAllLandsControllerEffect(final UntapAllLandsControllerEffect effect) public UntapAllLandsControllerEffect(FilterLandPermanent filter) { super(Outcome.Untap); - staticText = "Untap all " + filter.getMessage() + " you control"; + staticText = "untap all " + filter.getMessage() + " you control"; this.filter = filter; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/WishEffect.java b/Mage/src/main/java/mage/abilities/effects/common/WishEffect.java index 24fda8d7c695..e40c35439c1d 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/WishEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/WishEffect.java @@ -27,10 +27,6 @@ public class WishEffect extends OneShotEffect { private final String choiceText; private final boolean topOfLibrary; - public WishEffect() { - this(new FilterCard()); - } - public WishEffect(FilterCard filter) { this(filter, true); } @@ -49,13 +45,27 @@ public WishEffect(FilterCard filter, boolean reveal, boolean alsoFromExile, bool this.alsoFromExile = alsoFromExile; this.reveal = reveal; this.topOfLibrary = topOfLibrary; - choiceText = (topOfLibrary ? "Put " : "Choose ") + filter.getMessage() + " you own from outside the game" - + (alsoFromExile ? " or in exile" : "") - + (reveal ? ", reveal that card," : "") - + (topOfLibrary ? " on top of your library." : " and put it into your hand."); + if (!reveal) { + choiceText = "Put a card you own from outside the game " + + (topOfLibrary ? "on top of your library." : "into your hand."); + } else { + choiceText = (topOfLibrary ? "Put " : "Reveal ") + filter.getMessage() + " you own from outside the game" + + (alsoFromExile ? " or choose " + makeExileText(filter) + + " you own in exile. Put that card into your hand." : " and put it into your hand."); + } staticText = "You may " + Character.toLowerCase(choiceText.charAt(0)) + choiceText.substring(1, choiceText.length() - 1); } + private static String makeExileText(FilterCard filter) { + String s = filter.getMessage(); + if (s.startsWith("a ")) { + return s.replace("a ", "a face-up "); + } else if (s.startsWith("an ")) { + return s.replace("an ", "a face-up "); + } + return "a face-up " + s; + } + public WishEffect(final WishEffect effect) { super(effect); this.filter = effect.filter; @@ -74,57 +84,58 @@ public WishEffect copy() { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = source.getSourceObject(game); - if (controller != null && sourceObject != null) { - if (controller.chooseUse(Outcome.Benefit, choiceText, source, game)) { - Cards cards = controller.getSideboard(); - List exile = game.getExile().getAllCards(game); - boolean noTargets = cards.isEmpty() && (!alsoFromExile || exile.isEmpty()); - if (noTargets) { - game.informPlayer(controller, "You have no cards outside the game" + (alsoFromExile ? " or in exile" : "") + '.'); - return true; - } + if (controller == null || sourceObject == null) { + return false; + } + if (!controller.chooseUse(Outcome.Benefit, choiceText, source, game)) { + return false; + } + Cards cards = controller.getSideboard(); + List exile = game.getExile().getAllCards(game); + boolean noTargets = cards.isEmpty() && (!alsoFromExile || exile.isEmpty()); + if (noTargets) { + game.informPlayer(controller, "You have no cards outside the game" + (alsoFromExile ? " or in exile" : "") + '.'); + return true; + } - Set filtered = cards.getCards(filter, game); - Cards filteredCards = new CardsImpl(); - for (Card card : filtered) { - filteredCards.add(card.getId()); - } - if (alsoFromExile) { - for (Card exileCard : exile) { - if (exileCard.isOwnedBy(source.getControllerId()) && filter.match(exileCard, game)) { - filteredCards.add(exileCard); - } - } - } - if (filteredCards.isEmpty()) { - game.informPlayer(controller, "You don't have " + filter.getMessage() + " outside the game" + (alsoFromExile ? " or in exile" : "") + '.'); - return true; + Set filtered = cards.getCards(filter, game); + Cards filteredCards = new CardsImpl(); + for (Card card : filtered) { + filteredCards.add(card.getId()); + } + if (alsoFromExile) { + for (Card exileCard : exile) { + if (exileCard.isOwnedBy(source.getControllerId()) && filter.match(exileCard, game)) { + filteredCards.add(exileCard); } + } + } + if (filteredCards.isEmpty()) { + game.informPlayer(controller, "You don't have " + filter.getMessage() + " outside the game" + (alsoFromExile ? " or in exile" : "") + '.'); + return true; + } - TargetCard target = new TargetCard(Zone.ALL, filter); - target.setNotTarget(true); - if (controller.choose(Outcome.Benefit, filteredCards, target, game)) { - Card card = controller.getSideboard().get(target.getFirstTarget(), game); - if (card == null && alsoFromExile) { - card = game.getCard(target.getFirstTarget()); - } - if (card != null) { - if (topOfLibrary) { - controller.putCardsOnTopOfLibrary(card, game, source, true); - } else { - card.moveToZone(Zone.HAND, source, game, false); - } - if (reveal) { - Cards revealCard = new CardsImpl(); - revealCard.add(card); - controller.revealCards(sourceObject.getIdName(), revealCard, game); - } - } + TargetCard target = new TargetCard(Zone.ALL, filter); + target.setNotTarget(true); + if (controller.choose(Outcome.Benefit, filteredCards, target, game)) { + Card card = controller.getSideboard().get(target.getFirstTarget(), game); + if (card == null && alsoFromExile) { + card = game.getCard(target.getFirstTarget()); + } + if (card != null) { + if (topOfLibrary) { + controller.putCardsOnTopOfLibrary(card, game, source, true); + } else { + controller.moveCards(card, Zone.HAND, source, game); + } + if (reveal) { + Cards revealCard = new CardsImpl(); + revealCard.add(card); + controller.revealCards(sourceObject.getIdName(), revealCard, game); } } - return true; } - return false; + return true; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/AttacksIfAbleAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/AttacksIfAbleAllEffect.java index 43e14562a261..794918de4f97 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/AttacksIfAbleAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/AttacksIfAbleAllEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common.combat; import mage.MageObjectReference; diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/AttacksIfAbleAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/AttacksIfAbleAttachedEffect.java index f13ad0cc484f..329f0f83537b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/AttacksIfAbleAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/AttacksIfAbleAttachedEffect.java @@ -18,7 +18,7 @@ public class AttacksIfAbleAttachedEffect extends RequirementEffect { public AttacksIfAbleAttachedEffect(Duration duration, AttachmentType attachmentType) { super(duration); - this.staticText = attachmentType.verb() + " creature attacks each turn if able"; + this.staticText = attachmentType.verb() + " creature attacks each combat if able"; } public AttacksIfAbleAttachedEffect(final AttacksIfAbleAttachedEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/AttacksIfAbleTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/AttacksIfAbleTargetEffect.java index c545cd2488fc..848619ecd841 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/AttacksIfAbleTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/AttacksIfAbleTargetEffect.java @@ -50,7 +50,7 @@ public String getText(Mode mode) { if (this.duration == Duration.EndOfTurn) { return "target " + mode.getTargets().get(0).getTargetName() + " attacks this turn if able"; } else { - return "target " + mode.getTargets().get(0).getTargetName() + " attacks each turn if able"; + return "target " + mode.getTargets().get(0).getTargetName() + " attacks each combat if able"; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CanAttackAsThoughItDidntHaveDefenderAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CanAttackAsThoughItDidntHaveDefenderAllEffect.java index d3b865179db9..a7c1dbffe2f5 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/CanAttackAsThoughItDidntHaveDefenderAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CanAttackAsThoughItDidntHaveDefenderAllEffect.java @@ -48,8 +48,7 @@ public boolean apply(Game game, Ability source) { @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { Permanent permanent = game.getPermanent(objectId); - return permanent != null - && filter.match(permanent, source.getSourceId(), source.getControllerId(), game); + return filter.match(permanent, source.getSourceId(), source.getControllerId(), game); } private String getText() { diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CanAttackOnlyAloneEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CanAttackOnlyAloneEffect.java index 711ad88ebaca..65fb23661cc1 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/CanAttackOnlyAloneEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CanAttackOnlyAloneEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common.combat; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackAloneAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackAloneAttachedEffect.java index cf3a354dccbc..25c592c8561c 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackAloneAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackAloneAttachedEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common.combat; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackAloneSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackAloneSourceEffect.java index 9f8f27f5ce6f..28ea3271c31f 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackAloneSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackAloneSourceEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common.combat; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackControllerAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackControllerAttachedEffect.java index 605da5354c31..518e3752ca73 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackControllerAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackControllerAttachedEffect.java @@ -18,7 +18,7 @@ public class CantAttackControllerAttachedEffect extends RestrictionEffect { public CantAttackControllerAttachedEffect(AttachmentType attachmentType) { super(Duration.WhileOnBattlefield); - this.staticText = attachmentType.verb() + " creature can't attack you or a planeswalker you control"; + this.staticText = attachmentType.verb() + " creature can't attack you or planeswalkers you control"; } public CantAttackControllerAttachedEffect(final CantAttackControllerAttachedEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/ActivateAbilitiesAnyTimeYouCouldCastInstantEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/ActivateAbilitiesAnyTimeYouCouldCastInstantEffect.java index 5a19776e0dd0..0336b383e1fc 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/ActivateAbilitiesAnyTimeYouCouldCastInstantEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/ActivateAbilitiesAnyTimeYouCouldCastInstantEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common.continuous; import java.util.UUID; diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/AssignNoCombatDamageSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/AssignNoCombatDamageSourceEffect.java index 0ec383d15b3b..81c3ad03b229 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/AssignNoCombatDamageSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/AssignNoCombatDamageSourceEffect.java @@ -51,9 +51,8 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) { @Override public boolean checksEventType(GameEvent event, Game game) { switch (event.getType()) { - case DAMAGE_CREATURE: + case DAMAGE_PERMANENT: case DAMAGE_PLAYER: - case DAMAGE_PLANESWALKER: return true; default: return false; diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBasicLandTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBasicLandTargetEffect.java index ef33949bbc8b..5b93694a1639 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBasicLandTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBasicLandTargetEffect.java @@ -24,8 +24,8 @@ public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl { protected boolean chooseLandType; protected List landTypes = new ArrayList<>(); - protected List landTypesToAdd = new ArrayList<>(); - protected boolean loseOther; // loses all other abilities, card types, and creature types + private final List landTypesToAdd = new ArrayList<>(); + private final boolean loseOther; // loses all other abilities, card types, and creature types public BecomesBasicLandTargetEffect(Duration duration) { this(duration, true); @@ -40,7 +40,7 @@ public BecomesBasicLandTargetEffect(Duration duration, boolean chooseLandType, S } public BecomesBasicLandTargetEffect(Duration duration, boolean chooseLandType, boolean loseOther, SubType... landNames) { - super(duration, Outcome.Detriment); + super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Detriment); this.landTypes.addAll(Arrays.asList(landNames)); if (landTypes.contains(SubType.MOUNTAIN)) { dependencyTypes.add(DependencyType.BecomeMountain); @@ -71,11 +71,6 @@ public BecomesBasicLandTargetEffect(final BecomesBasicLandTargetEffect effect) { this.loseOther = effect.loseOther; } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public BecomesBasicLandTargetEffect copy() { return new BecomesBasicLandTargetEffect(this); @@ -101,53 +96,49 @@ public void init(Ability source, Game game) { } @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + public boolean apply(Game game, Ability source) { for (UUID targetPermanent : targetPointer.getTargets(game, source)) { Permanent land = game.getPermanent(targetPermanent); - if (land != null) { - switch (layer) { - case TypeChangingEffects_4: - // Attention: Cards like Unstable Frontier that use this class do not give the "Basic" supertype to the target - if (!land.isLand()) { - land.addCardType(CardType.LAND); - } - if (loseOther) { - // 305.7 Note that this doesn't remove any abilities - // that were granted to the land by other effects - // So the ability removing has to be done before Layer 6 - land.removeAllAbilities(source.getSourceId(), game); - // 305.7 - land.removeAllSubTypes(game, SubTypeSet.NonBasicLandType); - land.addSubType(game, landTypes); - } else { - landTypesToAdd.clear(); - for (SubType subtype : landTypes) { - if (!land.hasSubtype(subtype, game)) { - land.addSubType(game, subtype); - landTypesToAdd.add(subtype); - } - } - } - // add intrinsic land abilities here not in layer 6 - for (SubType landType : landTypesToAdd) { - switch (landType) { - case SWAMP: - land.addAbility(new BlackManaAbility(), source.getSourceId(), game); - break; - case MOUNTAIN: - land.addAbility(new RedManaAbility(), source.getSourceId(), game); - break; - case FOREST: - land.addAbility(new GreenManaAbility(), source.getSourceId(), game); - break; - case ISLAND: - land.addAbility(new BlueManaAbility(), source.getSourceId(), game); - break; - case PLAINS: - land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); - break; - } - } + if (land == null) { + continue; + } + if (!land.isLand()) { + land.addCardType(CardType.LAND); + } + if (loseOther) { + // 305.7 Note that this doesn't remove any abilities + // that were granted to the land by other effects + // So the ability removing has to be done before Layer 6 + land.removeAllAbilities(source.getSourceId(), game); + // 305.7 + land.removeAllSubTypes(game, SubTypeSet.NonBasicLandType); + land.addSubType(game, landTypes); + } else { + landTypesToAdd.clear(); + for (SubType subtype : landTypes) { + if (!land.hasSubtype(subtype, game)) { + land.addSubType(game, subtype); + landTypesToAdd.add(subtype); + } + } + } + // add intrinsic land abilities here not in layer 6 + for (SubType landType : landTypesToAdd) { + switch (landType) { + case PLAINS: + land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); + break; + case ISLAND: + land.addAbility(new BlueManaAbility(), source.getSourceId(), game); + break; + case SWAMP: + land.addAbility(new BlackManaAbility(), source.getSourceId(), game); + break; + case MOUNTAIN: + land.addAbility(new RedManaAbility(), source.getSourceId(), game); + break; + case FOREST: + land.addAbility(new GreenManaAbility(), source.getSourceId(), game); break; } } @@ -155,11 +146,6 @@ public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) return true; } - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.TypeChangingEffects_4; - } - private String setText() { StringBuilder sb = new StringBuilder(); if (chooseLandType) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesColorOrColorsTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesColorOrColorsTargetEffect.java index cdcf19baf9b1..752659b5013b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesColorOrColorsTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesColorOrColorsTargetEffect.java @@ -48,7 +48,7 @@ public boolean apply(Game game, Ability source) { if (controller != null && target != null) { for (int i = 0; i < 5; i++) { if (i > 0) { - if (!controller.chooseUse(Outcome.Neutral, "Do you wish to choose another color?", source, game)) { + if (!controller.chooseUse(Outcome.Neutral, "Choose another color?", source, game)) { break; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureSourceEffect.java index b9c3f839a094..07962c4ec180 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureSourceEffect.java @@ -146,9 +146,9 @@ public boolean apply(Game game, Ability source) { private void setText() { if (theyAreStillType != null && !theyAreStillType.isEmpty()) { - staticText = duration.toString() + " {this} becomes a " + token.getDescription() + " that's still a " + this.theyAreStillType; + staticText = duration.toString() + ", {this} becomes a " + token.getDescription() + " that's still a " + this.theyAreStillType; } else { - staticText = duration.toString() + " {this} becomes a " + token.getDescription(); + staticText = duration.toString() + ", {this} becomes a " + token.getDescription(); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesSubtypeAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesSubtypeAllEffect.java index ad2f68067102..d3a15c2dc67a 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesSubtypeAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesSubtypeAllEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common.continuous; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/CastAsThoughItHadFlashAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/CastAsThoughItHadFlashAllEffect.java index 46398e183502..af135587bf64 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/CastAsThoughItHadFlashAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/CastAsThoughItHadFlashAllEffect.java @@ -1,8 +1,5 @@ - - package mage.abilities.effects.common.continuous; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.keyword.MorphAbility; @@ -14,8 +11,9 @@ import mage.filter.FilterCard; import mage.game.Game; +import java.util.UUID; + /** - * * @author LevelX2 */ @@ -58,21 +56,15 @@ public boolean applies(UUID affectedSpellId, Ability source, UUID affectedContro if (card != null) { //Allow lands with morph to be played at instant speed if (card.isLand()) { - boolean morphAbility = false; - for (Ability checkAbility : card.getAbilities()) { - if (checkAbility instanceof MorphAbility) { - morphAbility = true; - break; - } - } + boolean morphAbility = card.getAbilities().stream().anyMatch(ability -> ability instanceof MorphAbility); if (morphAbility) { Card cardCopy = card.copy(); cardCopy.getCardType().clear(); cardCopy.addCardType(CardType.CREATURE); - return filter.match(cardCopy, game); + return filter.match(cardCopy, source.getSourceId(), affectedControllerId, game); } } - return filter.match(card, game); + return filter.match(card, source.getSourceId(), affectedControllerId, game); } } return false; diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/ExchangeControlTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/ExchangeControlTargetEffect.java index 2f780e7dd07a..2394f9dab537 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/ExchangeControlTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/ExchangeControlTargetEffect.java @@ -129,7 +129,7 @@ public boolean apply(Game game, Ability source) { filter.add(SubType.AURA.getPredicate()); for (UUID attachmentId : new HashSet<>(permanent.getAttachments())) { Permanent attachment = game.getPermanent(attachmentId); - if (attachment != null && filter.match(attachment, game)) { + if (filter.match(attachment, game)) { attachment.destroy(source, game, false); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAllOfChosenSubtypeEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAllOfChosenSubtypeEffect.java index e9351a24e8a1..bd9e56d36fba 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAllOfChosenSubtypeEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAllOfChosenSubtypeEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common.continuous; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledSpellsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledSpellsEffect.java index f147c581378a..d6f8aa2acd25 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledSpellsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledSpellsEffect.java @@ -65,7 +65,8 @@ public boolean apply(Game game, Ability source) { } // workaround to gain cost reduction abilities to commanders before cast (make it playable) - game.getCommanderCardsFromCommandZone(player, CommanderCardType.ANY).stream() + game.getCommanderCardsFromCommandZone(player, CommanderCardType.ANY) + .stream() .filter(card -> filter.match(card, game)) .forEach(card -> { game.getState().addOtherAbility(card, ability); @@ -77,12 +78,9 @@ public boolean apply(Game game, Ability source) { && !stackObject.isCopy() && stackObject.isControlledBy(source.getControllerId())) { Card card = game.getCard(stackObject.getSourceId()); - if (card != null - && filter.match(card, game)) { - if (!card.hasAbility(ability, game)) { - game.getState().addOtherAbility(card, ability); - return true; - } + if (filter.match(card, game)) { + game.getState().addOtherAbility(card, ability); + return true; } } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlTargetEffect.java index da5aef9bda48..ad66fc7eeec8 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlTargetEffect.java @@ -9,7 +9,6 @@ import mage.constants.Outcome; import mage.constants.SubLayer; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; @@ -76,42 +75,44 @@ public void init(Ability source, Game game) { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - boolean oneTargetStillExists = false; - for (UUID permanentId : getTargetPointer().getTargets(game, source)) { - Permanent permanent = game.getPermanent(permanentId); - if (permanent != null) { - oneTargetStillExists = true; - if (!permanent.isControlledBy(controllingPlayerId)) { - boolean controlChanged = false; - if (controllingPlayerId != null) { - if (permanent.changeControllerId(controllingPlayerId, game, source)) { - controlChanged = true; - } - } else { - if (permanent.changeControllerId(source.getControllerId(), game, source)) { - controlChanged = true; - } - } - if (source instanceof ActivatedAbility - && firstControlChange && !controlChanged) { - // If it was not possible to get control of target permanent by the activated ability the first time it took place - // the effect failed (e.g. because of Guardian Beast) and must be discarded - // This does not handle correctly multiple targets at once - discard(); - } - } + if (controller == null) { + discard(); // controller no longer exists + return false; + } + boolean oneTargetStillExists = false; + for (UUID permanentId : getTargetPointer().getTargets(game, source)) { + Permanent permanent = game.getPermanent(permanentId); + if (permanent == null) { + continue; + } + oneTargetStillExists = true; + if (permanent.isControlledBy(controllingPlayerId)) { + continue; + } + boolean controlChanged = false; + if (controllingPlayerId != null) { + if (permanent.changeControllerId(controllingPlayerId, game, source)) { + controlChanged = true; + } + } else { + if (permanent.changeControllerId(source.getControllerId(), game, source)) { + controlChanged = true; } } - // no valid target exists and the controller is no longer in the game, effect can be discarded - if (!oneTargetStillExists || !controller.isInGame()) { + if (source instanceof ActivatedAbility + && firstControlChange && !controlChanged) { + // If it was not possible to get control of target permanent by the activated ability the first time it took place + // the effect failed (e.g. because of Guardian Beast) and must be discarded + // This does not handle correctly multiple targets at once discard(); } - firstControlChange = false; - return true; } - discard(); // controller no longer exists - return false; + // no valid target exists and the controller is no longer in the game, effect can be discarded + if (!oneTargetStillExists || !controller.isInGame()) { + discard(); + } + firstControlChange = false; + return true; } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/PlayAdditionalLandsControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/PlayAdditionalLandsControllerEffect.java index 83663222cd1a..8005dd8f5932 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/PlayAdditionalLandsControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/PlayAdditionalLandsControllerEffect.java @@ -54,6 +54,9 @@ private void setText() { if (additionalCards == Integer.MAX_VALUE) { sb.append("any number of"); } else { + if (additionalCards > 1 && duration == Duration.EndOfTurn) { + sb.append("up to "); + } sb.append(CardUtil.numberToText(additionalCards, "an")); } sb.append(" additional land").append((additionalCards == 1 ? "" : "s")) diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/PlayTheTopCardEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/PlayTheTopCardEffect.java index 34b136f26adc..4e5827144c0a 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/PlayTheTopCardEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/PlayTheTopCardEffect.java @@ -7,34 +7,44 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.filter.FilterCard; -import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; -import mage.util.CardUtil; +import java.util.Locale; import java.util.UUID; /** - * @author nantuko + * @author nantuko, JayDi85 */ public class PlayTheTopCardEffect extends AsThoughEffectImpl { private final FilterCard filter; + // can play card or can play lands/cast spells, see two modes below + private final boolean canPlayCardOnly; + + public PlayTheTopCardEffect() { - this(StaticFilters.FILTER_CARD); - staticText = "You may play lands and cast spells from the top of your library"; + this(new FilterCard("play lands and cast spells"), false); } - public PlayTheTopCardEffect(FilterCard filter) { + public PlayTheTopCardEffect(FilterCard filter, boolean canPlayCardOnly) { super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit); this.filter = filter; - staticText = "You may " + filter.getMessage() + " from the top of your library"; + this.canPlayCardOnly = canPlayCardOnly; + this.staticText = "You may " + filter.getMessage() + " from the top of your library"; + + // verify check: if you see "card" text in the rules then use card mode + // (there aren't any real cards after oracle update, but can be added in the future) + if (this.canPlayCardOnly != filter.getMessage().toLowerCase(Locale.ENGLISH).contains("card")) { + throw new IllegalArgumentException("Wrong usage of card mode settings"); + } } public PlayTheTopCardEffect(final PlayTheTopCardEffect effect) { super(effect); this.filter = effect.filter; + this.canPlayCardOnly = effect.canPlayCardOnly; } @Override @@ -49,25 +59,45 @@ public PlayTheTopCardEffect copy() { @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - return applies(objectId, null, source, game, affectedControllerId); - } + // main card and all parts are checks in different calls. + // two modes: + // * can play cards (must check main card and allows any parts) + // * can play lands/spells (must check specific part and allows specific part) - @Override - public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) { + // current card's part Card cardToCheck = game.getCard(objectId); - objectId = CardUtil.getMainCardId(game, objectId); // for split cards + if (cardToCheck == null) { + return false; + } + + if (this.canPlayCardOnly) { + // check whole card intead part + cardToCheck = cardToCheck.getMainCard(); + } - if (cardToCheck != null - && playerId.equals(source.getControllerId()) - && cardToCheck.isOwnedBy(source.getControllerId()) - && (!cardToCheck.getManaCost().isEmpty() || cardToCheck.isLand()) - && filter.match(cardToCheck, source.getSourceId(), source.getControllerId(), game)) { - Player player = game.getPlayer(cardToCheck.getOwnerId()); + // must be you + if (!affectedControllerId.equals(source.getControllerId())) { + return false; + } - UUID needCardID = player.getLibrary().getFromTop(game) == null ? null : player.getLibrary().getFromTop(game).getId(); - return objectId.equals(needCardID); + // must be your card + Player player = game.getPlayer(cardToCheck.getOwnerId()); + if (player == null || !player.getId().equals(affectedControllerId)) { + return false; + } + + // must be from your library + Card topCard = player.getLibrary().getFromTop(game); + if (topCard == null || !topCard.getId().equals(cardToCheck.getMainCard().getId())) { + return false; + } + + // can't cast without mana cost + if (!cardToCheck.isLand() && cardToCheck.getManaCost().isEmpty()) { + return false; } - return false; - } + // must be correct card + return filter.match(cardToCheck, source.getSourceId(), affectedControllerId, game); + } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/SetPowerToughnessTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/SetPowerToughnessTargetEffect.java index b8ff7c4f846c..31b08e6b2d67 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/SetPowerToughnessTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/SetPowerToughnessTargetEffect.java @@ -12,6 +12,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.Target; +import mage.util.CardUtil; import java.util.UUID; @@ -68,6 +69,11 @@ public String getText(Mode mode) { return staticText; } StringBuilder sb = new StringBuilder(); + if (mode.getTargets().get(0).getMinNumberOfTargets() == 0) { + sb.append("up to "); + sb.append(CardUtil.numberToText(mode.getTargets().get(0).getMaxNumberOfTargets())); + sb.append(' '); + } if (!mode.getTargets().get(0).getTargetName().contains("target")) { sb.append("target "); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/SwitchPowerToughnessTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/SwitchPowerToughnessTargetEffect.java index 3eb33e61e49e..c3f4b31fa8af 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/SwitchPowerToughnessTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/SwitchPowerToughnessTargetEffect.java @@ -45,7 +45,7 @@ public boolean apply(Game game, Ability source) { @Override public String getText(Mode mode) { StringBuilder sb = new StringBuilder(); - sb.append("Switch target ").append(mode.getTargets().get(0).getTargetName()).append("'s power and toughness") + sb.append("switch target ").append(mode.getTargets().get(0).getTargetName()).append("'s power and toughness") .append(' ').append(duration.toString()); return sb.toString(); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/AbilitiesCostReductionControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/AbilitiesCostReductionControllerEffect.java index 86a4c0de5b71..5c87bd046fc5 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/cost/AbilitiesCostReductionControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/AbilitiesCostReductionControllerEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common.cost; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/CastWithoutPayingManaCostEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/CastWithoutPayingManaCostEffect.java index f7da8cee5576..55569283147d 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/cost/CastWithoutPayingManaCostEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/CastWithoutPayingManaCostEffect.java @@ -10,7 +10,7 @@ import mage.constants.Outcome; import mage.filter.FilterCard; import mage.filter.common.FilterNonlandCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.Target; @@ -39,7 +39,7 @@ public CastWithoutPayingManaCostEffect(int maxCost) { public CastWithoutPayingManaCostEffect(DynamicValue maxCost) { super(Outcome.PlayForFree); this.manaCost = maxCost; - this.staticText = "you may cast a card with converted mana cost " + this.staticText = "you may cast a card with mana value " + maxCost + " or less from your hand without paying its mana cost"; } @@ -56,13 +56,13 @@ public boolean apply(Game game, Ability source) { return false; } int cmc = manaCost.calculate(game, source, this); - FilterCard filter = new FilterNonlandCard("card with converted mana cost " + FilterCard filter = new FilterNonlandCard("card with mana value " + cmc + " or less from your hand"); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, cmc + 1)); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, cmc + 1)); Target target = new TargetCardInHand(filter); if (target.canChoose(source.getSourceId(), controller.getId(), game) - && controller.chooseUse(Outcome.PlayForFree, "Cast a card with converted mana cost " + cmc + && controller.chooseUse(Outcome.PlayForFree, "Cast a card with mana value " + cmc + " or less from your hand without paying its mana cost?", source, game)) { Card cardToCast = null; boolean cancel = false; @@ -76,7 +76,7 @@ public boolean apply(Game game, Ability source) { + cardToCast.getName() + " is no land and has no spell ability!"); cancel = true; } - if (cardToCast.getSpellAbility().canChooseTarget(game)) { + if (cardToCast.getSpellAbility().canChooseTarget(game, controller.getId())) { cancel = true; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionForEachSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionForEachSourceEffect.java index 6bae62051d71..5b998cbd3b2b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionForEachSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionForEachSourceEffect.java @@ -43,9 +43,7 @@ public SpellCostReductionForEachSourceEffect(ManaCosts reduceManaCosts StringBuilder sb = new StringBuilder(); sb.append("this spell costs "); - for (String manaSymbol : reduceManaCosts.getSymbols()) { - sb.append(manaSymbol); - } + sb.append(reduceManaCosts.getText()); sb.append(" less to cast for each ").append(this.eachAmount.getMessage()); this.staticText = sb.toString(); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceEffect.java index 6921639ee1e9..2df91a1fbf78 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceEffect.java @@ -34,9 +34,7 @@ public SpellCostReductionSourceEffect(ManaCosts manaCostsToReduce, Con StringBuilder sb = new StringBuilder(); sb.append("this spell costs "); - for (String manaSymbol : manaCostsToReduce.getSymbols()) { - sb.append(manaSymbol); - } + sb.append(manaCostsToReduce.getText()); sb.append(" less to cast"); if (this.condition != null) { sb.append(" if ").append(this.condition.toString()); diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasingAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasingAllEffect.java index fdec9bfdd34b..543d7d4ca862 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasingAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasingAllEffect.java @@ -63,9 +63,7 @@ private void setText() { sb.append(" cost "); if (this.increaseManaCosts != null) { - for (String manaSymbol : this.increaseManaCosts.getSymbols()) { - sb.append(manaSymbol); - } + sb.append(this.increaseManaCosts.getText()); } else { sb.append("{").append(increaseGenericCost).append("}"); } @@ -126,7 +124,7 @@ public boolean applies(Ability abilityToModify, Ability source, Game game) { // get playable and other staff without put on stack // used at least for flashback ability because Flashback ability doesn't use stack Card sourceCard = game.getCard(abilityToModify.getSourceId()); - return sourceCard != null && this.filter.match(sourceCard, game); + return this.filter.match(sourceCard, game); } } return false; diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostModificationThatTargetSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostModificationThatTargetSourceEffect.java index e8fbeb1c1da4..975420979d92 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostModificationThatTargetSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostModificationThatTargetSourceEffect.java @@ -113,7 +113,7 @@ public boolean applies(Ability abilityToModify, Ability source, Game game) { } Card spellCard = ((SpellAbility) abilityToModify).getCharacteristics(game); - if (spellCard == null || !this.spellFilter.match(spellCard, game)) { + if (!this.spellFilter.match(spellCard, game)) { return false; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllOfChosenSubtypeEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllOfChosenSubtypeEffect.java index 765536290605..74cfc4765663 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllOfChosenSubtypeEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllOfChosenSubtypeEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common.cost; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionControllerEffect.java index c916c12b99b1..23bf6e9f7c72 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionControllerEffect.java @@ -37,9 +37,7 @@ public SpellsCostReductionControllerEffect(FilterCard filter, ManaCosts numberToReveal) { - TargetCard chosenCards = new TargetCard(numberToReveal, numberToReveal, + TargetCard chosenCards = new TargetCard(numberToReveal, numberToReveal, Zone.HAND, new FilterCard("card in " + player.getName() + "'s hand")); chosenCards.setNotTarget(true); if (chosenCards.canChoose(source.getSourceId(), player.getId(), game) @@ -131,7 +132,7 @@ public boolean apply(Game game, Ability source) { } Card sourceCard = game.getCard(source.getSourceId()); - player.revealCards(sourceCard != null ? sourceCard.getIdName() + " (" + player.revealCards(sourceCard != null ? sourceCard.getIdName() + " (" + sourceCard.getZoneChangeCounter(game) + ')' : "Discard", revealedCards, game); boolean result = true; @@ -144,7 +145,7 @@ public boolean apply(Game game, Ability source) { if (!controller.choose(Outcome.Benefit, revealedCards, target, game)) { return result; } - result=!player.discard(new CardsImpl(target.getTargets()),false, source,game).isEmpty(); + result = !player.discard(new CardsImpl(target.getTargets()), false, source, game).isEmpty(); return result; } @@ -180,13 +181,10 @@ private String setText() { sb.append(". You choose "); boolean discardMultipleCards = !numberCardsToDiscard.toString().equals("1"); if (discardMultipleCards) { - sb.append(numberCardsToDiscard).append(' '); + sb.append(numberCardsToDiscard).append(' ').append(filter.getMessage()); } else { - if (!filter.getMessage().startsWith("a ") && !filter.getMessage().startsWith("an ")) { - sb.append("a "); - } + sb.append(CardUtil.addArticle(filter.getMessage())); } - sb.append(filter.getMessage()); if (revealAllCards) { sb.append(" from it."); } else { diff --git a/Mage/src/main/java/mage/abilities/effects/common/replacement/CreateTwiceThatManyTokensEffect.java b/Mage/src/main/java/mage/abilities/effects/common/replacement/CreateTwiceThatManyTokensEffect.java index 799881bca849..d99423022757 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/replacement/CreateTwiceThatManyTokensEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/replacement/CreateTwiceThatManyTokensEffect.java @@ -1,4 +1,3 @@ - package mage.abilities.effects.common.replacement; import mage.abilities.Ability; @@ -9,17 +8,17 @@ import mage.game.events.GameEvent; /** - * * @author LevelX2 */ public class CreateTwiceThatManyTokensEffect extends ReplacementEffectImpl { public CreateTwiceThatManyTokensEffect() { super(Duration.WhileOnBattlefield, Outcome.Copy); - staticText = "If an effect would create one or more tokens under your control, it creates twice that many of those tokens instead"; + staticText = "if one or more tokens would be created under your control, " + + "twice that many of those tokens are created instead"; } - public CreateTwiceThatManyTokensEffect(final CreateTwiceThatManyTokensEffect effect) { + private CreateTwiceThatManyTokensEffect(final CreateTwiceThatManyTokensEffect effect) { super(effect); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/replacement/DiesReplacementEffect.java b/Mage/src/main/java/mage/abilities/effects/common/replacement/DiesReplacementEffect.java index 8d776a5cb788..5232805d6a4d 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/replacement/DiesReplacementEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/replacement/DiesReplacementEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common.replacement; import mage.MageObjectReference; diff --git a/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/PlayLandsFromGraveyardControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/PlayLandsFromGraveyardControllerEffect.java index 4a2eb21a7c6f..cbe6d8d0674a 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/PlayLandsFromGraveyardControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/PlayLandsFromGraveyardControllerEffect.java @@ -10,7 +10,6 @@ import mage.filter.common.FilterLandCard; import mage.game.Game; import mage.players.Player; -import mage.util.CardUtil; import java.util.UUID; @@ -28,7 +27,7 @@ public PlayLandsFromGraveyardControllerEffect() { public PlayLandsFromGraveyardControllerEffect(FilterCard filter) { super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit); this.filter = filter; - staticText = "You may play " + filter.getMessage() + " from your graveyard"; + this.staticText = "You may play " + filter.getMessage() + " from your graveyard"; } public PlayLandsFromGraveyardControllerEffect(final PlayLandsFromGraveyardControllerEffect effect) { @@ -49,30 +48,35 @@ public PlayLandsFromGraveyardControllerEffect copy() { @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - return applies(objectId, null, source, game, affectedControllerId); - } - - @Override - public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) { + // current card's part Card cardToCheck = game.getCard(objectId); - objectId = CardUtil.getMainCardId(game, objectId); // for split cards if (cardToCheck == null) { return false; } + // must be you + if (!affectedControllerId.equals(source.getControllerId())) { + return false; + } + + // must be your card Player player = game.getPlayer(cardToCheck.getOwnerId()); - if (player == null) { + if (player == null || !player.getId().equals(affectedControllerId)) { return false; } - UUID needCardId = objectId; + // must be from your graveyard + UUID needCardId = cardToCheck.getMainCard().getId(); if (player.getGraveyard().getCards(game).stream().noneMatch(c -> c.getId().equals(needCardId))) { return false; } - return playerId.equals(source.getControllerId()) - && cardToCheck.isOwnedBy(source.getControllerId()) - && (!cardToCheck.getManaCost().isEmpty() || cardToCheck.isLand()) - && filter.match(cardToCheck, game); + // can't cast without mana cost + if (!cardToCheck.isLand() && cardToCheck.getManaCost().isEmpty()) { + return false; + } + + // must be correct card + return filter.match(cardToCheck, source.getSourceId(), affectedControllerId, game); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/TargetsHaveToTargetPermanentIfAbleEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/TargetsHaveToTargetPermanentIfAbleEffect.java index e02f7003b7c2..cb1f72695a2e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/TargetsHaveToTargetPermanentIfAbleEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/TargetsHaveToTargetPermanentIfAbleEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common.ruleModifying; import mage.MageObject; diff --git a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutInHandEffect.java b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutInHandEffect.java index 645d13e44286..21cd0a546881 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutInHandEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutInHandEffect.java @@ -35,7 +35,7 @@ public SearchLibraryGraveyardPutInHandEffect(FilterCard filter, boolean forceToS this.filter = filter; this.forceToSearchBoth = forceToSearchBoth; staticText = (youMay ? "you may " : "") + "search your library and" + (forceToSearchBoth ? "" : "/or") + " graveyard for a card named " + filter.getMessage() - + ", reveal it, and put it into your hand. " + (forceToSearchBoth ? "Then shuffle your library" : "If you search your library this way, shuffle it"); + + ", reveal it, and put it into your hand. " + (forceToSearchBoth ? "Then shuffle" : "If you search your library this way, shuffle"); } public SearchLibraryGraveyardPutInHandEffect(final SearchLibraryGraveyardPutInHandEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutOntoBattlefieldEffect.java b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutOntoBattlefieldEffect.java index 1afa6c7f2722..464d4ab10daf 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutOntoBattlefieldEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutOntoBattlefieldEffect.java @@ -34,7 +34,7 @@ public SearchLibraryGraveyardPutOntoBattlefieldEffect(FilterCard filter, boolean this.filter = filter; this.forceToSearchBoth = forceToSearchBoth; staticText = (youMay ? "You may " : "") + "search your library and" + (forceToSearchBoth ? "" : "/or") + " graveyard for a " + filter.getMessage() - + " and put it onto the battlefield. " + (forceToSearchBoth ? "Then shuffle your library" : "If you search your library this way, shuffle it"); + + " and put it onto the battlefield. " + (forceToSearchBoth ? "Then shuffle" : "If you search your library this way, shuffle"); } public SearchLibraryGraveyardPutOntoBattlefieldEffect(final SearchLibraryGraveyardPutOntoBattlefieldEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardWithLessCMCPutIntoPlay.java b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardWithLessMVPutIntoPlay.java similarity index 71% rename from Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardWithLessCMCPutIntoPlay.java rename to Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardWithLessMVPutIntoPlay.java index 9a37206fe456..bbca957698f7 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardWithLessCMCPutIntoPlay.java +++ b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardWithLessMVPutIntoPlay.java @@ -9,7 +9,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; @@ -19,28 +19,28 @@ * * @author antoni-g */ -public class SearchLibraryGraveyardWithLessCMCPutIntoPlay extends OneShotEffect { +public class SearchLibraryGraveyardWithLessMVPutIntoPlay extends OneShotEffect { private final FilterCard filter; - public SearchLibraryGraveyardWithLessCMCPutIntoPlay() { + public SearchLibraryGraveyardWithLessMVPutIntoPlay() { this(new FilterCard()); } - public SearchLibraryGraveyardWithLessCMCPutIntoPlay(FilterCard filter) { + public SearchLibraryGraveyardWithLessMVPutIntoPlay(FilterCard filter) { super(Outcome.PutCreatureInPlay); this.filter = filter; - staticText = "Search your library or graveyard for a " + filter.getMessage() + " with converted mana cost X or less, put it onto the battlefield, then shuffle your library"; + staticText = "Search your library and/or graveyard for a " + filter.getMessage() + " with mana value X or less, put it onto the battlefield. If you search your library this way, shuffle."; } - public SearchLibraryGraveyardWithLessCMCPutIntoPlay(final SearchLibraryGraveyardWithLessCMCPutIntoPlay effect) { + public SearchLibraryGraveyardWithLessMVPutIntoPlay(final SearchLibraryGraveyardWithLessMVPutIntoPlay effect) { super(effect); this.filter = effect.filter; } @Override - public SearchLibraryGraveyardWithLessCMCPutIntoPlay copy() { - return new SearchLibraryGraveyardWithLessCMCPutIntoPlay(this); + public SearchLibraryGraveyardWithLessMVPutIntoPlay copy() { + return new SearchLibraryGraveyardWithLessMVPutIntoPlay(this); } @Override @@ -51,9 +51,9 @@ public boolean apply(Game game, Ability source) { if (controller != null && sourceObject != null) { // create x cost filter FilterCard advancedFilter = filter.copy(); // never change static objects so copy the object here before - advancedFilter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, source.getManaCostsToPay().getX() + 1)); + advancedFilter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, source.getManaCostsToPay().getX() + 1)); - if (controller.chooseUse(outcome, "Search your library for a " + filter.getMessage() + " with CMC X or less" + '?', source, game)) { + if (controller.chooseUse(outcome, "Search your library for a " + filter.getMessage() + " with mana value X or less" + '?', source, game)) { TargetCardInLibrary target = new TargetCardInLibrary(advancedFilter); target.clearChosen(); if (controller.searchLibrary(target, source, game)) { @@ -64,7 +64,7 @@ public boolean apply(Game game, Ability source) { controller.shuffleLibrary(source, game); } - if (cardFound == null && controller.chooseUse(outcome, "Search your graveyard for a " + filter.getMessage() + " with CMC X or less" + '?', source, game)) { + if (cardFound == null && controller.chooseUse(outcome, "Search your graveyard for a " + filter.getMessage() + " with mana value X or less" + '?', source, game)) { TargetCard target = new TargetCard(0, 1, Zone.GRAVEYARD, advancedFilter); target.clearChosen(); if (controller.choose(outcome, controller.getGraveyard(), target, game)) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryPutInHandEffect.java b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryPutInHandEffect.java index b727e317ad4e..f34d46931d25 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryPutInHandEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryPutInHandEffect.java @@ -99,22 +99,19 @@ private void setText() { sb.append(target.getTargetName()); if (forceShuffle) { sb.append(revealCards ? ", reveal them" : ""); - sb.append(", put them into your hand, then shuffle your library"); + sb.append(", put them into your hand, then shuffle"); } else { sb.append(revealCards ? ", reveal them," : ""); - sb.append(" and put them into your hand. If you do, shuffle your library"); + sb.append(" and put them into your hand. If you do, shuffle"); } } else { - if (!target.getTargetName().startsWith("a ") && !target.getTargetName().startsWith("an ")) { - sb.append("a "); - } - sb.append(target.getTargetName()); + sb.append(CardUtil.addArticle(target.getTargetName())); if (forceShuffle) { - sb.append(revealCards ? ", reveal it" : ""); - sb.append(", put it into your hand, then shuffle your library"); + sb.append(revealCards ? ", reveal it, put it" : ", put that card"); + sb.append(" into your hand, then shuffle"); } else { sb.append(revealCards ? ", reveal it," : ""); - sb.append(" and put that card into your hand. If you do, shuffle your library"); + sb.append(" and put that card into your hand. If you do, shuffle"); } } staticText = sb.toString(); diff --git a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryPutInHandOrOnBattlefieldEffect.java b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryPutInHandOrOnBattlefieldEffect.java index 86bd7d7337fa..8b98012d0f32 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryPutInHandOrOnBattlefieldEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryPutInHandOrOnBattlefieldEffect.java @@ -119,9 +119,9 @@ private void setText() { sb.append("this way, you may put it onto the battlefield instead"); } if (forceShuffle) { - sb.append(". Then shuffle your library"); + sb.append(". Then shuffle"); } else { - sb.append(". If you do, shuffle your library"); + sb.append(". If you do, shuffle"); } staticText = sb.toString(); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryPutInPlayEffect.java b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryPutInPlayEffect.java index 677f499e3414..08d5825c86d4 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryPutInPlayEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryPutInPlayEffect.java @@ -88,8 +88,7 @@ private void setText() { sb.append(forceShuffle ? ", " : " and "); sb.append("put them onto the battlefield"); } else { - sb.append(target.getTargetName().startsWith("a ") || target.getTargetName().startsWith("an ") ? "" : "a ") - .append(target.getTargetName()); + sb.append(CardUtil.addArticle(target.getTargetName())); sb.append(forceShuffle ? ", " : " and "); sb.append("put it onto the battlefield"); } @@ -97,9 +96,9 @@ private void setText() { sb.append(" tapped"); } if (forceShuffle) { - sb.append(", then shuffle your library"); + sb.append(", then shuffle"); } else { - sb.append(". If you do, shuffle your library"); + sb.append(". If you do, shuffle"); } staticText = sb.toString(); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryPutInPlayTargetPlayerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryPutInPlayTargetPlayerEffect.java index 021f763d2f03..f3b662d77cfe 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryPutInPlayTargetPlayerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryPutInPlayTargetPlayerEffect.java @@ -1,12 +1,5 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common.search; -import java.util.List; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.SearchEffect; import mage.cards.CardsImpl; @@ -16,8 +9,10 @@ import mage.players.Player; import mage.target.common.TargetCardInLibrary; +import java.util.List; +import java.util.UUID; + /** - * * @author LevelX2 */ public class SearchLibraryPutInPlayTargetPlayerEffect extends SearchEffect { @@ -96,17 +91,17 @@ private void setText() { } else { sb.append("up to ").append(target.getMaxNumberOfTargets()).append(' '); } - sb.append(target.getTargetName()).append(" and put them onto the battlefield"); + sb.append(target.getTargetName()).append(", puts them onto the battlefield"); } else { - sb.append("a ").append(target.getTargetName()).append(" and put it onto the battlefield"); + sb.append("a ").append(target.getTargetName()).append(", puts it onto the battlefield"); } if (tapped) { sb.append(" tapped"); } if (forceShuffle) { - sb.append(". Then that player shuffles their library"); + sb.append(", then shuffles"); } else { - sb.append(". If that player does, they shuffle their library"); + sb.append(". If that player does, they shuffle"); } staticText = sb.toString(); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryPutOnLibraryEffect.java b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryPutOnLibraryEffect.java index d9923a55b0e2..006d6c4011dd 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryPutOnLibraryEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryPutOnLibraryEffect.java @@ -10,9 +10,9 @@ import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; +import mage.util.CardUtil; /** - * * @author BetaSteward_at_googlemail.com */ public class SearchLibraryPutOnLibraryEffect extends SearchEffect { @@ -70,14 +70,11 @@ public boolean apply(Game game, Ability source) { private void setText() { StringBuilder sb = new StringBuilder(); - sb.append("search your library for a ").append(target.getTargetName()); + sb.append("search your library for ").append(CardUtil.addArticle(target.getTargetName())); if (reveal) { - sb.append(" and reveal that card. Shuffle"); - } else { - sb.append(", then shuffle"); + sb.append(", reveal it"); } - sb.append(" your library and put that card on top of it"); + sb.append(", then shuffle and put that card on top"); staticText = sb.toString(); } - } diff --git a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryWithLessCMCPutInPlayEffect.java b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryWithLessCMCPutInPlayEffect.java index 8f4bb5f10551..cd7466c133f9 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryWithLessCMCPutInPlayEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryWithLessCMCPutInPlayEffect.java @@ -8,7 +8,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; @@ -28,7 +28,7 @@ public SearchLibraryWithLessCMCPutInPlayEffect() { public SearchLibraryWithLessCMCPutInPlayEffect(FilterCard filter) { super(Outcome.PutCreatureInPlay); this.filter = filter; - staticText = "Search your library for a " + filter.getMessage() + " with converted mana cost X or less, put it onto the battlefield, then shuffle your library"; + staticText = "Search your library for a " + filter.getMessage() + " with mana value X or less, put it onto the battlefield, then shuffle"; } public SearchLibraryWithLessCMCPutInPlayEffect(final SearchLibraryWithLessCMCPutInPlayEffect effect) { @@ -41,7 +41,7 @@ public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { FilterCard advancedFilter = filter.copy(); // never change static objects so copy the object here before - advancedFilter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, source.getManaCostsToPay().getX() + 1)); + advancedFilter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, source.getManaCostsToPay().getX() + 1)); TargetCardInLibrary target = new TargetCardInLibrary(advancedFilter); if (controller.searchLibrary(target, source, game)) { if (!target.getTargets().isEmpty()) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/search/SearchTargetGraveyardHandLibraryForCardNameAndExileEffect.java b/Mage/src/main/java/mage/abilities/effects/common/search/SearchTargetGraveyardHandLibraryForCardNameAndExileEffect.java index 2a4f1f90cca8..e7f8b3132e34 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/search/SearchTargetGraveyardHandLibraryForCardNameAndExileEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/search/SearchTargetGraveyardHandLibraryForCardNameAndExileEffect.java @@ -1,7 +1,6 @@ package mage.abilities.effects.common.search; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.OneShotEffect; @@ -16,8 +15,9 @@ import mage.target.TargetCard; import mage.target.common.TargetCardInLibrary; +import java.util.UUID; + /** - * * @author LevelX2 */ public abstract class SearchTargetGraveyardHandLibraryForCardNameAndExileEffect extends OneShotEffect { @@ -48,12 +48,11 @@ public SearchTargetGraveyardHandLibraryForCardNameAndExileEffect(final SearchTar } /** - * * @param game * @param source - * @param cardName name of the card to exile + * @param cardName name of the card to exile * @param targetPlayerId id of the target player to exile card name from his - * or her zones + * or her zones * @return */ public boolean applySearchAndExile(Game game, Ability source, String cardName, UUID targetPlayerId) { @@ -102,11 +101,14 @@ public boolean applySearchAndExile(Game game, Ability source, String cardName, U @Override public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } StringBuilder sb = new StringBuilder(); sb.append("search ").append(this.searchWhatText); sb.append(" graveyard, hand, and library for "); sb.append(this.searchForText); - sb.append(" and exile them. Then that player shuffles their library"); + sb.append(" and exile them. Then that player shuffles"); return sb.toString(); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/turn/SkipNextTurnSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/turn/SkipNextTurnSourceEffect.java index 9a37af2e79e5..1eb438073360 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/turn/SkipNextTurnSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/turn/SkipNextTurnSourceEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common.turn; import java.util.UUID; diff --git a/Mage/src/main/java/mage/abilities/effects/keyword/InvestigateEffect.java b/Mage/src/main/java/mage/abilities/effects/keyword/InvestigateEffect.java index 82d30662c216..5349e86a740d 100644 --- a/Mage/src/main/java/mage/abilities/effects/keyword/InvestigateEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/keyword/InvestigateEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.keyword; import mage.abilities.Ability; @@ -19,7 +14,7 @@ public class InvestigateEffect extends CreateTokenEffect { public InvestigateEffect() { super(new ClueArtifactToken()); - this.staticText = "Investigate. (Create a colorless Clue artifact token with \"{2}, Sacrifice this artifact: Draw a card.\")"; + this.staticText = "investigate. (Create a colorless Clue artifact token with \"{2}, Sacrifice this artifact: Draw a card.\")"; } public InvestigateEffect(final InvestigateEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/effects/keyword/SupportEffect.java b/Mage/src/main/java/mage/abilities/effects/keyword/SupportEffect.java index c650a130b7d9..c473476a1566 100644 --- a/Mage/src/main/java/mage/abilities/effects/keyword/SupportEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/keyword/SupportEffect.java @@ -23,7 +23,7 @@ public SupportEffect(Card card, int amount, boolean otherPermanent) { super(CounterType.P1P1.createInstance(0), StaticValue.get(1)); this.amountSupportTargets = StaticValue.get(amount); this.otherPermanent = otherPermanent; - if (card.isInstant() || card.isSorcery()) { + if (card.isInstantOrSorcery()) { card.getSpellAbility().addTarget(new TargetCreaturePermanent(0, amount, new FilterCreaturePermanent("target creatures"), false)); } staticText = setText(); diff --git a/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalColorlessManaEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalColorlessManaEffect.java index 36edf6a502bd..407588878dfb 100644 --- a/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalColorlessManaEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalColorlessManaEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.mana; import mage.Mana; diff --git a/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaEffect.java index c52e0744fb98..a4c42de4736e 100644 --- a/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.mana; import mage.Mana; diff --git a/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaOfAnyColorEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaOfAnyColorEffect.java index 6e2bb565063a..82060231a4a7 100644 --- a/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaOfAnyColorEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaOfAnyColorEffect.java @@ -9,6 +9,7 @@ import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.mana.builder.ConditionalManaBuilder; import mage.choices.ChoiceColor; +import mage.constants.MultiAmountType; import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; @@ -87,29 +88,26 @@ public Mana produceMana(Game game, Ability source) { if (game != null) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - ConditionalMana mana = null; int value = amount.calculate(game, source, this); - ChoiceColor choice = new ChoiceColor(true); - for (int i = 0; i < value; i++) { - if (choice.getChoice() == null) { + if (value > 0) { + if (oneChoice || value == 1) { + ChoiceColor choice = new ChoiceColor(true); controller.choose(outcome, choice, game); - } - if (choice.getChoice() == null) { - return null; - } - if (oneChoice) { - mana = new ConditionalMana(manaBuilder.setMana(choice.getMana(value), source, game).build()); - break; - } else { - if (mana == null) { - mana = new ConditionalMana(manaBuilder.setMana(choice.getMana(1), source, game).build()); - } else { - mana.add(choice.getMana(1)); + if (choice.getChoice() == null) { + return null; } - choice.clearChoice(); + return new ConditionalMana(manaBuilder.setMana(choice.getMana(value), source, game).build()); } + List manaStrings = new ArrayList<>(5); + manaStrings.add("W"); + manaStrings.add("U"); + manaStrings.add("B"); + manaStrings.add("R"); + manaStrings.add("G"); + List choices = controller.getMultiAmount(this.outcome, manaStrings, 0, value, MultiAmountType.MANA, game); + Mana mana = new Mana(choices.get(0), choices.get(1), choices.get(2), choices.get(3), choices.get(4), 0, 0, 0); + return new ConditionalMana(manaBuilder.setMana(mana, source, game).build()); } - return mana; } } return new Mana(); diff --git a/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaOfTwoDifferentColorsEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaOfTwoDifferentColorsEffect.java index d4dba97fdf15..78184818235d 100644 --- a/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaOfTwoDifferentColorsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaOfTwoDifferentColorsEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.mana; import mage.ConditionalMana; diff --git a/Mage/src/main/java/mage/abilities/effects/mana/AddManaInAnyCombinationEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddManaInAnyCombinationEffect.java index d98ecfaec079..30490589f2cf 100644 --- a/Mage/src/main/java/mage/abilities/effects/mana/AddManaInAnyCombinationEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/mana/AddManaInAnyCombinationEffect.java @@ -1,11 +1,5 @@ package mage.abilities.effects.mana; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; import mage.Mana; import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; @@ -13,10 +7,14 @@ import mage.abilities.mana.ManaOptions; import mage.constants.ColoredManaSymbol; import mage.constants.ManaType; +import mage.constants.MultiAmountType; import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; +import java.util.*; +import java.util.stream.Collectors; + /** * @author LevelX2 */ @@ -27,7 +25,13 @@ public class AddManaInAnyCombinationEffect extends ManaEffect { private final DynamicValue netAmount; public AddManaInAnyCombinationEffect(int amount) { - this(StaticValue.get(amount), StaticValue.get(amount), ColoredManaSymbol.B, ColoredManaSymbol.U, ColoredManaSymbol.R, ColoredManaSymbol.W, ColoredManaSymbol.G); + this(StaticValue.get(amount), StaticValue.get(amount), + ColoredManaSymbol.W, + ColoredManaSymbol.U, + ColoredManaSymbol.B, + ColoredManaSymbol.R, + ColoredManaSymbol.G + ); } public AddManaInAnyCombinationEffect(int amount, ColoredManaSymbol... coloredManaSymbols) { @@ -106,26 +110,20 @@ public List getNetMana(Game game, Ability source) { public Mana produceMana(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { + int size = manaSymbols.size(); Mana mana = new Mana(); - int amountOfManaLeft = amount.calculate(game, source, this); - int maxAmount = amountOfManaLeft; - - while (amountOfManaLeft > 0 && player.canRespond()) { - for (ColoredManaSymbol coloredManaSymbol : manaSymbols) { - int number = player.getAmount(0, amountOfManaLeft, "Distribute mana by color (" + mana.count() - + " of " + maxAmount + " done). How many " + coloredManaSymbol.getColorHtmlName() + " mana to add (enter 0 to pass to next color)?", game); - if (number > 0) { - for (int i = 0; i < number; i++) { - mana.add(new Mana(coloredManaSymbol)); - } - amountOfManaLeft -= number; - } - if (amountOfManaLeft == 0) { - break; - } + List manaStrings = new ArrayList<>(size); + for (ColoredManaSymbol coloredManaSymbol : manaSymbols) { + manaStrings.add(coloredManaSymbol.toString()); + } + List manaList = player.getMultiAmount(this.outcome, manaStrings, 0, amount.calculate(game, source, this), MultiAmountType.MANA, game); + for (int i = 0; i < size; i++) { + ColoredManaSymbol coloredManaSymbol = manaSymbols.get(i); + int amount = manaList.get(i); + for (int j = 0; j < amount; j++) { + mana.add(new Mana(coloredManaSymbol)); } } - return mana; } return null; @@ -134,7 +132,7 @@ public Mana produceMana(Game game, Ability source) { @Override public Set getProducableManaTypes(Game game, Ability source) { Set manaTypes = new HashSet<>(); - for(ColoredManaSymbol coloredManaSymbol: manaSymbols) { + for (ColoredManaSymbol coloredManaSymbol : manaSymbols) { if (coloredManaSymbol.equals(ColoredManaSymbol.B)) { manaTypes.add(ManaType.BLACK); } @@ -156,7 +154,8 @@ public Set getProducableManaTypes(Game game, Ability source) { private String setText() { StringBuilder sb = new StringBuilder("Add "); - sb.append(CardUtil.numberToText(amount.toString())); + String amountString = CardUtil.numberToText(amount.toString()); + sb.append(amountString); sb.append(" mana in any combination of "); if (manaSymbols.size() == 5) { sb.append("colors"); @@ -170,6 +169,10 @@ private String setText() { sb.append('{').append(coloredManaSymbol.toString()).append('}'); } } + if (amountString.equals("X")) { + sb.append(", where X is "); + sb.append(amount.getMessage()); + } return sb.toString(); } } diff --git a/Mage/src/main/java/mage/abilities/effects/mana/AddManaToManaPoolSourceControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddManaToManaPoolSourceControllerEffect.java index 455df4f3ce95..9d68d0abb381 100644 --- a/Mage/src/main/java/mage/abilities/effects/mana/AddManaToManaPoolSourceControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/mana/AddManaToManaPoolSourceControllerEffect.java @@ -1,9 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - package mage.abilities.effects.mana; import mage.Mana; diff --git a/Mage/src/main/java/mage/abilities/effects/mana/AddManaToManaPoolTargetControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddManaToManaPoolTargetControllerEffect.java index 9d9c94cd1328..5aba6c9f9f41 100644 --- a/Mage/src/main/java/mage/abilities/effects/mana/AddManaToManaPoolTargetControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/mana/AddManaToManaPoolTargetControllerEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.mana; import mage.Mana; diff --git a/Mage/src/main/java/mage/abilities/effects/mana/DoUnlessAnyPlayerPaysManaEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/DoUnlessAnyPlayerPaysManaEffect.java index 9ffd20d471c1..17dd216dca38 100644 --- a/Mage/src/main/java/mage/abilities/effects/mana/DoUnlessAnyPlayerPaysManaEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/mana/DoUnlessAnyPlayerPaysManaEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.mana; import mage.MageObject; diff --git a/Mage/src/main/java/mage/abilities/effects/mana/DynamicManaEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/DynamicManaEffect.java index e0d80d1d8d88..d14c147c51e1 100644 --- a/Mage/src/main/java/mage/abilities/effects/mana/DynamicManaEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/mana/DynamicManaEffect.java @@ -5,7 +5,7 @@ import mage.abilities.Mode; import mage.abilities.dynamicvalue.DynamicValue; import mage.choices.ChoiceColor; -import mage.constants.Outcome; +import mage.constants.MultiAmountType; import mage.game.Game; import mage.players.Player; @@ -143,18 +143,23 @@ public Mana produceMana(Game game, Ability source) { computedMana.setColorless(count); } else if (baseMana.getAny() > 0) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - ChoiceColor choiceColor = new ChoiceColor(true); - for (int i = 0; i < count; i++) { - if (!choiceColor.isChosen()) { - if (!controller.choose(Outcome.Benefit, choiceColor, game)) { - return computedMana; - } - } - choiceColor.increaseMana(computedMana); - if (!oneChoice) { - choiceColor.clearChoice(); + if (controller != null && count > 0) { + if (oneChoice || count == 1) { + ChoiceColor choice = new ChoiceColor(true); + controller.choose(outcome, choice, game); + if (choice.getChoice() == null) { + return computedMana; } + computedMana.add(choice.getMana(count)); + } else { + List manaStrings = new ArrayList<>(5); + manaStrings.add("W"); + manaStrings.add("U"); + manaStrings.add("B"); + manaStrings.add("R"); + manaStrings.add("G"); + List choices = controller.getMultiAmount(this.outcome, manaStrings, 0, count, MultiAmountType.MANA, game); + computedMana.add(new Mana(choices.get(0), choices.get(1), choices.get(2), choices.get(3), choices.get(4), 0, 0, 0)); } } } else { diff --git a/Mage/src/main/java/mage/abilities/hint/common/LandsYouControlHint.java b/Mage/src/main/java/mage/abilities/hint/common/LandsYouControlHint.java new file mode 100644 index 000000000000..2c398cb500c8 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/hint/common/LandsYouControlHint.java @@ -0,0 +1,26 @@ +package mage.abilities.hint.common; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.common.LandsYouControlCount; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.game.Game; + +/** + * @author TheElk801 + */ +public enum LandsYouControlHint implements Hint { + + instance; + private static final Hint hint = new ValueHint("Lands you control", LandsYouControlCount.instance); + + @Override + public String getText(Game game, Ability ability) { + return hint.getText(game, ability); + } + + @Override + public Hint copy() { + return instance; + } +} diff --git a/Mage/src/main/java/mage/abilities/keyword/AssistAbility.java b/Mage/src/main/java/mage/abilities/keyword/AssistAbility.java index 5eb237252871..aa638d229513 100644 --- a/Mage/src/main/java/mage/abilities/keyword/AssistAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/AssistAbility.java @@ -94,8 +94,9 @@ public ManaOptions getManaOptions(Ability source, Game game, ManaCost unpaid) { } // AI can't use assist (can't ask another player to help), maybe in teammode it can be enabled, but tests must works all the time + // Outcome.AIDontUseIt Player controller = game.getPlayer(source.getControllerId()); - if (controller != null && !controller.isTestMode() && !controller.isHuman()) { + if (controller != null && controller.isComputer()) { return options; } @@ -170,7 +171,7 @@ public boolean apply(Game game, Ability source) { if (controller != null && spell != null && targetPlayer != null) { // AI can't assist other players, maybe for teammates only (but tests must work as normal) int amountToPay = 0; - if (targetPlayer.isHuman() || targetPlayer.isTestMode()) { + if (!targetPlayer.isComputer()) { amountToPay = targetPlayer.announceXMana(0, unpaid.getMana().getGeneric(), "How much mana to pay as assist for " + controller.getName() + "?", game, source); } diff --git a/Mage/src/main/java/mage/abilities/keyword/AuraSwapAbility.java b/Mage/src/main/java/mage/abilities/keyword/AuraSwapAbility.java index 86bb3d81429d..e81d2526dea5 100644 --- a/Mage/src/main/java/mage/abilities/keyword/AuraSwapAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/AuraSwapAbility.java @@ -10,7 +10,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.other.AuraCardCanAttachToPermanentId; +import mage.filter.predicate.card.AuraCardCanAttachToPermanentId; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage/src/main/java/mage/abilities/keyword/BoastAbility.java b/Mage/src/main/java/mage/abilities/keyword/BoastAbility.java index 003a5af3ad19..f6dfc680aaae 100644 --- a/Mage/src/main/java/mage/abilities/keyword/BoastAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/BoastAbility.java @@ -69,7 +69,7 @@ public int getMaxActivationsPerTurn(Game game) { @Override public String getRule() { - return "Boast — " + super.getRule() + " (Activate this ability only if this creature attacked this turn and only once each turn.)"; + return "Boast — " + super.getRule() + " (Activate only if this creature attacked this turn and only once each turn.)"; } private static final class BoastTwiceAbility extends SimpleStaticAbility { diff --git a/Mage/src/main/java/mage/abilities/keyword/BountyAbility.java b/Mage/src/main/java/mage/abilities/keyword/BountyAbility.java index f4f49dd492d0..223fda229dae 100644 --- a/Mage/src/main/java/mage/abilities/keyword/BountyAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/BountyAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.keyword; import mage.abilities.common.DiesCreatureTriggeredAbility; diff --git a/Mage/src/main/java/mage/abilities/keyword/BuybackAbility.java b/Mage/src/main/java/mage/abilities/keyword/BuybackAbility.java index c13c08c62e2e..44bab1cb43bd 100644 --- a/Mage/src/main/java/mage/abilities/keyword/BuybackAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/BuybackAbility.java @@ -79,13 +79,13 @@ public int reduceCost(int genericManaToReduce) { if (cost instanceof ManaCostsImpl) { for (Object c : (ManaCostsImpl) cost) { if (c instanceof GenericManaCost) { - int newCostCMC = ((GenericManaCost) c).convertedManaCost() - amountToReduceBy - genericManaToReduce; + int newCostCMC = ((GenericManaCost) c).manaValue() - amountToReduceBy - genericManaToReduce; foundCostToReduce = true; if (newCostCMC > 0) { amountToReduceBy += genericManaToReduce; } else { - amountToReduce = ((GenericManaCost) c).convertedManaCost() - amountToReduceBy; - amountToReduceBy = ((GenericManaCost) c).convertedManaCost(); + amountToReduce = ((GenericManaCost) c).manaValue() - amountToReduceBy; + amountToReduceBy = ((GenericManaCost) c).manaValue(); } } } diff --git a/Mage/src/main/java/mage/abilities/keyword/CanBlockSpaceflightAbility.java b/Mage/src/main/java/mage/abilities/keyword/CanBlockSpaceflightAbility.java index 791618905768..6e8a88f23d66 100644 --- a/Mage/src/main/java/mage/abilities/keyword/CanBlockSpaceflightAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/CanBlockSpaceflightAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.keyword; import java.io.ObjectStreamException; diff --git a/Mage/src/main/java/mage/abilities/keyword/CascadeAbility.java b/Mage/src/main/java/mage/abilities/keyword/CascadeAbility.java index fbb1e5df7bea..43fd54a88dbe 100644 --- a/Mage/src/main/java/mage/abilities/keyword/CascadeAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/CascadeAbility.java @@ -1,36 +1,59 @@ package mage.abilities.keyword; import mage.ApprovingObject; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.Cards; -import mage.cards.CardsImpl; +import mage.cards.*; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.FilterCard; import mage.filter.StaticFilters; -import mage.filter.common.FilterLandCard; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; import mage.players.Player; import mage.target.common.TargetCardInExile; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + /** + * Cascade + * A keyword ability that may let a player cast a random extra spell for no cost. See rule 702.84, “Cascade.” + *

+ * 702.84. Cascade + *

+ * 702.84a Cascade is a triggered ability that functions only while the spell with cascade is on the stack. + * “Cascade” means “When you cast this spell, exile cards from the top of your library until you exile a + * nonland card whose converted mana cost is less than this spell’s converted mana cost. You may cast that + * card without paying its mana cost. Then put all cards exiled this way that weren’t cast on the bottom + * of your library in a random order.” + *

+ * 702.84b If an effect allows a player to take an action with one or more of the exiled cards “as you cascade,” + * the player may take that action after they have finished exiling cards due to the cascade ability. This action + * is taken before choosing whether to cast the last exiled card or, if no appropriate card was exiled, before + * putting the exiled cards on the bottom of their library in a random order. + *

+ * 702.84c If a spell has multiple instances of cascade, each triggers separately. + * * @author BetaSteward_at_googlemail.com */ public class CascadeAbility extends TriggeredAbilityImpl { //20091005 - 702.82 + //20210215 - 702.84a - Updated Cascade rule + + // can't use singletone due rules: + // 702.84c If a spell has multiple instances of cascade, each triggers separately. private static final String REMINDERTEXT = " (When you cast this spell, " + "exile cards from the top of your library until you exile a " - + "nonland card that costs less." - + " You may cast it without paying its mana cost. " - + "Put the exiled cards on the bottom in a random order.)"; + + "nonland card whose mana value is less than this spell's mana value. " + + "You may cast that spell without paying its mana cost " + + "if its mana value is less than this spell's mana value. " + + "Then put all cards exiled this way that weren't cast on the bottom of your library in a random order.)"; private final boolean withReminder; - private static final FilterCard filter = new FilterLandCard("land card (to put onto the battlefield)"); public CascadeAbility() { this(true); @@ -86,52 +109,83 @@ private CascadeEffect(CascadeEffect effect) { @Override public boolean apply(Game game, Ability source) { - Card card; Player controller = game.getPlayer(source.getControllerId()); if (controller == null) { return false; } - Cards cards = new CardsImpl(); - card = game.getCard(source.getSourceId()); - if (card == null) { + Card sourceCard = game.getCard(source.getSourceId()); + if (sourceCard == null) { return false; } - int sourceCost = card.getConvertedManaCost(); - do { - card = controller.getLibrary().getFromTop(game); - if (card == null) { + + // exile cards from the top of your library until you exile a nonland card whose converted mana cost is less than this spell's converted mana cost + Cards cardsToExile = new CardsImpl(); + int sourceCost = sourceCard.getManaValue(); + Card cardToCast = null; + for (Card card : controller.getLibrary().getCards(game)) { + cardsToExile.add(card); + if (!card.isLand() && card.getManaValue() < sourceCost) { + cardToCast = card; break; } - cards.add(card); - controller.moveCards(card, Zone.EXILED, source, game); - } while (controller.canRespond() - && (card.isLand() || card.getConvertedManaCost() >= sourceCost)); - + } + controller.moveCards(cardsToExile, Zone.EXILED, source, game); controller.getLibrary().reset(); // set back empty draw state if that caused an empty draw + // additional replacement effect: As you cascade, you may put a land card from among the exiled cards onto the battlefield tapped GameEvent event = GameEvent.getEvent(GameEvent.EventType.CASCADE_LAND, source.getSourceId(), source, source.getControllerId(), 0); game.replaceEvent(event); if (event.getAmount() > 0) { - TargetCardInExile target = new TargetCardInExile( - 0, event.getAmount(), StaticFilters.FILTER_CARD_LAND, null, true - ); - controller.choose(Outcome.PutCardInPlay, cards, target, game); + TargetCardInExile target = new TargetCardInExile(0, event.getAmount(), StaticFilters.FILTER_CARD_LAND, null, true); + target.withChooseHint("land to put onto battlefield tapped"); + controller.choose(Outcome.PutCardInPlay, cardsToExile, target, game); controller.moveCards( new CardsImpl(target.getTargets()).getCards(game), Zone.BATTLEFIELD, source, game, true, false, false, null ); } - if (card != null && controller.chooseUse( - outcome, "Use cascade effect on " + card.getLogName() + '?', source, game - )) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); + + // You may cast that spell without paying its mana cost if its converted mana cost is less than this spell's converted mana cost. + List partsToCast = new ArrayList<>(); + if (cardToCast != null) { + if (cardToCast instanceof SplitCard) { + partsToCast.add(((SplitCard) cardToCast).getLeftHalfCard()); + partsToCast.add(((SplitCard) cardToCast).getRightHalfCard()); + partsToCast.add(cardToCast); + } else if (cardToCast instanceof AdventureCard) { + partsToCast.add(((AdventureCard) cardToCast).getSpellCard()); + partsToCast.add(cardToCast); + } else if (cardToCast instanceof ModalDoubleFacesCard) { + partsToCast.add(((ModalDoubleFacesCard) cardToCast).getLeftHalfCard()); + partsToCast.add(((ModalDoubleFacesCard) cardToCast).getRightHalfCard()); + } else { + partsToCast.add(cardToCast); + } + // remove too big cmc + partsToCast.removeIf(card -> card.getManaValue() >= sourceCost); + // remove non spells + partsToCast.removeIf(card -> card.getSpellAbility() == null); + } + + String partsInfo = partsToCast.stream() + .map(MageObject::getIdName) + .collect(Collectors.joining(" or ")); + if (cardToCast != null + && partsToCast.size() > 0 + && controller.chooseUse(outcome, "Cast spell without paying its mana cost (" + partsInfo + ")?", source, game)) { + try { + // enable free cast for all compatible parts + partsToCast.forEach(card -> game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE)); + controller.cast(controller.chooseAbilityForCast(cardToCast, game, true), + game, true, new ApprovingObject(source, game)); + } finally { + partsToCast.forEach(card -> game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null)); + } } - // Move the remaining cards to the buttom of the library in a random order - cards.removeIf(uuid -> game.getState().getZone(uuid) != Zone.EXILED); - return controller.putCardsOnBottomOfLibrary(cards, game, source, false); + + // Then put all cards exiled this way that weren't cast on the bottom of your library in a random order. + cardsToExile.removeIf(uuid -> game.getState().getZone(uuid) != Zone.EXILED); + return controller.putCardsOnBottomOfLibrary(cardsToExile, game, source, false); } @Override diff --git a/Mage/src/main/java/mage/abilities/keyword/ChampionAbility.java b/Mage/src/main/java/mage/abilities/keyword/ChampionAbility.java index ee8a5f517a03..c0e785dd9afb 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ChampionAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ChampionAbility.java @@ -22,7 +22,7 @@ import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicate; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; diff --git a/Mage/src/main/java/mage/abilities/keyword/ChangelingAbility.java b/Mage/src/main/java/mage/abilities/keyword/ChangelingAbility.java index 2be76663b52b..e83311c5867f 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ChangelingAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ChangelingAbility.java @@ -4,7 +4,6 @@ import mage.abilities.effects.common.continuous.IsAllCreatureTypesSourceEffect; import mage.constants.Zone; - /** * October 1, 2012 * 702.71. Changeling @@ -16,17 +15,25 @@ */ public class ChangelingAbility extends StaticAbility { + private final boolean changelingText; + public ChangelingAbility() { + this(true); + } + + public ChangelingAbility(boolean changelingText) { super(Zone.ALL, new IsAllCreatureTypesSourceEffect()); + this.changelingText = changelingText; } private ChangelingAbility(final ChangelingAbility ability) { super(ability); + this.changelingText = ability.changelingText; } @Override public String getRule() { - return "Changeling (This card is every creature type.)"; + return changelingText ? "Changeling (This card is every creature type.)" : super.getRule(); } @Override diff --git a/Mage/src/main/java/mage/abilities/keyword/ChannelAbility.java b/Mage/src/main/java/mage/abilities/keyword/ChannelAbility.java index cb32349f5b80..0f1ecf865423 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ChannelAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ChannelAbility.java @@ -37,7 +37,7 @@ public String getRule() { StringBuilder sb = new StringBuilder("Channel — "); sb.append(super.getRule()); if(this.timing == TimingRule.SORCERY) { - sb.append(" Activate this ability only any time you could cast a sorcery."); + sb.append(" Activate only as a sorcery."); } return sb.toString(); } diff --git a/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java b/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java index 79d000798ed8..4da2477b775a 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java @@ -22,7 +22,6 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; -import mage.game.stack.StackObject; import mage.players.Player; import mage.target.common.TargetControlledPermanent; @@ -269,10 +268,7 @@ public boolean apply(Game game, Ability source) { if (controller != null && conspiredSpell != null) { Card card = game.getCard(conspiredSpell.getSourceId()); if (card != null) { - StackObject newStackObject = conspiredSpell.createCopyOnStack(game, source, source.getControllerId(), true); - if (newStackObject instanceof Spell && !game.isSimulation()) { - game.informPlayers(controller.getLogName() + ((Spell) newStackObject).getActivatedMessage(game)); - } + conspiredSpell.createCopyOnStack(game, source, source.getControllerId(), true); return true; } } diff --git a/Mage/src/main/java/mage/abilities/keyword/CyclingAbility.java b/Mage/src/main/java/mage/abilities/keyword/CyclingAbility.java index e236cd7fb946..83fb2c1071a7 100644 --- a/Mage/src/main/java/mage/abilities/keyword/CyclingAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/CyclingAbility.java @@ -9,6 +9,7 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.target.common.TargetCardInLibrary; +import mage.util.CardUtil; /** * @author BetaSteward_at_googlemail.com @@ -47,11 +48,11 @@ public CyclingAbility copy() { public String getRule() { StringBuilder rule = new StringBuilder(this.text); if (cost instanceof ManaCost) { - rule.append(' '); + rule.append(' ').append(cost.getText()); } else { - rule.append("—"); + rule.append("—").append(CardUtil.getTextWithFirstCharUpperCase(cost.getText())).append('.'); } - rule.append(cost.getText()).append(" (").append(super.getRule(true)).append(")"); + rule.append(" (").append(super.getRule(true)).append(")"); return rule.toString(); } } diff --git a/Mage/src/main/java/mage/abilities/keyword/DemonstrateAbility.java b/Mage/src/main/java/mage/abilities/keyword/DemonstrateAbility.java new file mode 100644 index 000000000000..36dc94878100 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/keyword/DemonstrateAbility.java @@ -0,0 +1,70 @@ +package mage.abilities.keyword; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CastSourceTriggeredAbility; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.common.TargetOpponent; + +/** + * @author TheElk801 + */ +public class DemonstrateAbility extends CastSourceTriggeredAbility { + + public DemonstrateAbility() { + super(new DemonstrateEffect(), true); + } + + private DemonstrateAbility(final DemonstrateAbility ability) { + super(ability); + } + + @Override + public DemonstrateAbility copy() { + return new DemonstrateAbility(this); + } + + @Override + public String getRule() { + return "Demonstrate (When you cast this spell, you may copy it. If you do, " + + "choose an opponent to also copy it. Players may choose new targets for their copies.)"; + } +} + +class DemonstrateEffect extends OneShotEffect { + + DemonstrateEffect() { + super(Outcome.Benefit); + } + + private DemonstrateEffect(final DemonstrateEffect effect) { + super(effect); + } + + @Override + public DemonstrateEffect copy() { + return new DemonstrateEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Spell spell = (Spell) getValue("spellCast"); + if (spell == null) { + return false; + } + spell.createCopyOnStack(game, source, source.getControllerId(), true); + TargetOpponent target = new TargetOpponent(true); + controller.choose(outcome, target, source.getSourceId(), game); + if (game.getPlayer(target.getFirstTarget()) != null) { + spell.createCopyOnStack(game, source, target.getFirstTarget(), true); + } + return true; + } +} diff --git a/Mage/src/main/java/mage/abilities/keyword/DevoidAbility.java b/Mage/src/main/java/mage/abilities/keyword/DevoidAbility.java index b6217bb91585..2054a0623f59 100644 --- a/Mage/src/main/java/mage/abilities/keyword/DevoidAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/DevoidAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.keyword; import mage.ObjectColor; diff --git a/Mage/src/main/java/mage/abilities/keyword/EchoAbility.java b/Mage/src/main/java/mage/abilities/keyword/EchoAbility.java index afdda45c0de6..451fa864ca45 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EchoAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EchoAbility.java @@ -1,4 +1,3 @@ - package mage.abilities.keyword; import mage.abilities.TriggeredAbilityImpl; @@ -15,7 +14,6 @@ import java.util.UUID; /** - * * @author Backfir3 */ public class EchoAbility extends TriggeredAbilityImpl { @@ -117,6 +115,9 @@ public String getRule() { if (echoCosts != null) { sb.append(echoCosts.getText()); } + if (!manaEcho) { + sb.append('.'); + } } sb.append(" (At the beginning of your upkeep, if this came under your control since the beginning of your last upkeep, sacrifice it unless you pay its echo cost.)"); return sb.toString(); diff --git a/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java b/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java index e5dadd0373e5..8de7be818fff 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java @@ -53,7 +53,7 @@ public ActivationStatus canActivate(UUID playerId, Game game) { if (controller != null) { for (Permanent creature : game.getBattlefield().getActivePermanents( new FilterControlledCreaturePermanent(), this.getControllerId(), this.getSourceId(), game)) { - ManaCost costToPay = CardUtil.reduceCost(emergeCost.copy(), creature.getConvertedManaCost()); + ManaCost costToPay = CardUtil.reduceCost(emergeCost.copy(), creature.getManaValue()); if (costToPay.canPay(this, this, this.getControllerId(), game)) { return ActivationStatus.getTrue(this, game); } @@ -67,7 +67,7 @@ public ActivationStatus canActivate(UUID playerId, Game game) { public ManaOptions getMinimumCostToActivate(UUID playerId, Game game) { int maxCMC = 0; for (Permanent creature : game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), playerId, this.getSourceId(), game)) { - int cmc = creature.getConvertedManaCost(); + int cmc = creature.getManaValue(); if (cmc > maxCMC) { maxCMC = cmc; } @@ -91,7 +91,7 @@ public boolean activate(Game game, boolean noMana) { if (controller.choose(Outcome.Sacrifice, target, this.getSourceId(), game)) { Permanent creature = game.getPermanent(target.getFirstTarget()); if (creature != null) { - CardUtil.reduceCost(this, creature.getConvertedManaCost()); + CardUtil.reduceCost(this, creature.getManaValue()); if (super.activate(game, false)) { if (creature.sacrifice(this, game)) { return true; @@ -117,6 +117,6 @@ public String getRule(boolean all) { @Override public String getRule() { - return "Emerge " + emergeCost.getText() + " (You may cast this spell by sacrificing a creature and paying the emerge cost reduced by that creature's converted mana cost.)"; + return "Emerge " + emergeCost.getText() + " (You may cast this spell by sacrificing a creature and paying the emerge cost reduced by that creature's mana value.)"; } } diff --git a/Mage/src/main/java/mage/abilities/keyword/EncoreAbility.java b/Mage/src/main/java/mage/abilities/keyword/EncoreAbility.java index 472a684bde52..237e7acbf8c2 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EncoreAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EncoreAbility.java @@ -6,6 +6,7 @@ import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.ExileSourceFromGraveCost; +import mage.abilities.dynamicvalue.common.OpponentsCount; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.RequirementEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; @@ -87,7 +88,7 @@ public boolean apply(Game game, Ability source) { EmptyToken token = new EmptyToken(); CardUtil.copyTo(token).from(card, game); Set addedTokens = new HashSet<>(); - int opponentCount = game.getOpponents(source.getControllerId()).size(); + int opponentCount = OpponentsCount.instance.calculate(game, source, this); if (opponentCount < 1) { return false; } @@ -95,6 +96,9 @@ public boolean apply(Game game, Ability source) { Iterator it = token.getLastAddedTokenIds().iterator(); while (it.hasNext()) { for (UUID playerId : game.getOpponents(source.getControllerId())) { + if (game.getPlayer(playerId) == null) { + continue; + } UUID tokenId = it.next(); MageObjectReference mageObjectReference = new MageObjectReference(tokenId, game); game.addEffect(new EncoreRequirementEffect( diff --git a/Mage/src/main/java/mage/abilities/keyword/EscapeAbility.java b/Mage/src/main/java/mage/abilities/keyword/EscapeAbility.java index f569d074c04f..f4a009a3cfd6 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EscapeAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EscapeAbility.java @@ -7,7 +7,7 @@ import mage.constants.SpellAbilityType; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.target.common.TargetCardInYourGraveyard; import mage.util.CardUtil; diff --git a/Mage/src/main/java/mage/abilities/keyword/ForecastAbility.java b/Mage/src/main/java/mage/abilities/keyword/ForecastAbility.java index 5cb06f17dcbe..c1a7c99fdd66 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ForecastAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ForecastAbility.java @@ -46,6 +46,6 @@ public ForecastAbility copy() { @Override public String getRule() { - return "Forecast — " + super.getRule() + " (Activate this ability only during your upkeep and only once each turn)"; + return "Forecast — " + super.getRule() + " (Activate only during your upkeep and only once each turn)"; } } diff --git a/Mage/src/main/java/mage/abilities/keyword/ForetellAbility.java b/Mage/src/main/java/mage/abilities/keyword/ForetellAbility.java index 3d236196a7b8..abd1b080544c 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ForetellAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ForetellAbility.java @@ -48,7 +48,9 @@ public ForetellAbility(Card card, String foretellCost, String foretellSplitCost) // exile the card and it can't be cast the turn it was foretold this.addEffect(new ForetellExileEffect(card, foretellCost, foretellSplitCost)); // look at face-down card anytime - addSubAbility(new SimpleStaticAbility(Zone.ALL, new ForetellLookAtCardEffect())); + Ability ability = new SimpleStaticAbility(Zone.ALL, new ForetellLookAtCardEffect()); + ability.setControllerId(controllerId); // if not set, anyone can look at the card in exile + addSubAbility(ability); this.setRuleVisible(true); this.addWatcher(new ForetoldWatcher()); } @@ -292,6 +294,12 @@ public class ForetellCostAbility extends SpellAbility { public ForetellCostAbility(String foretellCost) { super(null, "Testing", Zone.EXILED, SpellAbilityType.BASE_ALTERNATE, SpellAbilityCastMode.NORMAL); + // Needed for Dream Devourer and Ethereal Valkyrie reducing the cost of a colorless CMC 2 or less spell to 0 + // CardUtil.reduceCost returns an empty string in that case so we add a cost of 0 here + // https://github.com/magefree/mage/issues/7607 + if (foretellCost != null && foretellCost.isEmpty()) { + foretellCost = "{0}"; + } this.setAdditionalCostsRuleVisible(false); this.name = "Foretell " + foretellCost; this.addCost(new ManaCostsImpl(foretellCost)); diff --git a/Mage/src/main/java/mage/abilities/keyword/HauntAbility.java b/Mage/src/main/java/mage/abilities/keyword/HauntAbility.java index bc0b2eb49527..9bdc6d4fc7b3 100644 --- a/Mage/src/main/java/mage/abilities/keyword/HauntAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/HauntAbility.java @@ -13,23 +13,24 @@ import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; /** * 702.53. Haunt - * + *

* 702.53a Haunt is a triggered ability. "Haunt" on a permanent means "When this * permanent is put into a graveyard from the battlefield, exile it haunting * target creature." "Haunt" on an instant or sorcery spell means "When this * spell is put into a graveyard during its resolution, exile it haunting target * creature." - * + *

* 702.53b Cards that are in the exile zone as the result of a haunt ability * "haunt" the creature targeted by that ability. The phrase "creature it * haunts" refers to the object targeted by the haunt ability, regardless of * whether or not that object is still a creature. - * + *

* 702.53c Triggered abilities of cards with haunt that refer to the haunted * creature can trigger in the exile zone. * @@ -46,7 +47,7 @@ public HauntAbility(Card card, Effect effect) { addSubAbility(new HauntExileAbility(creatureHaunt)); } - public HauntAbility(final HauntAbility ability) { + private HauntAbility(final HauntAbility ability) { super(ability); this.usedFromExile = ability.usedFromExile; this.creatureHaunt = ability.creatureHaunt; @@ -59,51 +60,46 @@ public HauntAbility copy() { @Override public boolean checkEventType(GameEvent event, Game game) { - switch (event.getType()) { - case ENTERS_THE_BATTLEFIELD: - case ZONE_CHANGE: - return true; - } - return false; + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD + || event.getType() == GameEvent.EventType.ZONE_CHANGE; } @Override public boolean checkTrigger(GameEvent event, Game game) { switch (event.getType()) { case ENTERS_THE_BATTLEFIELD: - if (game.getState().getZone(getSourceId()) == Zone.BATTLEFIELD) { - return event.getTargetId().equals(getSourceId()); - } - break; + return game.getState().getZone(getSourceId()) == Zone.BATTLEFIELD + && event.getTargetId().equals(getSourceId()); case ZONE_CHANGE: - if (!usedFromExile - && game.getState().getZone(getSourceId()) == Zone.EXILED) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.isDiesEvent()) { - Card card = game.getCard(getSourceId()); - if (card != null - && game.getCard(event.getTargetId()) != null) { - String key = new StringBuilder("Haunting_") - .append(getSourceId().toString()) - .append(card.getZoneChangeCounter(game) - + (game.getPermanentOrLKIBattlefield(event.getTargetId())) - .getZoneChangeCounter(game)).toString(); - Object object = game.getState().getValue(key); - if (object instanceof FixedTarget) { - FixedTarget target = (FixedTarget) object; - if (target.getTarget() != null - && target.getTarget() - .equals(event.getTargetId())) { - usedFromExile = true; - return true; - } - } - } - } + if (usedFromExile + || game.getState().getZone(getSourceId()) != Zone.EXILED) { + return false; + } + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (!zEvent.isDiesEvent()) { + return false; + } + Card card = game.getCard(getSourceId()); + if (card == null + || game.getCard(event.getTargetId()) == null) { + return false; + } + String key = new StringBuilder("Haunting_") + .append(getSourceId().toString()) + .append(card.getZoneChangeCounter(game) + + (game.getPermanentOrLKIBattlefield(event.getTargetId())) + .getZoneChangeCounter(game)).toString(); + Object object = game.getState().getValue(key); + if (!(object instanceof FixedTarget)) { + return false; + } + FixedTarget target = (FixedTarget) object; + if (target.getTarget() != null + && target.getTarget() + .equals(event.getTargetId())) { + usedFromExile = true; + return true; } - break; - default: - return false; } return false; } @@ -134,7 +130,7 @@ public HauntExileAbility(boolean creatureHaunt) { } - public HauntExileAbility(final HauntExileAbility ability) { + private HauntExileAbility(final HauntExileAbility ability) { super(ability); this.creatureHaunt = ability.creatureHaunt; } @@ -171,12 +167,12 @@ public HauntExileAbility copy() { class HauntEffect extends OneShotEffect { - public HauntEffect() { + HauntEffect() { super(Outcome.Benefit); this.staticText = "exile it haunting target creature"; } - public HauntEffect(final HauntEffect effect) { + private HauntEffect(final HauntEffect effect) { super(effect); } @@ -187,27 +183,28 @@ public HauntEffect copy() { @Override public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); Card card = game.getCard(source.getSourceId()); - if (card != null) { - Permanent hauntedCreature = game.getPermanent(targetPointer.getFirst(game, source)); - // haunting card will only be moved to exile, if - if (hauntedCreature != null) { - if (card.moveToExile(source.getSourceId(), "Haunting", source, game)) { - // remember the haunted creature - String key = new StringBuilder("Haunting_") - .append(source.getSourceId().toString()) - .append(card.getZoneChangeCounter(game) - + hauntedCreature.getZoneChangeCounter(game)).toString(); // in case it is blinked - game.getState().setValue(key, new FixedTarget(targetPointer.getFirst(game, source))); - card.addInfo("hauntinfo", new StringBuilder("Haunting ").append(hauntedCreature.getLogName()).toString(), game); - hauntedCreature.addInfo("hauntinfo", new StringBuilder("Haunted by ").append(card.getLogName()).toString(), game); - if (!game.isSimulation()) { - game.informPlayers(new StringBuilder(card.getName()).append(" haunting ").append(hauntedCreature.getLogName()).toString()); - } - } - return true; - } + if (player == null || card == null) { + return false; } - return false; + Permanent hauntedCreature = game.getPermanent(targetPointer.getFirst(game, source)); + // haunting card will only be moved to exile, if + if (hauntedCreature == null) { + return false; + } + if (!player.moveCards(card, Zone.EXILED, source, game)) { + return true; + } + // remember the haunted creature + String key = new StringBuilder("Haunting_") + .append(source.getSourceId().toString()) + .append(card.getZoneChangeCounter(game) + + hauntedCreature.getZoneChangeCounter(game)).toString(); // in case it is blinked + game.getState().setValue(key, new FixedTarget(targetPointer.getFirst(game, source))); + card.addInfo("hauntinfo", new StringBuilder("Haunting ").append(hauntedCreature.getLogName()).toString(), game); + hauntedCreature.addInfo("hauntinfo", new StringBuilder("Haunted by ").append(card.getLogName()).toString(), game); + game.informPlayers(new StringBuilder(card.getName()).append(" haunting ").append(hauntedCreature.getLogName()).toString()); + return true; } } diff --git a/Mage/src/main/java/mage/abilities/keyword/IndestructibleAbility.java b/Mage/src/main/java/mage/abilities/keyword/IndestructibleAbility.java index 2286fc15c26b..09da61c1adb5 100644 --- a/Mage/src/main/java/mage/abilities/keyword/IndestructibleAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/IndestructibleAbility.java @@ -1,5 +1,6 @@ package mage.abilities.keyword; +import mage.abilities.MageSingleton; import mage.abilities.StaticAbility; import mage.abilities.icon.abilities.IndestructibleAbilityIcon; import mage.constants.Zone; @@ -21,7 +22,7 @@ * * @author BetaSteward_at_googlemail.com */ -public class IndestructibleAbility extends StaticAbility { +public class IndestructibleAbility extends StaticAbility implements MageSingleton { private static final IndestructibleAbility instance; diff --git a/Mage/src/main/java/mage/abilities/keyword/IngestAbility.java b/Mage/src/main/java/mage/abilities/keyword/IngestAbility.java index 75baf302ef2c..b34795ad6207 100644 --- a/Mage/src/main/java/mage/abilities/keyword/IngestAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/IngestAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.keyword; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/keyword/LeylineAbility.java b/Mage/src/main/java/mage/abilities/keyword/LeylineAbility.java index d2b31e2071f3..1fc20d2d029d 100644 --- a/Mage/src/main/java/mage/abilities/keyword/LeylineAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/LeylineAbility.java @@ -43,7 +43,7 @@ public LeylineAbility copy() { @Override public boolean askUseOpeningHandAction(Card card, Player player, Game game) { - return player.chooseUse(Outcome.PutCardInPlay, "Do you wish to put " + card.getName() + " on the battlefield?", this, game); + return player.chooseUse(Outcome.PutCardInPlay, "Put " + card.getName() + " on the battlefield?", this, game); } @Override diff --git a/Mage/src/main/java/mage/abilities/keyword/ModularAbility.java b/Mage/src/main/java/mage/abilities/keyword/ModularAbility.java index aacb15852f4c..726f241eee91 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ModularAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ModularAbility.java @@ -1,8 +1,5 @@ package mage.abilities.keyword; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.StaticAbility; import mage.abilities.common.DiesSourceTriggeredAbility; @@ -20,14 +17,16 @@ import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.Target; import mage.target.common.TargetArtifactPermanent; import mage.util.CardUtil; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + /** - * * 702.41. Modular - * + *

* 702.41a Modular represents both a static ability and a triggered ability. * "Modular N" means "This permanent enters the battlefield with N +1/+1 * counters on it" and "When this permanent is put into a graveyard from the @@ -35,7 +34,6 @@ * +1/+1 counter on this permanent." 702.41b If a creature has multiple * instances of modular, each one works separately. * - * * @author Loki, LevelX2 */ public class ModularAbility extends DiesSourceTriggeredAbility { @@ -45,8 +43,9 @@ public class ModularAbility extends DiesSourceTriggeredAbility { static { filter.add(CardType.CREATURE.getPredicate()); } - private int amount; - private boolean sunburst; + + private final int amount; + private final boolean sunburst; public ModularAbility(Card card, int amount) { this(card, amount, false); @@ -54,8 +53,7 @@ public ModularAbility(Card card, int amount) { public ModularAbility(Card card, int amount, boolean sunburst) { super(new ModularDistributeCounterEffect(), true); - Target target = new TargetArtifactPermanent(filter); - this.addTarget(target); + this.addTarget(new TargetArtifactPermanent(filter)); this.amount = amount; this.sunburst = sunburst; if (sunburst) { @@ -67,7 +65,7 @@ public ModularAbility(Card card, int amount, boolean sunburst) { } } - public ModularAbility(ModularAbility ability) { + private ModularAbility(ModularAbility ability) { super(ability); this.amount = ability.amount; this.sunburst = ability.sunburst; @@ -102,20 +100,19 @@ public String getRule() { } return sb.toString(); } - } class ModularStaticAbility extends StaticAbility { - private String ruleText; + private final String ruleText; - public ModularStaticAbility(int amount) { + ModularStaticAbility(int amount) { super(Zone.ALL, new EntersBattlefieldEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance(amount)))); ruleText = "This enters the battlefield with " + CardUtil.numberToText(amount, "a") + " +1/+1 counter" + (amount != 1 ? "s" : "") + " on it."; this.setRuleVisible(false); } - public ModularStaticAbility(final ModularStaticAbility ability) { + private ModularStaticAbility(final ModularStaticAbility ability) { super(ability); this.ruleText = ability.ruleText; } @@ -133,18 +130,12 @@ public String getRule() { class ModularDistributeCounterEffect extends OneShotEffect { - private static final FilterArtifactPermanent filter = new FilterArtifactPermanent("artifact creature"); - - static { - filter.add(CardType.CREATURE.getPredicate()); - } - - public ModularDistributeCounterEffect() { + ModularDistributeCounterEffect() { super(Outcome.BoostCreature); this.staticText = "you may put a +1/+1 counter on target artifact creature for each +1/+1 counter on this permanent"; } - public ModularDistributeCounterEffect(final ModularDistributeCounterEffect effect) { + private ModularDistributeCounterEffect(final ModularDistributeCounterEffect effect) { super(effect); } @@ -155,7 +146,7 @@ public ModularDistributeCounterEffect copy() { @Override public boolean apply(Game game, Ability source) { - Permanent sourcePermanent = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); + Permanent sourcePermanent = (Permanent) getValue("permanentLeftBattlefield"); Permanent targetArtifact = game.getPermanent(targetPointer.getFirst(game, source)); Player player = game.getPlayer(source.getControllerId()); if (sourcePermanent != null && targetArtifact != null && player != null) { diff --git a/Mage/src/main/java/mage/abilities/keyword/PartnerAbility.java b/Mage/src/main/java/mage/abilities/keyword/PartnerAbility.java index 936aa998e60b..232408de5cbc 100644 --- a/Mage/src/main/java/mage/abilities/keyword/PartnerAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/PartnerAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.keyword; import java.io.ObjectStreamException; diff --git a/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java b/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java index 500560a71fae..586ca686255e 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java @@ -99,7 +99,7 @@ public boolean canTarget(MageObject source, Game game) { // object is still a card and not a spell yet. So return only if the source object can't be a spell // otherwise the following FilterObject check will be applied if (source instanceof StackObject - || (!source.isInstant() && !source.isSorcery())) { + || !source.isInstantOrSorcery()) { return true; } } @@ -107,7 +107,7 @@ public boolean canTarget(MageObject source, Game game) { // Emrakul, the Aeons Torn if (filter instanceof FilterStackObject) { if (filter.match(source, game)) { - return (!source.isInstantOrSorcery()); + return !source.isInstantOrSorcery(); } } diff --git a/Mage/src/main/java/mage/abilities/keyword/ProwessAbility.java b/Mage/src/main/java/mage/abilities/keyword/ProwessAbility.java index dd242a70dfc4..f3d46f24d4f0 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ProwessAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ProwessAbility.java @@ -1,5 +1,3 @@ - - package mage.abilities.keyword; import mage.abilities.common.SpellCastControllerTriggeredAbility; @@ -10,20 +8,19 @@ import mage.filter.predicate.Predicates; /** - * * @author LevelX2 */ public class ProwessAbility extends SpellCastControllerTriggeredAbility { - private static final FilterSpell filterNonCreatureSpell = new FilterSpell("noncreature spell"); + private static final FilterSpell filterNonCreatureSpell = new FilterSpell("noncreature spell"); static { filterNonCreatureSpell.add(Predicates.not(CardType.CREATURE.getPredicate())); } public ProwessAbility() { - super(new BoostSourceEffect(1,1,Duration.EndOfTurn), false); - this.filter = filterNonCreatureSpell; + super(new BoostSourceEffect(1, 1, Duration.EndOfTurn), false); + this.filter = filterNonCreatureSpell; } public ProwessAbility(final ProwessAbility ability) { diff --git a/Mage/src/main/java/mage/abilities/keyword/RenownAbility.java b/Mage/src/main/java/mage/abilities/keyword/RenownAbility.java index d9a0685dd31e..a19837a68384 100644 --- a/Mage/src/main/java/mage/abilities/keyword/RenownAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/RenownAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.keyword; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/keyword/RepairAbility.java b/Mage/src/main/java/mage/abilities/keyword/RepairAbility.java index 03b4c2042c45..be3a2cdb148b 100644 --- a/Mage/src/main/java/mage/abilities/keyword/RepairAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/RepairAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.keyword; import java.util.UUID; diff --git a/Mage/src/main/java/mage/abilities/keyword/ReplicateAbility.java b/Mage/src/main/java/mage/abilities/keyword/ReplicateAbility.java index e10b4da315b4..0e52e0a5e187 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ReplicateAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ReplicateAbility.java @@ -96,10 +96,10 @@ public void addOptionalAdditionalCosts(Ability ability, Game game) { // canPay checks only single mana available, not total mana usage if (additionalCost.canPay(ability, this, ability.getControllerId(), game) && player.chooseUse(/*Outcome.Benefit*/Outcome.AIDontUseIt, - new StringBuilder("Pay ").append(times).append( - additionalCost.getText(false)).append(" ?").toString(), ability, game)) { + new StringBuilder("Pay ").append(times).append( + additionalCost.getText(false)).append(" ?").toString(), ability, game)) { additionalCost.activate(); - for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext();) { + for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext(); ) { Cost cost = (Cost) it.next(); if (cost instanceof ManaCostsImpl) { ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy()); @@ -225,8 +225,7 @@ public boolean apply(Game game, Ability source) { } } // create the copies - StackObject newStackObject = spell.createCopyOnStack(game, source, - source.getControllerId(), true, replicateCount); + spell.createCopyOnStack(game, source, source.getControllerId(), true, replicateCount); return true; } diff --git a/Mage/src/main/java/mage/abilities/keyword/SkulkAbility.java b/Mage/src/main/java/mage/abilities/keyword/SkulkAbility.java index db30ccb96a28..c50a57422486 100644 --- a/Mage/src/main/java/mage/abilities/keyword/SkulkAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/SkulkAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.keyword; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/keyword/SoulbondAbility.java b/Mage/src/main/java/mage/abilities/keyword/SoulbondAbility.java index 5eae2caeac26..d09693c89e89 100644 --- a/Mage/src/main/java/mage/abilities/keyword/SoulbondAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/SoulbondAbility.java @@ -14,7 +14,7 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicate; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; diff --git a/Mage/src/main/java/mage/abilities/keyword/SoulshiftAbility.java b/Mage/src/main/java/mage/abilities/keyword/SoulshiftAbility.java index fd5ff1e9df17..5d3e24cde6fa 100644 --- a/Mage/src/main/java/mage/abilities/keyword/SoulshiftAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/SoulshiftAbility.java @@ -9,8 +9,9 @@ import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.constants.SubType; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; +import mage.game.events.GameEvent; import mage.target.common.TargetCardInYourGraveyard; import java.util.UUID; @@ -46,14 +47,14 @@ public SoulshiftAbility(final SoulshiftAbility ability) { } @Override - public void trigger(Game game, UUID controllerId) { + public void trigger(Game game, UUID controllerId, GameEvent triggeringEvent) { this.getTargets().clear(); int intValue = amount.calculate(game, this, null); - FilterCard filter = new FilterCard("Spirit card with converted mana cost " + intValue + " or less from your graveyard"); - filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, intValue + 1)); + FilterCard filter = new FilterCard("Spirit card with mana value " + intValue + " or less from your graveyard"); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, intValue + 1)); filter.add(SubType.SPIRIT.getPredicate()); this.addTarget(new TargetCardInYourGraveyard(filter)); - super.trigger(game, controllerId); //To change body of generated methods, choose Tools | Templates. + super.trigger(game, controllerId, triggeringEvent); //To change body of generated methods, choose Tools | Templates. } @Override @@ -64,10 +65,10 @@ public DiesSourceTriggeredAbility copy() { @Override public String getRule() { if (amount instanceof StaticValue) { - return "Soulshift " + amount.toString() + " (When this creature dies, you may return target Spirit card with converted mana cost " + amount.toString() + " or less from your graveyard to your hand.)"; + return "Soulshift " + amount.toString() + " (When this creature dies, you may return target Spirit card with mana value " + amount.toString() + " or less from your graveyard to your hand.)"; } else { return "{this} has soulshift X, where X is the number of " + amount.getMessage() + - ". (When this creature dies, you may return target Spirit card with converted mana cost X or less from your graveyard to your hand.)"; + ". (When this creature dies, you may return target Spirit card with mana value X or less from your graveyard to your hand.)"; } } diff --git a/Mage/src/main/java/mage/abilities/keyword/SupportAbility.java b/Mage/src/main/java/mage/abilities/keyword/SupportAbility.java index 63fec0c7187b..aaef23be4fb0 100644 --- a/Mage/src/main/java/mage/abilities/keyword/SupportAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/SupportAbility.java @@ -5,7 +5,7 @@ import mage.abilities.effects.keyword.SupportEffect; import mage.cards.Card; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; /** @@ -25,7 +25,7 @@ public class SupportAbility extends EntersBattlefieldTriggeredAbility { */ public SupportAbility(Card card, int amount, boolean otherPermanent) { super(new SupportEffect(card, amount, otherPermanent)); - if (!card.isInstant() && !card.isSorcery()) { + if (!card.isInstantOrSorcery()) { FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures"); if (card.isCreature()) { filter.add(AnotherPredicate.instance); diff --git a/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java b/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java index 37e762baf1fd..9e1ff595cd5b 100644 --- a/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java @@ -138,7 +138,7 @@ public SuspendAbility(int suspend, ManaCost cost, Card card, boolean shortRule) if (cost != null) { sb.append(suspend == Integer.MAX_VALUE ? "X" : suspend).append("—") .append(cost.getText()).append(suspend - == Integer.MAX_VALUE ? ". X can't be 0" : ""); + == Integer.MAX_VALUE ? ". X can't be 0." : ""); if (!shortRule) { sb.append(" (Rather than cast this card from your hand, pay ") .append(cost.getText()) @@ -180,13 +180,11 @@ public static void addSuspendTemporaryToCard(Card card, Ability source, Game gam ability1.setSourceId(card.getId()); ability1.setControllerId(card.getOwnerId()); game.getState().addOtherAbility(card, ability1); - game.getState().addAbility(ability1, source.getSourceId(), card); SuspendPlayCardAbility ability2 = new SuspendPlayCardAbility(); ability2.setSourceId(card.getId()); ability2.setControllerId(card.getOwnerId()); game.getState().addOtherAbility(card, ability2); - game.getState().addAbility(ability2, source.getSourceId(), card); } public static UUID getSuspendExileId(UUID controllerId, Game game) { diff --git a/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java b/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java index f9cff425b6aa..5d598234ec35 100644 --- a/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java @@ -14,9 +14,6 @@ */ public class TransformAbility extends SimpleStaticAbility { - public static final String NO_SPELLS_TRANSFORM_RULE = "At the beginning of each upkeep, if no spells were cast last turn, transform {this}."; - public static final String TWO_OR_MORE_SPELLS_TRANSFORM_RULE = "At the beginning of each upkeep, if a player cast two or more spells last turn, transform {this}."; - // this state value controls if a permanent enters the battlefield already transformed public static final String VALUE_KEY_ENTER_TRANSFORMED = "EnterTransformed"; @@ -24,7 +21,7 @@ public TransformAbility() { super(Zone.BATTLEFIELD, new TransformEffect()); } - public TransformAbility(final TransformAbility ability) { + private TransformAbility(final TransformAbility ability) { super(ability); } @@ -73,12 +70,12 @@ public static void transform(Permanent permanent, Card sourceCard, Game game, Ab class TransformEffect extends ContinuousEffectImpl { - public TransformEffect() { - super(Duration.WhileOnBattlefield, Layer.CopyEffects_1, SubLayer.NA, Outcome.BecomeCreature); + TransformEffect() { + super(Duration.WhileOnBattlefield, Layer.CopyEffects_1, SubLayer.CopyEffects_1a, Outcome.BecomeCreature); staticText = ""; } - public TransformEffect(final TransformEffect effect) { + private TransformEffect(final TransformEffect effect) { super(effect); } @@ -120,5 +117,4 @@ public TransformEffect copy() { public String getText(Mode mode) { return ""; } - } diff --git a/Mage/src/main/java/mage/abilities/keyword/TransmuteAbility.java b/Mage/src/main/java/mage/abilities/keyword/TransmuteAbility.java index 7a4c9770b2be..da0f50e807ba 100644 --- a/Mage/src/main/java/mage/abilities/keyword/TransmuteAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/TransmuteAbility.java @@ -13,7 +13,7 @@ import mage.constants.TimingRule; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; @@ -57,7 +57,7 @@ public SimpleActivatedAbility copy() { public String getRule() { return new StringBuilder("Transmute ").append(this.getManaCosts().getText()) .append(" (").append(this.getManaCosts().getText()) - .append(", Discard this card: Search your library for a card with the same converted mana cost as this card, reveal it, and put it into your hand. Then shuffle your library. Transmute only as a sorcery.)").toString(); + .append(", Discard this card: Search your library for a card with the same mana value as this card, reveal it, and put it into your hand. Then shuffle. Transmute only as a sorcery.)").toString(); } } @@ -77,8 +77,8 @@ public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = game.getObject(source.getSourceId()); if (sourceObject != null && controller != null) { - FilterCard filter = new FilterCard("card with converted mana cost " + sourceObject.getConvertedManaCost()); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, sourceObject.getConvertedManaCost())); + FilterCard filter = new FilterCard("card with mana value " + sourceObject.getManaValue()); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, sourceObject.getManaValue())); TargetCardInLibrary target = new TargetCardInLibrary(1, filter); if (controller.searchLibrary(target, source, game)) { if (!target.getTargets().isEmpty()) { diff --git a/Mage/src/main/java/mage/abilities/keyword/UnearthAbility.java b/Mage/src/main/java/mage/abilities/keyword/UnearthAbility.java index 56ddae7badd6..4edf5d71038a 100644 --- a/Mage/src/main/java/mage/abilities/keyword/UnearthAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/UnearthAbility.java @@ -52,7 +52,7 @@ public UnearthAbility copy() { public String getRule() { StringBuilder sb = new StringBuilder("Unearth ").append(this.getManaCosts().getText()); sb.append(" (").append(this.getManaCosts().getText()); - sb.append(": Return this card from your graveyard to the battlefield. It gains haste. Exile it at the beginning of the next end step or if it would leave the battlefield. Unearth only as a sorcery.)"); + sb.append(": Return this card from your graveyard to the battlefield. It gains haste. Exile it at the beginning of the next end step or if it would leave the battlefield. Unearth only as a sorcery.)"); return sb.toString(); } diff --git a/Mage/src/main/java/mage/abilities/keyword/WardAbility.java b/Mage/src/main/java/mage/abilities/keyword/WardAbility.java new file mode 100644 index 000000000000..586ee2a118b7 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/keyword/WardAbility.java @@ -0,0 +1,72 @@ +package mage.abilities.keyword; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.costs.Cost; +import mage.abilities.costs.mana.ManaCost; +import mage.abilities.effects.common.CounterUnlessPaysEffect; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.StackObject; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +/** + * @author TheElk801 + */ +public class WardAbility extends TriggeredAbilityImpl { + + private final Cost cost; + + public WardAbility(Cost cost) { + super(Zone.BATTLEFIELD, new CounterUnlessPaysEffect(cost), false); + this.cost = cost; + } + + private WardAbility(final WardAbility ability) { + super(ability); + this.cost = ability.cost.copy(); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.TARGETED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!getSourceId().equals(event.getTargetId())) { + return false; + } + StackObject stackObject = game.getStack().getStackObject(event.getSourceId()); + if (stackObject == null || !game.getOpponents(getControllerId()).contains(stackObject.getControllerId())) { + return false; + } + getEffects().setTargetPointer(new FixedTarget(event.getSourceId(), game)); + return true; + } + + @Override + public WardAbility copy() { + return new WardAbility(this); + } + + @Override + public String getRule() { + StringBuilder sb = new StringBuilder("ward"); + if (cost instanceof ManaCost) { + sb.append(' ').append(cost.getText()); + } else { + sb.append("—").append(CardUtil.getTextWithFirstCharUpperCase(cost.getText())).append('.'); + } + sb.append(" (Whenever {this} becomes the target of a spell or ability an opponent controls, " + + "counter that spell or ability unless that player "); + if (cost instanceof ManaCost) { + sb.append("pays ").append(cost.getText()); + } else { + sb.append(cost.getText().replace("pay ", "pays ")); + } + sb.append(".)"); + return sb.toString(); + } +} diff --git a/Mage/src/main/java/mage/abilities/mana/ActivateAsSorceryManaAbility.java b/Mage/src/main/java/mage/abilities/mana/ActivateAsSorceryManaAbility.java index 050854678f25..7f8f3a361ec6 100644 --- a/Mage/src/main/java/mage/abilities/mana/ActivateAsSorceryManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/ActivateAsSorceryManaAbility.java @@ -30,6 +30,6 @@ public ActivateAsSorceryManaAbility copy() { @Override public String getRule() { - return super.getRule() + " Activate this ability only any time you could cast a sorcery."; + return super.getRule() + " Activate only as a sorcery."; } } diff --git a/Mage/src/main/java/mage/abilities/mana/ActivateIfConditionManaAbility.java b/Mage/src/main/java/mage/abilities/mana/ActivateIfConditionManaAbility.java index 9aa0f3389ef6..abf4e1e38aff 100644 --- a/Mage/src/main/java/mage/abilities/mana/ActivateIfConditionManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/ActivateIfConditionManaAbility.java @@ -33,7 +33,7 @@ public boolean activate(Game game, boolean noMana) { @Override public String getRule() { - return super.getRule() + " Activate this ability only if " + condition.toString() + '.'; + return super.getRule() + " Activate only if " + condition.toString() + '.'; } @Override diff --git a/Mage/src/main/java/mage/abilities/mana/ActivateOncePerTurnManaAbility.java b/Mage/src/main/java/mage/abilities/mana/ActivateOncePerTurnManaAbility.java index 91a526442960..9f729d37806d 100644 --- a/Mage/src/main/java/mage/abilities/mana/ActivateOncePerTurnManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/ActivateOncePerTurnManaAbility.java @@ -40,7 +40,7 @@ public boolean activate(Game game, boolean noMana) { @Override public String getRule() { - return super.getRule() + " Activate this ability only once each turn."; + return super.getRule() + " Activate only once each turn."; } @Override diff --git a/Mage/src/main/java/mage/abilities/mana/AnyColorPermanentTypesManaAbility.java b/Mage/src/main/java/mage/abilities/mana/AnyColorPermanentTypesManaAbility.java index 26a538f47ae1..fb8f9e9a1a26 100644 --- a/Mage/src/main/java/mage/abilities/mana/AnyColorPermanentTypesManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/AnyColorPermanentTypesManaAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.mana; import mage.Mana; diff --git a/Mage/src/main/java/mage/abilities/mana/ConditionalColoredManaAbility.java b/Mage/src/main/java/mage/abilities/mana/ConditionalColoredManaAbility.java index c89e16ce1aef..141cdb5b00eb 100644 --- a/Mage/src/main/java/mage/abilities/mana/ConditionalColoredManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/ConditionalColoredManaAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.mana; import mage.Mana; diff --git a/Mage/src/main/java/mage/abilities/mana/ConditionalColorlessManaAbility.java b/Mage/src/main/java/mage/abilities/mana/ConditionalColorlessManaAbility.java index 10995f008016..df79a208e14e 100644 --- a/Mage/src/main/java/mage/abilities/mana/ConditionalColorlessManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/ConditionalColorlessManaAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.mana; import mage.abilities.costs.Cost; diff --git a/Mage/src/main/java/mage/abilities/mana/ManaAbility.java b/Mage/src/main/java/mage/abilities/mana/ManaAbility.java index bc2bfcd09142..94e7a221a423 100644 --- a/Mage/src/main/java/mage/abilities/mana/ManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/ManaAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.mana; import java.util.List; diff --git a/Mage/src/main/java/mage/abilities/mana/ManaOptions.java b/Mage/src/main/java/mage/abilities/mana/ManaOptions.java index b2448fecca42..7909e986797d 100644 --- a/Mage/src/main/java/mage/abilities/mana/ManaOptions.java +++ b/Mage/src/main/java/mage/abilities/mana/ManaOptions.java @@ -589,7 +589,10 @@ public void removeDuplicated() { list.add(s); } } + // Remove fully included variations + // TODO: research too many manas and freeze (put 1 card to slow down, put 3 cards to freeze here) + // battlefield:Human:Cascading Cataracts:1 for (int i = this.size() - 1; i >= 0; i--) { for (int ii = 0; ii < i; ii++) { Mana moreValuable = Mana.getMoreValuableMana(this.get(i), this.get(ii)); diff --git a/Mage/src/main/java/mage/abilities/mana/builder/common/InstantOrSorcerySpellManaBuilder.java b/Mage/src/main/java/mage/abilities/mana/builder/common/InstantOrSorcerySpellManaBuilder.java index 62b2fbf0035e..a544ab73af28 100644 --- a/Mage/src/main/java/mage/abilities/mana/builder/common/InstantOrSorcerySpellManaBuilder.java +++ b/Mage/src/main/java/mage/abilities/mana/builder/common/InstantOrSorcerySpellManaBuilder.java @@ -44,7 +44,7 @@ class InstantOrSorceryCastManaCondition extends ManaCondition implements Conditi public boolean apply(Game game, Ability source) { if (source instanceof SpellAbility) { MageObject object = game.getObject(source.getSourceId()); - return object != null && (object.isInstant() || object.isSorcery()); + return object != null && object.isInstantOrSorcery(); } return false; } diff --git a/Mage/src/main/java/mage/abilities/mana/conditional/ConditionalAddManaOfTwoDifferentColorsAbility.java b/Mage/src/main/java/mage/abilities/mana/conditional/ConditionalAddManaOfTwoDifferentColorsAbility.java index 09930bde7d0a..ffa51c12a31f 100644 --- a/Mage/src/main/java/mage/abilities/mana/conditional/ConditionalAddManaOfTwoDifferentColorsAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/conditional/ConditionalAddManaOfTwoDifferentColorsAbility.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.mana.conditional; import mage.abilities.costs.Cost; diff --git a/Mage/src/main/java/mage/abilities/mana/conditional/ConditionalSpellManaBuilder.java b/Mage/src/main/java/mage/abilities/mana/conditional/ConditionalSpellManaBuilder.java index b02c358e0e52..a39e9d95cfa4 100644 --- a/Mage/src/main/java/mage/abilities/mana/conditional/ConditionalSpellManaBuilder.java +++ b/Mage/src/main/java/mage/abilities/mana/conditional/ConditionalSpellManaBuilder.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.mana.conditional; import mage.ConditionalMana; @@ -78,7 +73,7 @@ public boolean apply(Game game, Ability source) { } else if (object instanceof Commander) { spell = new Spell(((Commander) object).getSourceObject(), (SpellAbility) source, source.getControllerId(), game.getState().getZone(source.getSourceId()), game); } - return spell != null && filter.match(spell, source.getSourceId(), source.getControllerId(), game); + return filter.match(spell, source.getSourceId(), source.getControllerId(), game); } } return false; diff --git a/Mage/src/main/java/mage/abilities/text/TextPartImpl.java b/Mage/src/main/java/mage/abilities/text/TextPartImpl.java index abc79ebfb998..a14b9bb088e2 100644 --- a/Mage/src/main/java/mage/abilities/text/TextPartImpl.java +++ b/Mage/src/main/java/mage/abilities/text/TextPartImpl.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.text; import java.util.UUID; diff --git a/Mage/src/main/java/mage/cards/AdventureCard.java b/Mage/src/main/java/mage/cards/AdventureCard.java index 473d53636a10..bc3bab17758f 100644 --- a/Mage/src/main/java/mage/cards/AdventureCard.java +++ b/Mage/src/main/java/mage/cards/AdventureCard.java @@ -18,7 +18,7 @@ public abstract class AdventureCard extends CardImpl { /* The adventure spell card, i.e. Swift End. */ - protected Card spellCard; + protected AdventureCardSpell spellCard; public AdventureCard(UUID ownerId, CardSetInfo setInfo, CardType[] types, CardType[] typesSpell, String costs, String adventureName, String costsSpell) { super(ownerId, setInfo, types, costs); @@ -28,13 +28,19 @@ public AdventureCard(UUID ownerId, CardSetInfo setInfo, CardType[] types, CardTy public AdventureCard(AdventureCard card) { super(card); this.spellCard = card.getSpellCard().copy(); - ((AdventureCardSpell) this.spellCard).setParentCard(this); + this.spellCard.setParentCard(this); } - public Card getSpellCard() { + public AdventureCardSpell getSpellCard() { return spellCard; } + public void setParts(AdventureCardSpell cardSpell) { + // for card copy only - set new parts + this.spellCard = cardSpell; + cardSpell.setParentCard(this); + } + @Override public void assignNewId() { super.assignNewId(); diff --git a/Mage/src/main/java/mage/cards/AdventureCardSpell.java b/Mage/src/main/java/mage/cards/AdventureCardSpell.java index 5b873acc8d34..6e75792c949d 100644 --- a/Mage/src/main/java/mage/cards/AdventureCardSpell.java +++ b/Mage/src/main/java/mage/cards/AdventureCardSpell.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.cards; /** diff --git a/Mage/src/main/java/mage/cards/Card.java b/Mage/src/main/java/mage/cards/Card.java index a54fc780ace0..c4686e25a619 100644 --- a/Mage/src/main/java/mage/cards/Card.java +++ b/Mage/src/main/java/mage/cards/Card.java @@ -181,6 +181,14 @@ default boolean isOwnedBy(UUID controllerId) { return getOwnerId().equals(controllerId); } + /** + * Commander tax calculation. Tax logic can be changed (example: from {2} to life cost, see Liesa, Shroud of Dusk) + * + * @param game + * @param source + * @param abilityToModify + * @return + */ default boolean commanderCost(Game game, Ability source, Ability abilityToModify) { CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class); int castCount = watcher.getPlaysCount(getMainCard().getId()); diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java index 8b769adeacc6..f3321519121c 100644 --- a/Mage/src/main/java/mage/cards/CardImpl.java +++ b/Mage/src/main/java/mage/cards/CardImpl.java @@ -367,30 +367,6 @@ public SpellAbility getSpellAbility() { return spellAbility; } - /** - * Dynamic cost modification for card (process only own abilities). Example: - * if it need stack related info (like real targets) then must check two - * states (game.inCheckPlayableState): - *

- * 1. In playable state it must check all possible use cases (e.g. allow to - * reduce on any available target and modes) - *

- * 2. In real cast state it must check current use case (e.g. real selected - * targets and modes) - * - * @param ability - * @param game - */ - @Override - public void adjustCosts(Ability ability, Game game) { - ability.adjustCosts(game); - } - - @Override - public void adjustTargets(Ability ability, Game game) { - ability.adjustTargets(game); - } - @Override public void setOwnerId(UUID ownerId) { this.ownerId = ownerId; diff --git a/Mage/src/main/java/mage/cards/Cards.java b/Mage/src/main/java/mage/cards/Cards.java index a6c6a21efcda..44e0ea223da9 100644 --- a/Mage/src/main/java/mage/cards/Cards.java +++ b/Mage/src/main/java/mage/cards/Cards.java @@ -1,13 +1,14 @@ - package mage.cards; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.game.Game; + import java.io.Serializable; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.UUID; -import mage.filter.FilterCard; -import mage.game.Game; public interface Cards extends Set, Serializable { @@ -19,9 +20,9 @@ public interface Cards extends Set, Serializable { void setOwner(UUID ownerId, Game game); - void addAll(List createCards); + void addAll(List createCards); - void addAll(Set createCards); + void addAll(Set createCards); Set getCards(Game game); @@ -42,4 +43,6 @@ public interface Cards extends Set, Serializable { int count(FilterCard filter, UUID sourceId, UUID playerId, Game game); Cards copy(); + + void retainZone(Zone zone, Game game); } diff --git a/Mage/src/main/java/mage/cards/CardsImpl.java b/Mage/src/main/java/mage/cards/CardsImpl.java index 6433768b7976..4c7b2a725cb2 100644 --- a/Mage/src/main/java/mage/cards/CardsImpl.java +++ b/Mage/src/main/java/mage/cards/CardsImpl.java @@ -1,6 +1,8 @@ package mage.cards; +import mage.MageItem; import mage.MageObject; +import mage.constants.Zone; import mage.filter.FilterCard; import mage.game.Game; import mage.util.RandomUtil; @@ -28,10 +30,12 @@ public CardsImpl(Card card) { } } - public CardsImpl(Set cards) { - for (Card card : cards) { - this.add(card.getId()); - } + public CardsImpl(List cards) { + this.addAll(cards); + } + + public CardsImpl(Set cards) { + this.addAll(cards); } public CardsImpl(Collection cardIds) { @@ -172,16 +176,22 @@ public String getValue(Game game) { } @Override - public void addAll(List cards) { - for (Card card : cards) { - add(card.getId()); + public void addAll(List cards) { + if (cards != null) { + cards.stream() + .filter(Objects::nonNull) + .map(MageItem::getId) + .forEach(this::add); } } @Override - public void addAll(Set cards) { - for (Card card : cards) { - add(card.getId()); + public void addAll(Set cards) { + if (cards != null) { + cards.stream() + .filter(Objects::nonNull) + .map(MageItem::getId) + .forEach(this::add); } } @@ -197,4 +207,8 @@ public Collection getUniqueCards(Game game) { return cards.values(); } + @Override + public void retainZone(Zone zone, Game game) { + removeIf(uuid -> game.getState().getZone(uuid) != zone); + } } diff --git a/Mage/src/main/java/mage/cards/ExpansionSet.java b/Mage/src/main/java/mage/cards/ExpansionSet.java index 50d6a311f965..2803abf7b8dc 100644 --- a/Mage/src/main/java/mage/cards/ExpansionSet.java +++ b/Mage/src/main/java/mage/cards/ExpansionSet.java @@ -6,6 +6,7 @@ import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; +import mage.collation.BoosterCollator; import mage.constants.Rarity; import mage.constants.SetType; import mage.util.CardUtil; @@ -26,7 +27,7 @@ public abstract class ExpansionSet implements Serializable { public static final CardGraphicInfo FULL_ART_BFZ_VARIOUS = new CardGraphicInfo(FrameStyle.BFZ_FULL_ART_BASIC, true); public static final CardGraphicInfo FULL_ART_ZEN_VARIOUS = new CardGraphicInfo(FrameStyle.ZEN_FULL_ART_BASIC, true); - public class SetCardInfo implements Serializable { + public static class SetCardInfo implements Serializable { private final String name; private final String cardNumber; @@ -92,6 +93,7 @@ public boolean isFullArt() { protected Date releaseDate; protected ExpansionSet parentSet; protected SetType setType; + protected BoosterCollator boosterCollator; // TODO: 03.10.2018, hasBasicLands can be removed someday -- it's uses to optimize lands search in deck generation and lands adding (search all available lands from sets) protected boolean hasBasicLands = true; @@ -120,14 +122,23 @@ public boolean isFullArt() { protected int maxCardNumberInBooster; // used to omit cards with collector numbers beyond the regular cards in a set for boosters protected final EnumMap> savedCards; + protected final Map inBoosterMap = new HashMap<>(); public ExpansionSet(String name, String code, Date releaseDate, SetType setType) { + this(name, code, releaseDate, setType, null); + } + + public ExpansionSet(String name, String code, Date releaseDate, SetType setType, BoosterCollator boosterCollator) { this.name = name; this.code = code; this.releaseDate = releaseDate; this.setType = setType; this.maxCardNumberInBooster = Integer.MAX_VALUE; savedCards = new EnumMap<>(Rarity.class); + this.boosterCollator = boosterCollator; + if (this.boosterCollator != null) { + this.boosterCollator.shuffle(); + } } public String getName() { @@ -240,6 +251,9 @@ protected void addToBooster(List booster, List cards) { } public List createBooster() { + if (boosterCollator != null) { + return createBoosterUsingCollator(); + } for (int i = 0; i < 100; i++) {//don't want to somehow loop forever @@ -262,6 +276,32 @@ public List createBooster() { return tryBooster(); } + public void shuffleCollator() { + if (boosterCollator != null) { + boosterCollator.shuffle(); + } + } + + private List createBoosterUsingCollator() { + if (inBoosterMap.isEmpty()) { + generateBoosterMap(); + } + return boosterCollator + .makeBooster() + .stream() + .map(inBoosterMap::get) + .map(CardInfo::getCard) + .collect(Collectors.toList()); + } + + protected void generateBoosterMap() { + CardRepository + .instance + .findCards(new CardCriteria().setCodes(code)) + .stream() + .forEach(cardInfo -> inBoosterMap.put(cardInfo.getCardNumber(), cardInfo)); + } + protected boolean boosterIsValid(List booster) { if (validateBoosterColors) { if (!validateColors(booster)) { @@ -523,56 +563,44 @@ private void addSpecial(List booster) { List specialMythic = getSpecialMythic(); specialCards += specialMythic.size(); + List specialRare = getSpecialRare(); specialCards += specialRare.size(); + List specialUncommon = getSpecialUncommon(); specialCards += specialUncommon.size(); - List specialCommon = getSpecialCommon(); + List specialCommon = getSpecialCommon(); specialCards += specialCommon.size(); if (specialCards > 0) { for (int i = 0; i < numBoosterSpecial; i++) { - if (RandomUtil.nextInt(15) < 10) { - if (!specialCommon.isEmpty()) { - addToBooster(booster, specialCommon); - } else { - i--; - } + if (!specialCommon.isEmpty() + && RandomUtil.nextInt(15) < 10) { + addToBooster(booster, specialCommon); continue; } - if (RandomUtil.nextInt(4) < 3) { - if (!specialUncommon.isEmpty()) { - addToBooster(booster, specialUncommon); - } else { - i--; - } + if (!specialUncommon.isEmpty() + && RandomUtil.nextInt(4) < 3) { + addToBooster(booster, specialUncommon); continue; } - if (RandomUtil.nextInt(8) < 7) { - if (!specialRare.isEmpty()) { - addToBooster(booster, specialRare); - } else { - i--; - } + if (!specialRare.isEmpty() + && RandomUtil.nextInt(8) < 7) { + addToBooster(booster, specialRare); continue; } if (!specialMythic.isEmpty()) { - if (!specialBonus.isEmpty()) { - if (RandomUtil.nextInt(3) < 2) { - addToBooster(booster, specialMythic); - continue; - } - } else { + if (specialBonus.isEmpty() || RandomUtil.nextInt(3) < 2) { addToBooster(booster, specialMythic); continue; } - } else { - i--; } if (!specialBonus.isEmpty()) { addToBooster(booster, specialBonus); + continue; } + i--; } } } diff --git a/Mage/src/main/java/mage/cards/FrameStyle.java b/Mage/src/main/java/mage/cards/FrameStyle.java index 4ab72228fbae..5549dcabfaea 100644 --- a/Mage/src/main/java/mage/cards/FrameStyle.java +++ b/Mage/src/main/java/mage/cards/FrameStyle.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.cards; /** diff --git a/Mage/src/main/java/mage/cards/MeldCard.java b/Mage/src/main/java/mage/cards/MeldCard.java index 168b1ade2a5e..05355ec5ab25 100644 --- a/Mage/src/main/java/mage/cards/MeldCard.java +++ b/Mage/src/main/java/mage/cards/MeldCard.java @@ -94,12 +94,12 @@ public void setOwnerId(UUID ownerId) { } @Override - public int getConvertedManaCost() { + public int getManaValue() { if (this.isCopy()) { return 0; } else { - return (this.topHalfCard != null ? this.topHalfCard.getConvertedManaCost() : 0) - + (this.bottomHalfCard != null ? this.bottomHalfCard.getConvertedManaCost() : 0); + return (this.topHalfCard != null ? this.topHalfCard.getManaValue() : 0) + + (this.bottomHalfCard != null ? this.bottomHalfCard.getManaValue() : 0); } } diff --git a/Mage/src/main/java/mage/cards/ModalDoubleFacesCard.java b/Mage/src/main/java/mage/cards/ModalDoubleFacesCard.java index dda16ba35f54..9aca8948c6bd 100644 --- a/Mage/src/main/java/mage/cards/ModalDoubleFacesCard.java +++ b/Mage/src/main/java/mage/cards/ModalDoubleFacesCard.java @@ -7,6 +7,7 @@ import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; import mage.constants.*; +import mage.counters.Counter; import mage.counters.Counters; import mage.game.Game; import mage.game.GameState; @@ -54,6 +55,14 @@ public ModalDoubleFacesCardHalf getRightHalfCard() { return (ModalDoubleFacesCardHalf) rightHalfCard; } + public void setParts(ModalDoubleFacesCardHalf leftHalfCard, ModalDoubleFacesCardHalf rightHalfCard) { + // for card copy only - set new parts + this.leftHalfCard = leftHalfCard; + leftHalfCard.setParentCard(this); + this.rightHalfCard = rightHalfCard; + rightHalfCard.setParentCard(this); + } + @Override public void assignNewId() { super.assignNewId(); @@ -123,6 +132,16 @@ public Counters getCounters(GameState state) { return state.getCardState(leftHalfCard.getId()).getCounters(); } + @Override + public boolean addCounters(Counter counter, UUID playerAddingCounters, Ability source, Game game, List appliedEffects, boolean isEffect) { + return leftHalfCard.addCounters(counter, playerAddingCounters, source, game, appliedEffects, isEffect); + } + + @Override + public void removeCounters(String name, int amount, Ability source, Game game) { + leftHalfCard.removeCounters(name, amount, source, game); + } + @Override public boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId) { switch (ability.getSpellAbilityType()) { @@ -308,7 +327,7 @@ public ManaCosts getManaCost() { } @Override - public int getConvertedManaCost() { + public int getManaValue() { // Rules: // The converted mana cost of a modal double-faced card is based on the characteristics of the // face that’s being considered. On the stack and battlefield, consider whichever face is up. @@ -316,7 +335,7 @@ public int getConvertedManaCost() { // mana cost of a transforming double-faced card is determined. // on stack or battlefield it must be half card with own cost - return leftHalfCard.getConvertedManaCost(); + return leftHalfCard.getManaValue(); } @Override diff --git a/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalf.java b/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalf.java index bb2c1364cf9f..d7dee2090bdb 100644 --- a/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalf.java +++ b/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalf.java @@ -14,5 +14,7 @@ public interface ModalDoubleFacesCardHalf extends Card { ModalDoubleFacesCard getParentCard(); - void setPT(MageInt power, MageInt toughtness); + void setPT(int power, int toughness); + + void setPT(MageInt power, MageInt toughness); } diff --git a/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalfImpl.java b/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalfImpl.java index 762e3ebb5228..ce24d5e81e91 100644 --- a/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalfImpl.java +++ b/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalfImpl.java @@ -96,6 +96,11 @@ public ModalDoubleFacesCard getParentCard() { return this.parentCard; } + @Override + public void setPT(int power, int toughness) { + this.setPT(new MageInt(power), new MageInt(toughness)); + } + @Override public void setPT(MageInt power, MageInt toughness) { this.power = power; diff --git a/Mage/src/main/java/mage/cards/SplitCard.java b/Mage/src/main/java/mage/cards/SplitCard.java index 9bc7a9abc91c..b1d0c9a17630 100644 --- a/Mage/src/main/java/mage/cards/SplitCard.java +++ b/Mage/src/main/java/mage/cards/SplitCard.java @@ -42,6 +42,14 @@ public SplitCard(SplitCard card) { ((SplitCardHalf) rightHalfCard).setParentCard(this); } + public void setParts(SplitCardHalf leftHalfCard, SplitCardHalf rightHalfCard) { + // for card copy only - set new parts + this.leftHalfCard = leftHalfCard; + leftHalfCard.setParentCard(this); + this.rightHalfCard = rightHalfCard; + rightHalfCard.setParentCard(this); + } + public SplitCardHalf getLeftHalfCard() { return (SplitCardHalf) leftHalfCard; } @@ -196,13 +204,13 @@ public void setOwnerId(UUID ownerId) { } @Override - public int getConvertedManaCost() { + public int getManaValue() { // 202.3d The converted mana cost of a split card not on the stack or of a fused split spell on the // stack is determined from the combined mana costs of its halves. Otherwise, while a split card is // on the stack, the converted mana cost of the spell is determined by the mana cost of the half // that was chosen to be cast. See rule 708, “Split Cards.” // split card and it's halfes contains own mana costs, so no need to rewrite logic - return super.getConvertedManaCost(); + return super.getManaValue(); } } diff --git a/Mage/src/main/java/mage/cards/decks/Constructed.java b/Mage/src/main/java/mage/cards/decks/Constructed.java index da1a2928e140..27390a1dda05 100644 --- a/Mage/src/main/java/mage/cards/decks/Constructed.java +++ b/Mage/src/main/java/mage/cards/decks/Constructed.java @@ -17,7 +17,8 @@ public class Constructed extends DeckValidator { private static final Logger logger = Logger.getLogger(DeckValidator.class); private static final List anyNumberCardsAllowed = new ArrayList<>(Arrays.asList( - "Relentless Rats", "Shadowborn Apostle", "Rat Colony", "Persistent Petitioners", "Seven Dwarves" + "Relentless Rats", "Shadowborn Apostle", "Rat Colony", + "Persistent Petitioners", "Seven Dwarves", "Dragon's Approach" )); protected static final List basicLandNames = new ArrayList<>(Arrays.asList( "Forest", "Island", "Mountain", "Swamp", "Plains", "Wastes", "Snow-Covered Forest", diff --git a/Mage/src/main/java/mage/cards/decks/exporter/XmageInfoDeckExporter.java b/Mage/src/main/java/mage/cards/decks/exporter/XmageInfoDeckExporter.java index 09d3278925bb..052be0596288 100644 --- a/Mage/src/main/java/mage/cards/decks/exporter/XmageInfoDeckExporter.java +++ b/Mage/src/main/java/mage/cards/decks/exporter/XmageInfoDeckExporter.java @@ -63,7 +63,7 @@ public void writeDeck(PrintWriter out, DeckCardLists deck) { out.printf("%d [%s:%s] %s%n\n", amount.get("M@" + card.getCardKey()), card.getSetCode(), card.getCardNum(), card.getCardName()); } else { out.printf("%d [%s:%s] %s ;; %s ;; %s ;; %d %n", amount.get("M@" + card.getCardKey()), card.getSetCode(), card.getCardNum(), card.getCardName(), - cardInfo.getColor().getDescription(), cardInfo.getTypes().toString(), cardInfo.getConvertedManaCost()); + cardInfo.getColor().getDescription(), cardInfo.getTypes().toString(), cardInfo.getManaValue()); } } @@ -73,7 +73,7 @@ public void writeDeck(PrintWriter out, DeckCardLists deck) { out.printf("SB: %d [%s:%s] %s%n\n", amount.get("S@" + card.getCardKey()), card.getSetCode(), card.getCardNum(), card.getCardName()); } else { out.printf("SB: %d [%s:%s] %s ;; %s ;; %s ;; %d %n", amount.get("S@" + card.getCardKey()), card.getSetCode(), card.getCardNum(), card.getCardName(), - cardInfo.getColor().getDescription(), cardInfo.getTypes().toString(), cardInfo.getConvertedManaCost()); + cardInfo.getColor().getDescription(), cardInfo.getTypes().toString(), cardInfo.getManaValue()); } } diff --git a/Mage/src/main/java/mage/cards/mock/MockCard.java b/Mage/src/main/java/mage/cards/mock/MockCard.java index 3221ffda4f0b..d4dbe077238d 100644 --- a/Mage/src/main/java/mage/cards/mock/MockCard.java +++ b/Mage/src/main/java/mage/cards/mock/MockCard.java @@ -4,13 +4,13 @@ import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; -import mage.abilities.costs.mana.ManaCostsImpl; import mage.cards.CardImpl; import mage.cards.ModalDoubleFacesCard; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; import org.apache.log4j.Logger; +import java.util.ArrayList; import java.util.List; /** @@ -29,10 +29,13 @@ public class MockCard extends CardImpl { private int startingLoyalty; // mana cost extra info for multiple mana drawing - protected ManaCosts manaCostLeft; - protected ManaCosts manaCostRight; + // warning, don't use ManaCost objects here due too much memory consumptions + protected List manaCostLeftStr; + protected List manaCostRightStr; + protected List manaCostStr; protected String adventureSpellName; protected boolean isModalDoubleFacesCard; + protected int manaValue; public MockCard(CardInfo card) { super(null, card.getName()); @@ -47,9 +50,11 @@ public MockCard(CardInfo card) { this.usesVariousArt = card.usesVariousArt(); - this.manaCostLeft = new ManaCostsImpl(join(card.getManaCosts(CardInfo.ManaCostSide.LEFT))); - this.manaCostRight = new ManaCostsImpl(join(card.getManaCosts(CardInfo.ManaCostSide.RIGHT))); - this.manaCost = new ManaCostsImpl(join(card.getManaCosts(CardInfo.ManaCostSide.ALL))); + //this.manaCost = new ManaCostsImpl(join(card.getManaCosts(CardInfo.ManaCostSide.ALL))); + this.manaCostLeftStr = card.getManaCosts(CardInfo.ManaCostSide.LEFT); + this.manaCostRightStr = card.getManaCosts(CardInfo.ManaCostSide.RIGHT); + this.manaCostStr = card.getManaCosts(CardInfo.ManaCostSide.ALL); + this.manaValue = card.getManaValue(); this.color = card.getColor(); @@ -95,6 +100,14 @@ public MockCard(CardInfo card) { public MockCard(final MockCard card) { super(card); + + this.startingLoyalty = card.startingLoyalty; + this.manaCostLeftStr = new ArrayList<>(card.manaCostLeftStr); + this.manaCostRightStr = new ArrayList<>(card.manaCostRightStr); + this.manaCostStr = new ArrayList<>(card.manaCostStr); + this.adventureSpellName = card.adventureSpellName; + this.isModalDoubleFacesCard = card.isModalDoubleFacesCard; + this.manaValue = card.manaValue; } @Override @@ -109,18 +122,30 @@ public MockCard copy() { @Override public ManaCosts getManaCost() { - return manaCost; + // only split half cards can store mana cost in objects list instead strings (memory optimization) + // see https://github.com/magefree/mage/issues/7515 + throw new IllegalArgumentException("Unsupport method call: getManaCost in " + this.getClass().getCanonicalName()); + } + + @Override + public List getManaCostSymbols() { + return getManaCostStr(CardInfo.ManaCostSide.ALL); + } + + @Override + public int getManaValue() { + return this.manaValue; } - public ManaCosts getManaCost(CardInfo.ManaCostSide manaCostSide) { + public List getManaCostStr(CardInfo.ManaCostSide manaCostSide) { switch (manaCostSide) { case LEFT: - return manaCostLeft; + return manaCostLeftStr; case RIGHT: - return manaCostRight; + return manaCostRightStr; default: case ALL: - return manaCost; + return manaCostStr; } } @@ -148,14 +173,6 @@ private MageInt mageIntFromString(String value) { } } - private String join(List strings) { - StringBuilder sb = new StringBuilder(); - for (String string : strings) { - sb.append(string); - } - return sb.toString(); - } - private Ability textAbilityFromString(final String text) { return new MockAbility(text); } diff --git a/Mage/src/main/java/mage/cards/mock/MockSplitCardHalf.java b/Mage/src/main/java/mage/cards/mock/MockSplitCardHalf.java index 3e5112c03871..ac8166012444 100644 --- a/Mage/src/main/java/mage/cards/mock/MockSplitCardHalf.java +++ b/Mage/src/main/java/mage/cards/mock/MockSplitCardHalf.java @@ -1,10 +1,17 @@ package mage.cards.mock; +import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.cards.Card; import mage.cards.SplitCard; import mage.cards.SplitCardHalf; import mage.cards.repository.CardInfo; +import java.util.ArrayList; +import java.util.List; + /** * * @author LevelX2 @@ -12,13 +19,20 @@ public class MockSplitCardHalf extends MockCard implements SplitCardHalf { private SplitCard splitCardParent; + private ManaCosts manaCosts; + private List manaCostsSymbols; public MockSplitCardHalf(CardInfo card) { super(card); + this.manaCostsSymbols = card.getManaCosts(CardInfo.ManaCostSide.ALL); + this.manaCosts = new ManaCostsImpl<>(String.join("", this.manaCostsSymbols)); } public MockSplitCardHalf(final MockSplitCardHalf card) { super(card); + this.splitCardParent = card.splitCardParent; + this.manaCosts = card.manaCosts.copy(); + this.manaCostsSymbols = new ArrayList<>(card.manaCostsSymbols); } @Override @@ -36,4 +50,15 @@ public SplitCard getParentCard() { return splitCardParent; } + @Override + public ManaCosts getManaCost() { + // only split half cards can store mana cost in objects list instead strings (memory optimization) + return manaCosts; + } + + @Override + public List getManaCostSymbols() { + // only split half cards can store mana cost in objects list instead strings (memory optimization) + return manaCostsSymbols; + } } diff --git a/Mage/src/main/java/mage/cards/repository/CardCriteria.java b/Mage/src/main/java/mage/cards/repository/CardCriteria.java index cb8ec259fd91..94d73d0da281 100644 --- a/Mage/src/main/java/mage/cards/repository/CardCriteria.java +++ b/Mage/src/main/java/mage/cards/repository/CardCriteria.java @@ -34,7 +34,7 @@ public class CardCriteria { private boolean red; private boolean white; private boolean colorless; - private Integer convertedManaCost; + private Integer manaValue; private String sortBy; private Long start; private Long count; @@ -167,8 +167,8 @@ public CardCriteria subtypes(String... subtypes) { return this; } - public CardCriteria convertedManaCost(Integer convertedManaCost) { - this.convertedManaCost = convertedManaCost; + public CardCriteria manaValue(Integer manaValue) { + this.manaValue = manaValue; return this; } @@ -265,8 +265,8 @@ public void buildQuery(QueryBuilder qb) throws SQLException { clausesCount++; } - if (convertedManaCost != null) { - where.eq("convertedManaCost", convertedManaCost); + if (manaValue != null) { + where.eq("manaValue", manaValue); clausesCount++; } @@ -433,8 +433,8 @@ public boolean isColorless() { return colorless; } - public Integer getConvertedManaCost() { - return convertedManaCost; + public Integer getManaValue() { + return manaValue; } public String getSortBy() { diff --git a/Mage/src/main/java/mage/cards/repository/CardInfo.java b/Mage/src/main/java/mage/cards/repository/CardInfo.java index 2c60fcd89630..fd750f9443d8 100644 --- a/Mage/src/main/java/mage/cards/repository/CardInfo.java +++ b/Mage/src/main/java/mage/cards/repository/CardInfo.java @@ -53,7 +53,7 @@ public class CardInfo { @DatabaseField protected String startingLoyalty; @DatabaseField - protected int convertedManaCost; + protected int manaValue; @DatabaseField(dataType = DataType.ENUM_STRING) protected Rarity rarity; @DatabaseField @@ -126,7 +126,7 @@ public CardInfo(Card card) { this.className = card.getClass().getCanonicalName(); this.power = card.getPower().toString(); this.toughness = card.getToughness().toString(); - this.convertedManaCost = card.getConvertedManaCost(); + this.manaValue = card.getManaValue(); this.rarity = card.getRarity(); this.splitCard = card instanceof SplitCard; this.splitCardFuse = card.getSpellAbility() != null && card.getSpellAbility().getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED; @@ -167,19 +167,19 @@ public CardInfo(Card card) { // mana cost can contains multiple cards (split left/right, modal double faces, card/adventure) if (card instanceof SplitCard) { - List manaCostLeft = ((SplitCard) card).getLeftHalfCard().getManaCost().getSymbols(); - List manaCostRight = ((SplitCard) card).getRightHalfCard().getManaCost().getSymbols(); + List manaCostLeft = ((SplitCard) card).getLeftHalfCard().getManaCostSymbols(); + List manaCostRight = ((SplitCard) card).getRightHalfCard().getManaCostSymbols(); this.setManaCosts(CardUtil.concatManaSymbols(SPLIT_MANA_SEPARATOR_FULL, manaCostLeft, manaCostRight)); } else if (card instanceof ModalDoubleFacesCard) { - List manaCostLeft = ((ModalDoubleFacesCard) card).getLeftHalfCard().getManaCost().getSymbols(); - List manaCostRight = ((ModalDoubleFacesCard) card).getRightHalfCard().getManaCost().getSymbols(); + List manaCostLeft = ((ModalDoubleFacesCard) card).getLeftHalfCard().getManaCostSymbols(); + List manaCostRight = ((ModalDoubleFacesCard) card).getRightHalfCard().getManaCostSymbols(); this.setManaCosts(CardUtil.concatManaSymbols(SPLIT_MANA_SEPARATOR_FULL, manaCostLeft, manaCostRight)); } else if (card instanceof AdventureCard) { - List manaCostLeft = ((AdventureCard) card).getSpellCard().getManaCost().getSymbols(); - List manaCostRight = card.getManaCost().getSymbols(); + List manaCostLeft = ((AdventureCard) card).getSpellCard().getManaCostSymbols(); + List manaCostRight = card.getManaCostSymbols(); this.setManaCosts(CardUtil.concatManaSymbols(SPLIT_MANA_SEPARATOR_FULL, manaCostLeft, manaCostRight)); } else { - this.setManaCosts(card.getManaCost().getSymbols()); + this.setManaCosts(card.getManaCostSymbols()); } int length = 0; @@ -293,7 +293,7 @@ private String joinList(List items) { return sb.toString(); } - private List parseList(String list, ManaCostSide manaCostSide) { + public static List parseList(String list, ManaCostSide manaCostSide) { if (list.isEmpty()) { return Collections.emptyList(); } @@ -335,8 +335,8 @@ public final void setTypes(ArrayList types) { this.types = sb.toString(); } - public int getConvertedManaCost() { - return convertedManaCost; + public int getManaValue() { + return manaValue; } public final List getManaCosts(ManaCostSide manaCostSide) { diff --git a/Mage/src/main/java/mage/cards/repository/CardRepository.java b/Mage/src/main/java/mage/cards/repository/CardRepository.java index a8e663a12d64..e96c6174c985 100644 --- a/Mage/src/main/java/mage/cards/repository/CardRepository.java +++ b/Mage/src/main/java/mage/cards/repository/CardRepository.java @@ -36,7 +36,7 @@ public enum CardRepository { // raise this if db structure was changed private static final long CARD_DB_VERSION = 53; // raise this if new cards were added to the server - private static final long CARD_CONTENT_VERSION = 236; + private static final long CARD_CONTENT_VERSION = 238; private Dao cardDao; private Set classNames; private final RepositoryEventSource eventSource = new RepositoryEventSource(); diff --git a/Mage/src/main/java/mage/choices/ChoiceColor.java b/Mage/src/main/java/mage/choices/ChoiceColor.java index e93a2b844a8c..47c6775be5fd 100644 --- a/Mage/src/main/java/mage/choices/ChoiceColor.java +++ b/Mage/src/main/java/mage/choices/ChoiceColor.java @@ -63,11 +63,15 @@ public void removeColorFromChoices(String colorName) { } public ObjectColor getColor() { - if (choice == null) { + return getColorFromString(choice); + } + + public static ObjectColor getColorFromString(String colorString) { + if (colorString == null) { return null; } ObjectColor color = new ObjectColor(); - switch (choice) { + switch (colorString) { case "Black": color.setBlack(true); break; diff --git a/Mage/src/main/java/mage/choices/Choices.java b/Mage/src/main/java/mage/choices/Choices.java index 9bb5c0910dbf..c934575142f3 100644 --- a/Mage/src/main/java/mage/choices/Choices.java +++ b/Mage/src/main/java/mage/choices/Choices.java @@ -56,6 +56,9 @@ public boolean isChosen() { public boolean choose(Game game, Ability source) { if (this.size() > 0) { Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } while (!isChosen()) { Choice choice = this.getUnchosen().get(0); if (!player.choose(outcome, choice, game)) { diff --git a/Mage/src/main/java/mage/choices/ChooseFriendsAndFoes.java b/Mage/src/main/java/mage/choices/ChooseFriendsAndFoes.java index 6ab7e4891d78..81191af538cf 100644 --- a/Mage/src/main/java/mage/choices/ChooseFriendsAndFoes.java +++ b/Mage/src/main/java/mage/choices/ChooseFriendsAndFoes.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.choices; import java.util.ArrayList; diff --git a/Mage/src/main/java/mage/choices/TwoChoiceVote.java b/Mage/src/main/java/mage/choices/TwoChoiceVote.java new file mode 100644 index 000000000000..1bd3d831feeb --- /dev/null +++ b/Mage/src/main/java/mage/choices/TwoChoiceVote.java @@ -0,0 +1,41 @@ +package mage.choices; + +import mage.abilities.Ability; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; + +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * @author TheElk801 + */ +public class TwoChoiceVote extends VoteHandler { + + private final String choice1; + private final String choice2; + private final Outcome outcome; + + public TwoChoiceVote(String choice1, String choice2, Outcome outcome) { + this.choice1 = choice1; + this.choice2 = choice2; + this.outcome = outcome; + } + + @Override + protected Set getPossibleVotes(Ability source, Game game) { + return new LinkedHashSet<>(Arrays.asList(Boolean.TRUE, Boolean.FALSE)); + } + + @Override + public Boolean playerChoose(String voteInfo, Player player, Player decidingPlayer, Ability source, Game game) { + return decidingPlayer.chooseUse(outcome, voteInfo, null, choice1, choice2, source, game); + } + + @Override + protected String voteName(Boolean vote) { + return (vote ? choice1 : choice2); + } +} diff --git a/Mage/src/main/java/mage/choices/VoteHandler.java b/Mage/src/main/java/mage/choices/VoteHandler.java new file mode 100644 index 000000000000..ba561d770f3c --- /dev/null +++ b/Mage/src/main/java/mage/choices/VoteHandler.java @@ -0,0 +1,182 @@ +package mage.choices; + +import mage.abilities.Ability; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.events.VoteEvent; +import mage.game.events.VotedEvent; +import mage.players.Player; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public abstract class VoteHandler { + + protected final Map> playerMap = new HashMap<>(); + protected VoteHandlerAI aiVoteHint = null; + + public void doVotes(Ability source, Game game) { + doVotes(source, game, null); + } + + public void doVotes(Ability source, Game game, VoteHandlerAI aiVoteHint) { + this.aiVoteHint = aiVoteHint; + this.playerMap.clear(); + int stepCurrent = 0; + int stepTotal = game.getState().getPlayersInRange(source.getControllerId(), game).size(); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + stepCurrent++; + VoteEvent event = new VoteEvent(playerId, source); + game.replaceEvent(event); + Player player = game.getPlayer(event.getTargetId()); + Player decidingPlayer = game.getPlayer(event.getPlayerId()); + if (player == null || decidingPlayer == null) { + continue; + } + int voteCount = event.getExtraVotes() + event.getOptionalExtraVotes() + 1; + for (int i = 0; i < voteCount; i++) { + // Decision for extra choice goes from original player, not from deciding. + // Rules from Illusion of Choice: + // If another player controls Ballot Broker, that player first takes their “normal” vote + // with you choosing the result, then that player decides whether they are taking the + // additional vote. If there is an additional vote, you again choose the result. + // (2016-08-23) + + // Outcome.Benefit - AI must use extra vote all the time + if (i > event.getExtraVotes() && !player.chooseUse( + Outcome.Benefit, "Use an extra vote?", source, game + )) { + continue; + } + + String stepName = (i > 0 ? "extra step" : "step"); + String voteInfo = String.format("Vote, %s %d of %d", stepName, stepCurrent, stepTotal); + T vote; + if (decidingPlayer.isComputer() && this.aiVoteHint != null) { + // ai choose + vote = this.aiVoteHint.makeChoice(this, player, decidingPlayer, source, game); + } else { + // human choose + vote = playerChoose(voteInfo, player, decidingPlayer, source, game); + } + if (vote == null) { + continue; + } + String message = voteInfo + ": " + player.getName() + " voted for " + voteName(vote); + if (!Objects.equals(player, decidingPlayer)) { + message += " (chosen by " + decidingPlayer.getName() + ')'; + } + game.informPlayers(message); + this.playerMap.computeIfAbsent(playerId, x -> new ArrayList<>()).add(vote); + } + } + + // show final results to players + + Map totalVotes = new LinkedHashMap<>(); + // fill by possible choices + this.getPossibleVotes(source, game).forEach(vote -> { + totalVotes.putIfAbsent(vote, 0); + }); + // fill by real choices + playerMap.entrySet() + .stream() + .flatMap(votesList -> votesList.getValue().stream()) + .forEach(vote -> { + totalVotes.compute(vote, (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + }); + + Set winners = this.getMostVoted(); + String totalVotesStr = totalVotes.entrySet() + .stream() + .map(entry -> (winners.contains(entry.getKey()) ? " -win- " : " -lose- ") + voteName(entry.getKey()) + ": " + entry.getValue()) + .sorted() + .collect(Collectors.joining("
")); + game.informPlayers("Vote results:
" + totalVotesStr); + + game.fireEvent(new VotedEvent(source, this)); + } + + /** + * Return possible votes. Uses for info only (final results). + * + * @param source + * @param game + * @return + */ + protected abstract Set getPossibleVotes(Ability source, Game game); + + /** + * Choose dialog for voting. Another player can choose it (example: Illusion of Choice) + * + * @param player + * @param decidingPlayer + * @param source + * @param game + * @return + */ + protected abstract T playerChoose(String voteInfo, Player player, Player decidingPlayer, Ability source, Game game); + + /** + * Show readable choice name + * + * @param vote + * @return + */ + protected abstract String voteName(T vote); + + public List getVotes(UUID playerId) { + return playerMap.computeIfAbsent(playerId, x -> new ArrayList<>()); + } + + public int getVoteCount(T vote) { + return playerMap + .values() + .stream() + .flatMap(Collection::stream) + .map(vote::equals) + .mapToInt(x -> x ? 1 : 0) + .sum(); + } + + public List getVotedFor(T vote) { + return playerMap + .entrySet() + .stream() + .filter(entry -> entry.getValue() != null && entry.getValue().contains(vote)) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + } + + public Set getMostVoted() { + Map map = new HashMap<>(); + playerMap + .values() + .stream() + .flatMap(Collection::stream) + .forEach(t -> map.compute(t, (s, i) -> i == null ? 1 : Integer.sum(i, 1))); + int max = map.values().stream().mapToInt(x -> x).max().orElse(0); + return map + .entrySet() + .stream() + .filter(e -> e.getValue() >= max) + .map(Map.Entry::getKey) + .collect(Collectors.toSet()); + } + + public Set getDidntVote(UUID playerId) { + if (playerMap.computeIfAbsent(playerId, x -> new ArrayList<>()).isEmpty()) { + return playerMap.keySet(); + } + return playerMap + .entrySet() + .stream() + .filter(e -> e.getValue() != null && !e.getValue().isEmpty()) + .filter(e -> !e.getValue().stream().allMatch(playerMap.get(playerId)::contains)) + .map(Map.Entry::getKey) + .collect(Collectors.toSet()); + } +} diff --git a/Mage/src/main/java/mage/choices/VoteHandlerAI.java b/Mage/src/main/java/mage/choices/VoteHandlerAI.java new file mode 100644 index 000000000000..c5e82f7b39f5 --- /dev/null +++ b/Mage/src/main/java/mage/choices/VoteHandlerAI.java @@ -0,0 +1,23 @@ +package mage.choices; + +import mage.abilities.Ability; +import mage.game.Game; +import mage.players.Player; + +/** + * @author JayDi85 + */ +@FunctionalInterface +public interface VoteHandlerAI { + + /** + * AI choosing hints for votes + * + * @param voteHandler voting handler for choosing + * @param aiPlayer player who must choose + * @param aiDecidingPlayer real player who make a choice (cab be changed by another effect, example: Illusion of Choice) + * @param aiSource + * @param aiGame + */ + T makeChoice(VoteHandler voteHandler, Player aiPlayer, Player aiDecidingPlayer, Ability aiSource, Game aiGame); +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/collation/BoosterCollator.java b/Mage/src/main/java/mage/collation/BoosterCollator.java new file mode 100644 index 000000000000..350b4bcbb4fb --- /dev/null +++ b/Mage/src/main/java/mage/collation/BoosterCollator.java @@ -0,0 +1,13 @@ +package mage.collation; + +import java.util.List; + +/** + * @author TheElk801 + */ +public interface BoosterCollator { + + public void shuffle(); + + public List makeBooster(); +} diff --git a/Mage/src/main/java/mage/collation/BoosterStructure.java b/Mage/src/main/java/mage/collation/BoosterStructure.java new file mode 100644 index 000000000000..23aa81e4a7f3 --- /dev/null +++ b/Mage/src/main/java/mage/collation/BoosterStructure.java @@ -0,0 +1,36 @@ +package mage.collation; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Current implementation is built only for sequential collation + * Boosters are collated through a variety of different methods + * Striped collation not supported yet + * For more information: https://www.lethe.xyz/mtg/collation/ + * + * @author TheElk801 + */ +public abstract class BoosterStructure { + + private final List slots; + + protected BoosterStructure(CardRun... runs) { + this.slots = Arrays.asList(runs); + } + + public List makeRun() { + List cards = new ArrayList<>(); + for (CardRun run : this.slots) { + cards.add(run.getNext()); + } + return cards; + } + + public void shuffle() { + for (CardRun run : this.slots) { + run.shuffle(); + } + } +} diff --git a/Mage/src/main/java/mage/collation/CardRun.java b/Mage/src/main/java/mage/collation/CardRun.java new file mode 100644 index 000000000000..1889b59f0d37 --- /dev/null +++ b/Mage/src/main/java/mage/collation/CardRun.java @@ -0,0 +1,11 @@ +package mage.collation; + +/** + * @author TheElk801 + */ +public abstract class CardRun extends Rotater { + + public CardRun(boolean keepOrder, String... numbers) { + super(keepOrder, numbers); + } +} diff --git a/Mage/src/main/java/mage/collation/RarityConfiguration.java b/Mage/src/main/java/mage/collation/RarityConfiguration.java new file mode 100644 index 000000000000..3ba6807b860a --- /dev/null +++ b/Mage/src/main/java/mage/collation/RarityConfiguration.java @@ -0,0 +1,27 @@ +package mage.collation; + +/** + * @author TheElk801 + */ +public class RarityConfiguration extends Rotater { + + public RarityConfiguration(BoosterStructure item) { + super(item); + } + + public RarityConfiguration(BoosterStructure item1, BoosterStructure item2) { + super(item1, item2); + } + + public RarityConfiguration(boolean keepOrder, BoosterStructure... items) { + super(keepOrder, items); + } + + @Override + public void shuffle() { + for (BoosterStructure structure : this.items) { + structure.shuffle(); + } + super.shuffle(); + } +} diff --git a/Mage/src/main/java/mage/collation/Rotater.java b/Mage/src/main/java/mage/collation/Rotater.java new file mode 100644 index 000000000000..6558106e50ef --- /dev/null +++ b/Mage/src/main/java/mage/collation/Rotater.java @@ -0,0 +1,50 @@ +package mage.collation; + +import mage.util.RandomUtil; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * A class for shuffling a list by choosing a random starting point and looping through it + * + * @author TheElk801 + */ +public class Rotater { + + protected final List items; + private final boolean keepOrder; + private int position = 0; + + public Rotater(T item) { + this(true, item); + } + + public Rotater(T item1, T item2) { + this(true, item1, item2); + } + + public Rotater(boolean keepOrder, T... items) { + this.items = Arrays.asList(items); + this.keepOrder = keepOrder; + } + + public int iterate() { + int i = position; + position++; + position %= items.size(); + return i; + } + + public T getNext() { + return items.get(iterate()); + } + + public void shuffle() { + position = RandomUtil.nextInt(items.size()); + if (!keepOrder) { + Collections.shuffle(items, RandomUtil.getRandom()); + } + } +} diff --git a/Mage/src/main/java/mage/constants/AbilityWord.java b/Mage/src/main/java/mage/constants/AbilityWord.java index e37da7f9d0e6..18a15835f04e 100644 --- a/Mage/src/main/java/mage/constants/AbilityWord.java +++ b/Mage/src/main/java/mage/constants/AbilityWord.java @@ -32,6 +32,7 @@ public enum AbilityWord { LANDFALL("Landfall"), LIEUTENANT("Lieutenant"), METALCRAFT("Metalcraft"), + MAGECRAFT("Magecraft"), MORBID("Morbid"), PARLEY("Parley"), RADIANCE("Radiance"), diff --git a/Mage/src/main/java/mage/constants/MultiAmountType.java b/Mage/src/main/java/mage/constants/MultiAmountType.java new file mode 100644 index 000000000000..feed33809d53 --- /dev/null +++ b/Mage/src/main/java/mage/constants/MultiAmountType.java @@ -0,0 +1,108 @@ +package mage.constants; + +import com.google.common.collect.Iterables; +import mage.util.CardUtil; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.stream.IntStream; + +public enum MultiAmountType { + + MANA("Add mana", "Distribute mana among colors"), + DAMAGE("Assign damage", "Assign damage among targets"); + + private final String title; + private final String header; + + MultiAmountType(String title, String header) { + this.title = title; + this.header = header; + } + + public String getTitle() { + return title; + } + + public String getHeader() { + return header; + } + + public static List prepareDefaltValues(int count, int min, int max) { + // default values must be assigned from first to last by minimum values + List res = new ArrayList<>(); + if (count == 0) { + return res; + } + + // fill list + IntStream.range(0, count).forEach(i -> res.add(0)); + + // fill values + if (min > 0) { + res.set(0, min); + } + + return res; + } + + public static List prepareMaxValues(int count, int min, int max) { + // fill max values as much as possible + List res = new ArrayList<>(); + if (count == 0) { + return res; + } + + // fill list + int startingValue = max / count; + IntStream.range(0, count).forEach(i -> res.add(startingValue)); + + // fill values + // from first to last until complete + List resIndexes = new ArrayList<>(res.size()); + IntStream.range(0, res.size()).forEach(resIndexes::add); + // infinite iterator (no needs with starting values use, but can be used later for different logic) + Iterator resIterator = Iterables.cycle(resIndexes).iterator(); + int valueInc = 1; + int valueTotal = startingValue * count; + while (valueTotal < max) { + int currentIndex = resIterator.next(); + int newValue = CardUtil.overflowInc(res.get(currentIndex), valueInc); + res.set(currentIndex, newValue); + valueTotal += valueInc; + } + + return res; + } + + public static boolean isGoodValues(List values, int count, int min, int max) { + if (values.size() != count) { + return false; + } + + int currentSum = values.stream().mapToInt(i -> i).sum(); + return currentSum >= min && currentSum <= max; + } + + public static List parseAnswer(String answerToParse, int count, int min, int max, boolean returnDefaultOnError) { + List res = new ArrayList<>(); + + // parse + String normalValue = answerToParse.trim(); + if (!normalValue.isEmpty()) { + Arrays.stream(normalValue.split(" ")).forEach(valueStr -> { + res.add(CardUtil.parseIntWithDefault(valueStr, 0)); + }); + } + + // data check + if (returnDefaultOnError && !isGoodValues(res, count, min, max)) { + // on broken data - return default + return prepareDefaltValues(count, min, max); + } + + return res; + } +} diff --git a/Mage/src/main/java/mage/constants/SubLayer.java b/Mage/src/main/java/mage/constants/SubLayer.java index 242aa769b10e..008c21aaf9c4 100644 --- a/Mage/src/main/java/mage/constants/SubLayer.java +++ b/Mage/src/main/java/mage/constants/SubLayer.java @@ -1,10 +1,11 @@ package mage.constants; /** - * * @author North */ public enum SubLayer { + CopyEffects_1a, + FaceDownEffects_1b, CharacteristicDefining_7a, SetPT_7b, ModifyPT_7c, diff --git a/Mage/src/main/java/mage/constants/SubType.java b/Mage/src/main/java/mage/constants/SubType.java index 55079b085d19..470941b8a95c 100644 --- a/Mage/src/main/java/mage/constants/SubType.java +++ b/Mage/src/main/java/mage/constants/SubType.java @@ -12,6 +12,7 @@ public enum SubType { //205.3k Instants and sorceries share their lists of subtypes; these subtypes are called spell types. ADVENTURE("Adventure", SubTypeSet.SpellType), ARCANE("Arcane", SubTypeSet.SpellType), + LESSON("Lesson", SubTypeSet.SpellType), TRAP("Trap", SubTypeSet.SpellType), // 205.3i: Lands have their own unique set of subtypes; these subtypes are called land types. // Of that list, Forest, Island, Mountain, Plains, and Swamp are the basic land types. @@ -146,6 +147,7 @@ public enum SubType { FISH("Fish", SubTypeSet.CreatureType), FLAGBEARER("Flagbearer", SubTypeSet.CreatureType), FOX("Fox", SubTypeSet.CreatureType), + FRACTAL("Fractal", SubTypeSet.CreatureType), FROG("Frog", SubTypeSet.CreatureType), FUNGUS("Fungus", SubTypeSet.CreatureType), // G @@ -184,6 +186,7 @@ public enum SubType { ILLUSION("Illusion", SubTypeSet.CreatureType), IMP("Imp", SubTypeSet.CreatureType), INCARNATION("Incarnation", SubTypeSet.CreatureType), + INKLING("Inkling", SubTypeSet.CreatureType), INSECT("Insect", SubTypeSet.CreatureType), ITHORIAN("Ithorian", SubTypeSet.CreatureType, true), // Star Wars // J diff --git a/Mage/src/main/java/mage/counters/CounterType.java b/Mage/src/main/java/mage/counters/CounterType.java index 9841626fcb20..7bbfa35e0aa2 100644 --- a/Mage/src/main/java/mage/counters/CounterType.java +++ b/Mage/src/main/java/mage/counters/CounterType.java @@ -20,6 +20,7 @@ public enum CounterType { AWAKENING("awakening"), BLAZE("blaze"), BLOOD("blood"), + BOOK("book"), BOUNTY("bounty"), BRIBERY("bribery"), BRICK("brick"), @@ -74,6 +75,7 @@ public enum CounterType { HEXPROOF("hexproof"), HIT("hit"), HOOFPRINT("hoofprint"), + HONE("hone"), HOUR("hour", "an"), HOURGLASS("hourglass", "an"), HUNGER("hunger"), @@ -126,6 +128,7 @@ public enum CounterType { PLAGUE("plague"), PLOT("plot"), POLYP("polyp"), + POINT("point"), POISON("poison"), PRESSURE("pressure"), PREY("prey"), @@ -164,6 +167,7 @@ public enum CounterType { VIGILANCE("vigilance"), VITALITY("vitality"), VORTEX("vortex"), + VOW("vow"), VOYAGE("voyage"), WAGE("wage"), WINCH("winch"), diff --git a/Mage/src/main/java/mage/designations/Designation.java b/Mage/src/main/java/mage/designations/Designation.java index 30263940c510..a89cf4e6fee7 100644 --- a/Mage/src/main/java/mage/designations/Designation.java +++ b/Mage/src/main/java/mage/designations/Designation.java @@ -196,7 +196,7 @@ public ManaCosts getManaCost() { } @Override - public int getConvertedManaCost() { + public int getManaValue() { return 0; } diff --git a/Mage/src/main/java/mage/filter/FilterPermanent.java b/Mage/src/main/java/mage/filter/FilterPermanent.java index 63ec4379e380..f7d4e9759a70 100644 --- a/Mage/src/main/java/mage/filter/FilterPermanent.java +++ b/Mage/src/main/java/mage/filter/FilterPermanent.java @@ -52,7 +52,7 @@ public boolean checkObjectClass(Object object) { @Override public boolean match(Permanent permanent, UUID sourceId, UUID playerId, Game game) { - if (!permanent.isPhasedIn() || !this.match(permanent, game)) { + if (!this.match(permanent, game) || !permanent.isPhasedIn()) { return false; } diff --git a/Mage/src/main/java/mage/filter/StaticFilters.java b/Mage/src/main/java/mage/filter/StaticFilters.java index 566db4eac9f5..146451c1aaec 100644 --- a/Mage/src/main/java/mage/filter/StaticFilters.java +++ b/Mage/src/main/java/mage/filter/StaticFilters.java @@ -6,9 +6,9 @@ import mage.constants.TargetController; import mage.filter.common.*; import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.mageobject.KickedSpellPredicate; import mage.filter.predicate.mageobject.MulticoloredPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; import mage.filter.predicate.permanent.AttackingPredicate; import mage.filter.predicate.permanent.TokenPredicate; @@ -114,6 +114,12 @@ public final class StaticFilters { FILTER_CARD_FROM_YOUR_GRAVEYARD.setLockedFilter(true); } + public static final FilterCard FILTER_CARD_INSTANT_OR_SORCERY_FROM_YOUR_GRAVEYARD = new FilterInstantOrSorceryCard("instant or sorcery card from your graveyard"); + + static { + FILTER_CARD_INSTANT_OR_SORCERY_FROM_YOUR_GRAVEYARD.setLockedFilter(true); + } + public static final FilterNoncreatureCard FILTER_CARD_NON_CREATURE = new FilterNoncreatureCard(); static { @@ -246,10 +252,16 @@ public final class StaticFilters { FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT.setLockedFilter(true); } - public static final FilterCreaturePermanent FILTER_ARTIFACT_CREATURE_PERMANENT = new FilterArtifactCreaturePermanent(); + public static final FilterCreaturePermanent FILTER_PERMANENT_ARTIFACT_CREATURE = new FilterArtifactCreaturePermanent(); + + static { + FILTER_PERMANENT_ARTIFACT_CREATURE.setLockedFilter(true); + } + + public static final FilterCreaturePermanent FILTER_PERMANENTS_ARTIFACT_CREATURE = new FilterArtifactCreaturePermanent("artifact creatures"); static { - FILTER_ARTIFACT_CREATURE_PERMANENT.setLockedFilter(true); + FILTER_PERMANENTS_ARTIFACT_CREATURE.setLockedFilter(true); } public static final FilterControlledArtifactPermanent FILTER_ARTIFACTS_NON_CREATURE = new FilterControlledArtifactPermanent("Noncreature artifacts"); @@ -298,6 +310,12 @@ public final class StaticFilters { FILTER_CONTROLLED_PERMANENT.setLockedFilter(true); } + public static final FilterControlledPermanent FILTER_CONTROLLED_A_PERMANENT = new FilterControlledPermanent("a permanent you control"); + + static { + FILTER_CONTROLLED_A_PERMANENT.setLockedFilter(true); + } + public static final FilterControlledPermanent FILTER_CONTROLLED_PERMANENT_ARTIFACT = new FilterControlledArtifactPermanent(); static { @@ -559,6 +577,12 @@ public final class StaticFilters { FILTER_SPELL_OR_ABILITY.setLockedFilter(true); } + public static final FilterStackObject FILTER_SPELL_OR_ABILITY_A = new FilterStackObject("a spell or ability"); + + static { + FILTER_SPELL_OR_ABILITY_A.setLockedFilter(true); + } + public static final FilterCreatureSpell FILTER_SPELL_A_CREATURE = new FilterCreatureSpell("a creature spell"); static { @@ -650,6 +674,13 @@ public final class StaticFilters { FILTER_SPELL_KICKED_A.setLockedFilter(true); } + public static final FilterCreaturePermanent FILTER_CREATURE_TOKEN = new FilterCreaturePermanent("creature token"); + + static { + FILTER_CREATURE_TOKEN.add(TokenPredicate.instance); + FILTER_CREATURE_TOKEN.setLockedFilter(true); + } + public static final FilterCreaturePermanent FILTER_CREATURE_TOKENS = new FilterCreaturePermanent("creature tokens"); static { diff --git a/Mage/src/main/java/mage/filter/common/FilterCreatureAttackingYou.java b/Mage/src/main/java/mage/filter/common/FilterCreatureAttackingYou.java index 4657174caa12..abe2d05ac80b 100644 --- a/Mage/src/main/java/mage/filter/common/FilterCreatureAttackingYou.java +++ b/Mage/src/main/java/mage/filter/common/FilterCreatureAttackingYou.java @@ -19,7 +19,7 @@ public FilterCreatureAttackingYou() { } public FilterCreatureAttackingYou(boolean orWalker) { - this("creature that's attacking you" + (orWalker ? "or a planeswalker you control" : ""), orWalker); + this("creature that's attacking you" + (orWalker ? " or a planeswalker you control" : ""), orWalker); } public FilterCreatureAttackingYou(String name) { diff --git a/Mage/src/main/java/mage/filter/common/FilterHistoricCard.java b/Mage/src/main/java/mage/filter/common/FilterHistoricCard.java index eb3f7cf1c1f1..b4a1e39f9375 100644 --- a/Mage/src/main/java/mage/filter/common/FilterHistoricCard.java +++ b/Mage/src/main/java/mage/filter/common/FilterHistoricCard.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.filter.common; import mage.filter.FilterCard; diff --git a/Mage/src/main/java/mage/filter/common/FilterOpponentOrPlaneswalker.java b/Mage/src/main/java/mage/filter/common/FilterOpponentOrPlaneswalker.java index 807c110f80a7..2711c4bdc1a0 100644 --- a/Mage/src/main/java/mage/filter/common/FilterOpponentOrPlaneswalker.java +++ b/Mage/src/main/java/mage/filter/common/FilterOpponentOrPlaneswalker.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.filter.common; import mage.filter.FilterOpponent; diff --git a/Mage/src/main/java/mage/filter/common/FilterOpponentsCreaturePermanent.java b/Mage/src/main/java/mage/filter/common/FilterOpponentsCreaturePermanent.java index c50379ba4638..30cfd185ab6b 100644 --- a/Mage/src/main/java/mage/filter/common/FilterOpponentsCreaturePermanent.java +++ b/Mage/src/main/java/mage/filter/common/FilterOpponentsCreaturePermanent.java @@ -3,11 +3,6 @@ import mage.constants.SubType; import mage.constants.TargetController; -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ /** * * @author Styxo diff --git a/Mage/src/main/java/mage/filter/common/FilterPermanentOrPlayer.java b/Mage/src/main/java/mage/filter/common/FilterPermanentOrPlayer.java index 327404dfd192..c885b03522de 100644 --- a/Mage/src/main/java/mage/filter/common/FilterPermanentOrPlayer.java +++ b/Mage/src/main/java/mage/filter/common/FilterPermanentOrPlayer.java @@ -5,6 +5,8 @@ import mage.filter.FilterInPlay; import mage.filter.FilterPermanent; import mage.filter.FilterPlayer; +import mage.filter.predicate.ObjectPlayerPredicate; +import mage.filter.predicate.Predicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -44,6 +46,11 @@ public boolean checkObjectClass(Object object) { return true; } + public void add(ObjectPlayerPredicate predicate) { + playerFilter.add((Predicate) predicate); + permanentFilter.add((Predicate) predicate); + } + @Override public boolean match(MageItem o, Game game) { if (super.match(o, game)) { diff --git a/Mage/src/main/java/mage/filter/common/FilterPlayerOrPlaneswalker.java b/Mage/src/main/java/mage/filter/common/FilterPlayerOrPlaneswalker.java index 2ba0c1247722..5ab775c43e18 100644 --- a/Mage/src/main/java/mage/filter/common/FilterPlayerOrPlaneswalker.java +++ b/Mage/src/main/java/mage/filter/common/FilterPlayerOrPlaneswalker.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.filter.common; import mage.filter.FilterPlayer; diff --git a/Mage/src/main/java/mage/filter/common/FilterTeamCreaturePermanent.java b/Mage/src/main/java/mage/filter/common/FilterTeamCreaturePermanent.java index 06bec479d07c..3b6a01a30cfe 100644 --- a/Mage/src/main/java/mage/filter/common/FilterTeamCreaturePermanent.java +++ b/Mage/src/main/java/mage/filter/common/FilterTeamCreaturePermanent.java @@ -3,11 +3,6 @@ import mage.constants.SubType; import mage.constants.TargetController; -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ /** * * @author TheElk801 diff --git a/Mage/src/main/java/mage/filter/common/FilterTeamPermanent.java b/Mage/src/main/java/mage/filter/common/FilterTeamPermanent.java index 6fb625d39733..2d55e7361415 100644 --- a/Mage/src/main/java/mage/filter/common/FilterTeamPermanent.java +++ b/Mage/src/main/java/mage/filter/common/FilterTeamPermanent.java @@ -4,11 +4,6 @@ import mage.constants.TargetController; import mage.filter.FilterPermanent; -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ /** * * @author TheElk801 diff --git a/Mage/src/main/java/mage/filter/predicate/other/AuraCardCanAttachToLKIPermanentId.java b/Mage/src/main/java/mage/filter/predicate/card/AuraCardCanAttachToLKIPermanentId.java similarity index 96% rename from Mage/src/main/java/mage/filter/predicate/other/AuraCardCanAttachToLKIPermanentId.java rename to Mage/src/main/java/mage/filter/predicate/card/AuraCardCanAttachToLKIPermanentId.java index 9f4f7040d04e..202b421c8ceb 100644 --- a/Mage/src/main/java/mage/filter/predicate/other/AuraCardCanAttachToLKIPermanentId.java +++ b/Mage/src/main/java/mage/filter/predicate/card/AuraCardCanAttachToLKIPermanentId.java @@ -1,6 +1,6 @@ -package mage.filter.predicate.other; +package mage.filter.predicate.card; import java.util.UUID; import mage.cards.Card; diff --git a/Mage/src/main/java/mage/filter/predicate/other/AuraCardCanAttachToPermanentId.java b/Mage/src/main/java/mage/filter/predicate/card/AuraCardCanAttachToPermanentId.java similarity index 97% rename from Mage/src/main/java/mage/filter/predicate/other/AuraCardCanAttachToPermanentId.java rename to Mage/src/main/java/mage/filter/predicate/card/AuraCardCanAttachToPermanentId.java index 54d3c490ed40..f8d5b2e490c4 100644 --- a/Mage/src/main/java/mage/filter/predicate/other/AuraCardCanAttachToPermanentId.java +++ b/Mage/src/main/java/mage/filter/predicate/card/AuraCardCanAttachToPermanentId.java @@ -1,4 +1,4 @@ -package mage.filter.predicate.other; +package mage.filter.predicate.card; import java.util.UUID; import mage.cards.Card; diff --git a/Mage/src/main/java/mage/filter/predicate/other/CardCanTargetPermanentId.java b/Mage/src/main/java/mage/filter/predicate/card/CardCanTargetPermanentId.java similarity index 95% rename from Mage/src/main/java/mage/filter/predicate/other/CardCanTargetPermanentId.java rename to Mage/src/main/java/mage/filter/predicate/card/CardCanTargetPermanentId.java index d87cb515c73b..e20266c45a28 100644 --- a/Mage/src/main/java/mage/filter/predicate/other/CardCanTargetPermanentId.java +++ b/Mage/src/main/java/mage/filter/predicate/card/CardCanTargetPermanentId.java @@ -1,5 +1,5 @@ -package mage.filter.predicate.other; +package mage.filter.predicate.card; import java.util.UUID; import mage.cards.Card; diff --git a/Mage/src/main/java/mage/filter/predicate/card/CardOnTopOfLibraryPredicate.java b/Mage/src/main/java/mage/filter/predicate/card/CardOnTopOfLibraryPredicate.java new file mode 100644 index 000000000000..6024ed0c9053 --- /dev/null +++ b/Mage/src/main/java/mage/filter/predicate/card/CardOnTopOfLibraryPredicate.java @@ -0,0 +1,26 @@ +package mage.filter.predicate.card; + +import mage.cards.Card; +import mage.filter.predicate.ObjectPlayer; +import mage.filter.predicate.ObjectPlayerPredicate; +import mage.game.Game; +import mage.players.Player; + +/** + * @author JayDi85 + */ + +public enum CardOnTopOfLibraryPredicate implements ObjectPlayerPredicate> { + instance; + + @Override + public boolean apply(ObjectPlayer input, Game game) { + Player player = game.getPlayer(input.getObject().getOwnerId()); + if (player == null) { + return false; + } + + Card topCard = player.getLibrary().getFromTop(game); + return topCard != null && topCard.getId().equals(input.getObject().getMainCard().getId()); + } +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/filter/predicate/other/CardTextPredicate.java b/Mage/src/main/java/mage/filter/predicate/card/CardTextPredicate.java similarity index 99% rename from Mage/src/main/java/mage/filter/predicate/other/CardTextPredicate.java rename to Mage/src/main/java/mage/filter/predicate/card/CardTextPredicate.java index 33b78bcb9334..e081b02ff50c 100644 --- a/Mage/src/main/java/mage/filter/predicate/other/CardTextPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/card/CardTextPredicate.java @@ -1,4 +1,4 @@ -package mage.filter.predicate.other; +package mage.filter.predicate.card; import mage.cards.AdventureCard; import mage.cards.Card; diff --git a/Mage/src/main/java/mage/filter/predicate/other/CastFromZonePredicate.java b/Mage/src/main/java/mage/filter/predicate/card/CastFromZonePredicate.java similarity index 76% rename from Mage/src/main/java/mage/filter/predicate/other/CastFromZonePredicate.java rename to Mage/src/main/java/mage/filter/predicate/card/CastFromZonePredicate.java index fa684cf8ac2f..5008ed595802 100644 --- a/Mage/src/main/java/mage/filter/predicate/other/CastFromZonePredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/card/CastFromZonePredicate.java @@ -1,9 +1,4 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.filter.predicate.other; +package mage.filter.predicate.card; import mage.cards.Card; import mage.constants.Zone; diff --git a/Mage/src/main/java/mage/filter/predicate/permanent/DefendingPlayerOwnsCardPredicate.java b/Mage/src/main/java/mage/filter/predicate/card/DefendingPlayerOwnsCardPredicate.java similarity index 93% rename from Mage/src/main/java/mage/filter/predicate/permanent/DefendingPlayerOwnsCardPredicate.java rename to Mage/src/main/java/mage/filter/predicate/card/DefendingPlayerOwnsCardPredicate.java index b16b59b3b815..b4eeb609e5b8 100644 --- a/Mage/src/main/java/mage/filter/predicate/permanent/DefendingPlayerOwnsCardPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/card/DefendingPlayerOwnsCardPredicate.java @@ -1,4 +1,4 @@ -package mage.filter.predicate.permanent; +package mage.filter.predicate.card; import mage.cards.Card; import mage.filter.predicate.ObjectPlayer; diff --git a/Mage/src/main/java/mage/filter/predicate/other/ExpansionSetPredicate.java b/Mage/src/main/java/mage/filter/predicate/card/ExpansionSetPredicate.java similarity index 93% rename from Mage/src/main/java/mage/filter/predicate/other/ExpansionSetPredicate.java rename to Mage/src/main/java/mage/filter/predicate/card/ExpansionSetPredicate.java index fbe4966eddad..6afe314eed23 100644 --- a/Mage/src/main/java/mage/filter/predicate/other/ExpansionSetPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/card/ExpansionSetPredicate.java @@ -1,5 +1,5 @@ -package mage.filter.predicate.other; +package mage.filter.predicate.card; import mage.cards.Card; import mage.filter.predicate.Predicate; diff --git a/Mage/src/main/java/mage/filter/predicate/other/FaceDownCastablePredicate.java b/Mage/src/main/java/mage/filter/predicate/card/FaceDownCastablePredicate.java similarity index 93% rename from Mage/src/main/java/mage/filter/predicate/other/FaceDownCastablePredicate.java rename to Mage/src/main/java/mage/filter/predicate/card/FaceDownCastablePredicate.java index e7d3d55f5b37..61f7fee66057 100644 --- a/Mage/src/main/java/mage/filter/predicate/other/FaceDownCastablePredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/card/FaceDownCastablePredicate.java @@ -1,4 +1,4 @@ -package mage.filter.predicate.other; +package mage.filter.predicate.card; import mage.abilities.keyword.MorphAbility; import mage.cards.Card; diff --git a/Mage/src/main/java/mage/filter/predicate/other/FaceDownPredicate.java b/Mage/src/main/java/mage/filter/predicate/card/FaceDownPredicate.java similarity index 91% rename from Mage/src/main/java/mage/filter/predicate/other/FaceDownPredicate.java rename to Mage/src/main/java/mage/filter/predicate/card/FaceDownPredicate.java index 47caf2325807..69ae289397eb 100644 --- a/Mage/src/main/java/mage/filter/predicate/other/FaceDownPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/card/FaceDownPredicate.java @@ -1,4 +1,4 @@ -package mage.filter.predicate.other; +package mage.filter.predicate.card; import mage.cards.Card; import mage.filter.predicate.Predicate; diff --git a/Mage/src/main/java/mage/filter/predicate/other/OwnerIdPredicate.java b/Mage/src/main/java/mage/filter/predicate/card/OwnerIdPredicate.java similarity index 93% rename from Mage/src/main/java/mage/filter/predicate/other/OwnerIdPredicate.java rename to Mage/src/main/java/mage/filter/predicate/card/OwnerIdPredicate.java index 15c2a46491c2..dce85dc3e4de 100644 --- a/Mage/src/main/java/mage/filter/predicate/other/OwnerIdPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/card/OwnerIdPredicate.java @@ -1,5 +1,5 @@ -package mage.filter.predicate.other; +package mage.filter.predicate.card; import java.util.UUID; import mage.cards.Card; diff --git a/Mage/src/main/java/mage/filter/predicate/card/PutIntoGraveFromBattlefieldThisTurnPredicate.java b/Mage/src/main/java/mage/filter/predicate/card/PutIntoGraveFromBattlefieldThisTurnPredicate.java new file mode 100644 index 000000000000..c0be0f81fc94 --- /dev/null +++ b/Mage/src/main/java/mage/filter/predicate/card/PutIntoGraveFromBattlefieldThisTurnPredicate.java @@ -0,0 +1,19 @@ +package mage.filter.predicate.card; + +import mage.cards.Card; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.watchers.common.CardsPutIntoGraveyardWatcher; + +/** + * @author TheElk801 + */ +public enum PutIntoGraveFromBattlefieldThisTurnPredicate implements Predicate { + instance; + + @Override + public boolean apply(Card input, Game game) { + CardsPutIntoGraveyardWatcher watcher = game.getState().getWatcher(CardsPutIntoGraveyardWatcher.class); + return watcher != null && watcher.checkCardFromBattlefield(input, game); + } +} diff --git a/Mage/src/main/java/mage/filter/predicate/permanent/AnotherPredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/AnotherPredicate.java similarity index 92% rename from Mage/src/main/java/mage/filter/predicate/permanent/AnotherPredicate.java rename to Mage/src/main/java/mage/filter/predicate/mageobject/AnotherPredicate.java index b0f42114f273..1c104eb326e1 100644 --- a/Mage/src/main/java/mage/filter/predicate/permanent/AnotherPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/AnotherPredicate.java @@ -1,5 +1,5 @@ -package mage.filter.predicate.permanent; +package mage.filter.predicate.mageobject; import mage.MageObject; import mage.filter.predicate.ObjectSourcePlayer; diff --git a/Mage/src/main/java/mage/filter/predicate/permanent/CommanderPredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/CommanderPredicate.java similarity index 92% rename from Mage/src/main/java/mage/filter/predicate/permanent/CommanderPredicate.java rename to Mage/src/main/java/mage/filter/predicate/mageobject/CommanderPredicate.java index dc4dd234bcb6..2608bcecc8b2 100644 --- a/Mage/src/main/java/mage/filter/predicate/permanent/CommanderPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/CommanderPredicate.java @@ -1,4 +1,4 @@ -package mage.filter.predicate.permanent; +package mage.filter.predicate.mageobject; import mage.MageObject; import mage.filter.predicate.Predicate; diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/HistoricPredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/HistoricPredicate.java index 39d0a7c15f86..9a274ae54a0e 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/HistoricPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/HistoricPredicate.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.filter.predicate.mageobject; import mage.MageObject; diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/MageObjectReferencePredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/MageObjectReferencePredicate.java index a0c1fe391ad2..36c6d7621031 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/MageObjectReferencePredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/MageObjectReferencePredicate.java @@ -1,16 +1,17 @@ - package mage.filter.predicate.mageobject; +import mage.MageItem; import mage.MageObject; import mage.MageObjectReference; import mage.filter.predicate.Predicate; import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; /** - * * @author TheElk801 */ -public class MageObjectReferencePredicate implements Predicate { +public class MageObjectReferencePredicate implements Predicate { private final MageObjectReference mor; @@ -19,8 +20,23 @@ public MageObjectReferencePredicate(MageObjectReference mor) { } @Override - public boolean apply(MageObject input, Game game) { - return mor.refersTo(input, game); + public boolean apply(MageItem input, Game game) { + if (input instanceof Player) { + return mor.getSourceId().equals(input.getId()); + } + return input instanceof MageObject && mor.refersTo((MageObject) input, game); + } + + public String getName(Game game) { + Permanent permanent = mor.getPermanent(game); + if (permanent != null) { + return permanent.getIdName(); + } + Player player = game.getPlayer(mor.getSourceId()); + if (player != null) { + return player.getName(); + } + return null; } @Override diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/ConvertedManaCostParityPredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/ManaValueParityPredicate.java similarity index 60% rename from Mage/src/main/java/mage/filter/predicate/mageobject/ConvertedManaCostParityPredicate.java rename to Mage/src/main/java/mage/filter/predicate/mageobject/ManaValueParityPredicate.java index 273f01e087a6..d26e3edd742f 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/ConvertedManaCostParityPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/ManaValueParityPredicate.java @@ -7,21 +7,21 @@ /** * @author TheElk801 */ -public enum ConvertedManaCostParityPredicate implements Predicate { +public enum ManaValueParityPredicate implements Predicate { EVEN(0), ODD(1); private final int parity; - ConvertedManaCostParityPredicate(int parity) { + ManaValueParityPredicate(int parity) { this.parity = parity; } @Override public boolean apply(MageObject input, Game game) { - return input.getConvertedManaCost() % 2 == parity; + return input.getManaValue() % 2 == parity; } @Override public String toString() { - return "ConvertedManaCostParity" + super.toString(); + return "ManaValueParity" + super.toString(); }} diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/ConvertedManaCostPredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/ManaValuePredicate.java similarity index 57% rename from Mage/src/main/java/mage/filter/predicate/mageobject/ConvertedManaCostPredicate.java rename to Mage/src/main/java/mage/filter/predicate/mageobject/ManaValuePredicate.java index e199ea9c18fc..d1114444da30 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/ConvertedManaCostPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/ManaValuePredicate.java @@ -9,19 +9,19 @@ * * @author North */ -public class ConvertedManaCostPredicate extends IntComparePredicate { +public class ManaValuePredicate extends IntComparePredicate { - public ConvertedManaCostPredicate(ComparisonType type, int value) { + public ManaValuePredicate(ComparisonType type, int value) { super(type, value); } @Override protected int getInputValue(MageObject input) { - return input.getConvertedManaCost(); + return input.getManaValue(); } @Override public String toString() { - return "ConvertedManaCost" + super.toString(); + return "ManaValue" + super.toString(); } } diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/NoAbilityPredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/NoAbilityPredicate.java new file mode 100644 index 000000000000..fe450dc5a35d --- /dev/null +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/NoAbilityPredicate.java @@ -0,0 +1,69 @@ +package mage.filter.predicate.mageobject; + +import mage.MageObject; +import mage.abilities.Abilities; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.keyword.special.JohanVigilanceAbility; +import mage.cards.Card; +import mage.filter.predicate.Predicate; +import mage.game.Game; + +import java.util.Objects; + +/** + * @author anonymous + */ +public enum NoAbilityPredicate implements Predicate { + instance; + + // Muraganda Petroglyphs gives a bonus only to creatures that have no rules text at all. This includes true vanilla + // creatures (such as Grizzly Bears), face-down creatures, many tokens, and creatures that have lost their abilities + // (due to Ovinize, for example). Any ability of any kind, whether or not the ability functions in the on the + // battlefield zone, including things like “Cycling {2}” means the creature doesn’t get the bonus. + // (2007-05-01) + + @Override + public boolean apply(MageObject input, Game game) { + boolean isFaceDown = false; + Abilities abilities; + if (input instanceof Card) { + abilities = ((Card) input).getAbilities(game); + isFaceDown = ((Card) input).isFaceDown(game); + } else { + abilities = input.getAbilities(); + } + if (isFaceDown) { + // Some Auras and Equipment grant abilities to creatures, meaning the affected creature would no longer + // get the +2/+2 bonus. For example, Flight grants flying to the enchanted creature. Other Auras and + // Equipment do not, meaning the affected creature would continue to get the +2/+2 bonus. For example, + // Dehydration states something now true about the enchanted creature, but doesn’t give it any abilities. + // Auras and Equipment that grant abilities will use the words “gains” or “has,” and they’ll list a keyword + // ability or an ability in quotation marks. + // (2007-05-01) + + for (Ability ability : abilities) { + if (ability.getWorksFaceDown()) { + // inner face down abilities like turn up and becomes creature + continue; + } + if (!Objects.equals(ability.getClass(), SpellAbility.class) && !ability.getClass().equals(JohanVigilanceAbility.class)) { + return false; + } + } + return true; + } + + for (Ability ability : abilities) { + if (!Objects.equals(ability.getClass(), SpellAbility.class) && !ability.getClass().equals(JohanVigilanceAbility.class)) { + return false; + } + } + return true; + } + + @Override + public String toString() { + return "with no abilities"; + } +} diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/SharesColorPredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/SharesColorPredicate.java index d42c44e12682..138aade01148 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/SharesColorPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/SharesColorPredicate.java @@ -1,9 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - package mage.filter.predicate.mageobject; import mage.MageObject; diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/SharesColorWithSourcePredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/SharesColorWithSourcePredicate.java index a73fb55feb1a..56f38b0d6221 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/SharesColorWithSourcePredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/SharesColorWithSourcePredicate.java @@ -1,9 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - package mage.filter.predicate.mageobject; import mage.MageObject; diff --git a/Mage/src/main/java/mage/filter/predicate/permanent/SharesCreatureTypePredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/SharesCreatureTypePredicate.java similarity index 92% rename from Mage/src/main/java/mage/filter/predicate/permanent/SharesCreatureTypePredicate.java rename to Mage/src/main/java/mage/filter/predicate/mageobject/SharesCreatureTypePredicate.java index 59b22fa7434a..636bf584d244 100644 --- a/Mage/src/main/java/mage/filter/predicate/permanent/SharesCreatureTypePredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/SharesCreatureTypePredicate.java @@ -1,4 +1,4 @@ -package mage.filter.predicate.permanent; +package mage.filter.predicate.mageobject; import mage.MageObject; import mage.filter.predicate.Predicate; diff --git a/Mage/src/main/java/mage/filter/predicate/other/TargetsOnlyOnePlayerPredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/TargetsOnlyOnePlayerPredicate.java similarity index 85% rename from Mage/src/main/java/mage/filter/predicate/other/TargetsOnlyOnePlayerPredicate.java rename to Mage/src/main/java/mage/filter/predicate/mageobject/TargetsOnlyOnePlayerPredicate.java index 7d5a6820d229..5510e927ea2b 100644 --- a/Mage/src/main/java/mage/filter/predicate/other/TargetsOnlyOnePlayerPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/TargetsOnlyOnePlayerPredicate.java @@ -1,9 +1,4 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.filter.predicate.other; +package mage.filter.predicate.mageobject; import java.util.UUID; import mage.MageObject; diff --git a/Mage/src/main/java/mage/filter/predicate/other/TargetsPermanentPredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/TargetsPermanentPredicate.java similarity index 90% rename from Mage/src/main/java/mage/filter/predicate/other/TargetsPermanentPredicate.java rename to Mage/src/main/java/mage/filter/predicate/mageobject/TargetsPermanentPredicate.java index a78546f22e02..fd16aea94be2 100644 --- a/Mage/src/main/java/mage/filter/predicate/other/TargetsPermanentPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/TargetsPermanentPredicate.java @@ -1,4 +1,4 @@ -package mage.filter.predicate.other; +package mage.filter.predicate.mageobject; import mage.MageObject; import mage.abilities.Mode; @@ -35,7 +35,7 @@ public boolean apply(ObjectSourcePlayer input, Game game) { } for (UUID targetId : target.getTargets()) { Permanent permanent = game.getPermanentOrLKIBattlefield(targetId); - if (permanent != null && targetFilter.match(permanent, input.getSourceId(), input.getPlayerId(), game)) { + if (targetFilter.match(permanent, input.getSourceId(), input.getPlayerId(), game)) { return true; } } diff --git a/Mage/src/main/java/mage/filter/predicate/other/TargetsPlayerPredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/TargetsPlayerPredicate.java similarity index 85% rename from Mage/src/main/java/mage/filter/predicate/other/TargetsPlayerPredicate.java rename to Mage/src/main/java/mage/filter/predicate/mageobject/TargetsPlayerPredicate.java index 45abc29cf3f1..e7bf4fe8a186 100644 --- a/Mage/src/main/java/mage/filter/predicate/other/TargetsPlayerPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/TargetsPlayerPredicate.java @@ -1,9 +1,4 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.filter.predicate.other; +package mage.filter.predicate.mageobject; import java.util.UUID; import mage.MageObject; diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/TextPartSubtypePredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/TextPartSubtypePredicate.java index a00c4166347f..ecddb80214d4 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/TextPartSubtypePredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/TextPartSubtypePredicate.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.filter.predicate.mageobject; import mage.MageObject; diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/VariableManaCostPredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/VariableManaCostPredicate.java index ef492472a449..3b159a887d47 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/VariableManaCostPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/VariableManaCostPredicate.java @@ -1,4 +1,3 @@ - package mage.filter.predicate.mageobject; import mage.MageObject; @@ -11,12 +10,12 @@ * * @author LevelX2 */ -public class VariableManaCostPredicate implements Predicate { +public enum VariableManaCostPredicate implements Predicate { + instance; @Override public boolean apply(MageObject input, Game game) { - return input.getManaCost().stream().anyMatch(manaCost -> manaCost instanceof VariableManaCost); - + return input.getManaCost().stream().anyMatch(VariableManaCost.class::isInstance); } @Override diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/AnotherTargetPredicate.java b/Mage/src/main/java/mage/filter/predicate/other/AnotherTargetPredicate.java similarity index 98% rename from Mage/src/main/java/mage/filter/predicate/mageobject/AnotherTargetPredicate.java rename to Mage/src/main/java/mage/filter/predicate/other/AnotherTargetPredicate.java index ee93a32a909a..5383ed5731e1 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/AnotherTargetPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/other/AnotherTargetPredicate.java @@ -1,5 +1,5 @@ -package mage.filter.predicate.mageobject; +package mage.filter.predicate.other; import java.util.UUID; import mage.MageItem; diff --git a/Mage/src/main/java/mage/filter/predicate/ability/ArtifactSourcePredicate.java b/Mage/src/main/java/mage/filter/predicate/other/ArtifactSourcePredicate.java similarity index 94% rename from Mage/src/main/java/mage/filter/predicate/ability/ArtifactSourcePredicate.java rename to Mage/src/main/java/mage/filter/predicate/other/ArtifactSourcePredicate.java index e5deb8194e2b..f9b91af2036f 100644 --- a/Mage/src/main/java/mage/filter/predicate/ability/ArtifactSourcePredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/other/ArtifactSourcePredicate.java @@ -1,5 +1,5 @@ -package mage.filter.predicate.ability; +package mage.filter.predicate.other; import mage.filter.predicate.Predicate; import mage.game.Game; diff --git a/Mage/src/main/java/mage/filter/predicate/permanent/DamagedPlayerThisTurnPredicate.java b/Mage/src/main/java/mage/filter/predicate/other/DamagedPlayerThisTurnPredicate.java similarity index 98% rename from Mage/src/main/java/mage/filter/predicate/permanent/DamagedPlayerThisTurnPredicate.java rename to Mage/src/main/java/mage/filter/predicate/other/DamagedPlayerThisTurnPredicate.java index 3fb4305a6b59..c45dc5815c3f 100644 --- a/Mage/src/main/java/mage/filter/predicate/permanent/DamagedPlayerThisTurnPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/other/DamagedPlayerThisTurnPredicate.java @@ -1,4 +1,4 @@ -package mage.filter.predicate.permanent; +package mage.filter.predicate.other; import mage.constants.TargetController; import mage.filter.predicate.ObjectPlayer; diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/FromSetPredicate.java b/Mage/src/main/java/mage/filter/predicate/other/FromSetPredicate.java similarity index 91% rename from Mage/src/main/java/mage/filter/predicate/mageobject/FromSetPredicate.java rename to Mage/src/main/java/mage/filter/predicate/other/FromSetPredicate.java index 0acf3d1faa52..1393761af93d 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/FromSetPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/other/FromSetPredicate.java @@ -1,5 +1,5 @@ -package mage.filter.predicate.mageobject; +package mage.filter.predicate.other; import java.util.Set; import java.util.UUID; diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/NumberOfTargetsPredicate.java b/Mage/src/main/java/mage/filter/predicate/other/NumberOfTargetsPredicate.java similarity index 96% rename from Mage/src/main/java/mage/filter/predicate/mageobject/NumberOfTargetsPredicate.java rename to Mage/src/main/java/mage/filter/predicate/other/NumberOfTargetsPredicate.java index 97cfe4d37ffd..8236bb12852f 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/NumberOfTargetsPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/other/NumberOfTargetsPredicate.java @@ -1,4 +1,4 @@ -package mage.filter.predicate.mageobject; +package mage.filter.predicate.other; import java.util.UUID; import mage.abilities.Mode; diff --git a/Mage/src/main/java/mage/filter/predicate/other/PermanentCanTargetPermanentId.java b/Mage/src/main/java/mage/filter/predicate/other/PermanentCanTargetPermanentId.java deleted file mode 100644 index 46b073d965a7..000000000000 --- a/Mage/src/main/java/mage/filter/predicate/other/PermanentCanTargetPermanentId.java +++ /dev/null @@ -1,36 +0,0 @@ - -package mage.filter.predicate.other; - -import java.util.UUID; -import mage.filter.predicate.Predicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.target.Target; - -/** - * - * @author LevelIX - */ -public class PermanentCanTargetPermanentId implements Predicate { - - private final UUID toBeCheckedPermanentId; - - public PermanentCanTargetPermanentId(UUID toBeCheckedPermanentId) { - this.toBeCheckedPermanentId = toBeCheckedPermanentId; - } - - @Override - public boolean apply(Permanent input, Game game) { - for (Target target : input.getSpellAbility().getTargets()) { - if (target.canTarget(toBeCheckedPermanentId, input.getSpellAbility(), game)) { - return true; - } - } - return false; - } - - @Override - public String toString() { - return "PermanentCanTargetPermanentId(" + toBeCheckedPermanentId + ')'; - } -} \ No newline at end of file diff --git a/Mage/src/main/java/mage/filter/predicate/other/SpellZonePredicate.java b/Mage/src/main/java/mage/filter/predicate/other/SpellZonePredicate.java index b9b85b26725f..339b3b44d8aa 100644 --- a/Mage/src/main/java/mage/filter/predicate/other/SpellZonePredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/other/SpellZonePredicate.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.filter.predicate.other; import mage.constants.Zone; diff --git a/Mage/src/main/java/mage/filter/predicate/permanent/AnotherEnchantedPredicate.java b/Mage/src/main/java/mage/filter/predicate/permanent/AnotherEnchantedPredicate.java index 04d3fa4e6f8b..75d946dde9f5 100644 --- a/Mage/src/main/java/mage/filter/predicate/permanent/AnotherEnchantedPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/permanent/AnotherEnchantedPredicate.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.filter.predicate.permanent; import mage.filter.predicate.ObjectSourcePlayer; diff --git a/Mage/src/main/java/mage/filter/predicate/permanent/AttachedToPredicate.java b/Mage/src/main/java/mage/filter/predicate/permanent/AttachedToPredicate.java index 9f287b3a9301..bc12b7b00fd5 100644 --- a/Mage/src/main/java/mage/filter/predicate/permanent/AttachedToPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/permanent/AttachedToPredicate.java @@ -22,7 +22,8 @@ public AttachedToPredicate(FilterPermanent filter) { @Override public boolean apply(Permanent input, Game game) { UUID attachedTo = input.getAttachedTo(); - return attachedTo != null && filter.match(game.getPermanent(attachedTo), game); + Permanent permanent = game.getPermanent(attachedTo); + return filter.match(permanent, game); } @Override diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/AttachmentAttachedToCardTypePredicate.java b/Mage/src/main/java/mage/filter/predicate/permanent/AttachmentAttachedToCardTypePredicate.java similarity index 95% rename from Mage/src/main/java/mage/filter/predicate/mageobject/AttachmentAttachedToCardTypePredicate.java rename to Mage/src/main/java/mage/filter/predicate/permanent/AttachmentAttachedToCardTypePredicate.java index 943a49d467f9..fdafeffdaf64 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/AttachmentAttachedToCardTypePredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/permanent/AttachmentAttachedToCardTypePredicate.java @@ -1,4 +1,4 @@ -package mage.filter.predicate.mageobject; +package mage.filter.predicate.permanent; import mage.constants.CardType; import mage.filter.predicate.Predicate; diff --git a/Mage/src/main/java/mage/filter/predicate/other/AuraPermanentCanAttachToPermanentId.java b/Mage/src/main/java/mage/filter/predicate/permanent/AuraPermanentCanAttachToPermanentId.java similarity index 96% rename from Mage/src/main/java/mage/filter/predicate/other/AuraPermanentCanAttachToPermanentId.java rename to Mage/src/main/java/mage/filter/predicate/permanent/AuraPermanentCanAttachToPermanentId.java index a52633c2b560..3f0c69acebf8 100644 --- a/Mage/src/main/java/mage/filter/predicate/other/AuraPermanentCanAttachToPermanentId.java +++ b/Mage/src/main/java/mage/filter/predicate/permanent/AuraPermanentCanAttachToPermanentId.java @@ -1,5 +1,5 @@ -package mage.filter.predicate.other; +package mage.filter.predicate.permanent; import java.util.UUID; import mage.filter.Filter; diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/EnchantmentOrEnchantedPredicate.java b/Mage/src/main/java/mage/filter/predicate/permanent/EnchantmentOrEnchantedPredicate.java similarity index 95% rename from Mage/src/main/java/mage/filter/predicate/mageobject/EnchantmentOrEnchantedPredicate.java rename to Mage/src/main/java/mage/filter/predicate/permanent/EnchantmentOrEnchantedPredicate.java index ab282fc21a13..a37ee68d4dce 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/EnchantmentOrEnchantedPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/permanent/EnchantmentOrEnchantedPredicate.java @@ -1,4 +1,4 @@ -package mage.filter.predicate.mageobject; +package mage.filter.predicate.permanent; import mage.constants.SubType; import mage.filter.predicate.Predicate; diff --git a/Mage/src/main/java/mage/filter/predicate/permanent/EquippedPredicate.java b/Mage/src/main/java/mage/filter/predicate/permanent/EquippedPredicate.java index c92c4145943f..f460a5a75913 100644 --- a/Mage/src/main/java/mage/filter/predicate/permanent/EquippedPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/permanent/EquippedPredicate.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.filter.predicate.permanent; import mage.constants.SubType; diff --git a/Mage/src/main/java/mage/filter/predicate/permanent/SummoningSicknessPredicate.java b/Mage/src/main/java/mage/filter/predicate/permanent/SummoningSicknessPredicate.java index aa8c528f8085..d5404fd97fbd 100644 --- a/Mage/src/main/java/mage/filter/predicate/permanent/SummoningSicknessPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/permanent/SummoningSicknessPredicate.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.filter.predicate.permanent; import mage.filter.predicate.Predicate; diff --git a/Mage/src/main/java/mage/game/CardState.java b/Mage/src/main/java/mage/game/CardState.java index ad896638850c..8302c100a17c 100644 --- a/Mage/src/main/java/mage/game/CardState.java +++ b/Mage/src/main/java/mage/game/CardState.java @@ -1,11 +1,16 @@ package mage.game; import java.io.Serializable; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; + import mage.abilities.Abilities; import mage.abilities.AbilitiesImpl; import mage.abilities.Ability; +import mage.counters.Counter; import mage.counters.Counters; /** @@ -127,4 +132,30 @@ public void setMelded(boolean melded) { this.melded = melded; } + @Override + public String toString() { + List info = new ArrayList<>(); + + if (this.faceDown) { + info.add("face down"); + } + if (this.counters != null && !this.counters.isEmpty()) { + info.add("counters: " + this.counters.values().stream().mapToInt(Counter::getCount).sum()); + } + if (this.abilities != null && !this.abilities.isEmpty()) { + info.add("abilities: " + abilities.size()); + } + if (this.lostAllAbilities) { + info.add("lost all"); + } + if (this.melded) { + info.add("melded"); + } + + if (info.isEmpty()) { + return ""; + } else { + return String.join("; ", info); + } + } } diff --git a/Mage/src/main/java/mage/game/Exile.java b/Mage/src/main/java/mage/game/Exile.java index a8a4474e4025..0ffaf01f1ba6 100644 --- a/Mage/src/main/java/mage/game/Exile.java +++ b/Mage/src/main/java/mage/game/Exile.java @@ -120,4 +120,12 @@ public void cleanupEndOfTurnZones(Game game) { } } } + + @Override + public String toString() { + return "Cards: " + exileZones.values() + .stream() + .mapToInt(ExileZone::size) + .sum(); + } } diff --git a/Mage/src/main/java/mage/game/Game.java b/Mage/src/main/java/mage/game/Game.java index 9b132ee3de70..af4ad622b368 100644 --- a/Mage/src/main/java/mage/game/Game.java +++ b/Mage/src/main/java/mage/game/Game.java @@ -277,6 +277,8 @@ default boolean isOpponent(Player player, UUID playerToCheckId) { void fireGetAmountEvent(UUID playerId, String message, int min, int max); + void fireGetMultiAmountEvent(UUID playerId, List messages, int min, int max, Map options); + void fireChoosePileEvent(UUID playerId, String message, List pile1, List pile2); void fireInformEvent(String message); @@ -421,9 +423,7 @@ default boolean isOpponent(Player player, UUID playerToCheckId) { Card copyCard(Card cardToCopy, Ability source, UUID newController); - void addTriggeredAbility(TriggeredAbility ability); - - UUID addDelayedTriggeredAbility(DelayedTriggeredAbility delayedAbility); + void addTriggeredAbility(TriggeredAbility ability, GameEvent triggeringEvent); UUID addDelayedTriggeredAbility(DelayedTriggeredAbility delayedAbility, Ability source); diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index 96f8871693dd..0a9a6ea93305 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -74,6 +74,7 @@ import java.io.Serializable; import java.util.*; import java.util.Map.Entry; +import java.util.stream.Collectors; public abstract class GameImpl implements Game, Serializable { @@ -552,7 +553,7 @@ public Card getCard(UUID cardId) { // copied cards removes, but delayed triggered possible from it, see https://github.com/magefree/mage/issues/5437 // TODO: remove that workround after LKI rework, see GameState.copyCard if (card == null) { - card = (Card) state.getValue(GameState.COPIED_FROM_CARD_KEY + cardId.toString()); + card = (Card) state.getValue(GameState.COPIED_CARD_KEY + cardId.toString()); } return card; } @@ -1138,6 +1139,7 @@ public void initGameDefaultWatchers() { getState().addWatcher(new BlockedAttackerWatcher()); getState().addWatcher(new DamageDoneWatcher()); getState().addWatcher(new PlanarRollWatcher()); + getState().addWatcher(new AttackedThisTurnWatcher()); getState().addWatcher(new PlayersAttackedThisTurnWatcher()); getState().addWatcher(new CardsDrawnThisTurnWatcher()); } @@ -1416,11 +1418,11 @@ public void playPriority(UUID activePlayerId, boolean resuming) { + ex.getMessage()); } Player activePlayer = this.getPlayer(getActivePlayerId()); - if (activePlayer != null && !activePlayer.isTestMode()) { + if (activePlayer != null && !activePlayer.isTestsMode()) { errorContinueCounter++; continue; } else { - throw new MageException("Error in testclass"); + throw new MageException("Error in unit tests"); } } finally { setCheckPlayableState(false); @@ -1686,6 +1688,7 @@ public Permanent copyPermanent(Duration duration, Permanent copyFromPermanent, U } } } + // if it was no copy of copy take the target itself if (newBluePrint == null) { newBluePrint = copyFromPermanent.copy(); @@ -1732,16 +1735,17 @@ public Permanent copyPermanent(Duration duration, Permanent copyFromPermanent, U @Override public Card copyCard(Card cardToCopy, Ability source, UUID newController) { - return state.copyCard(cardToCopy, source, this); + return state.copyCard(cardToCopy, newController, this); } /** * For internal use only * * @param ability + * @param triggeringEvent */ @Override - public void addTriggeredAbility(TriggeredAbility ability) { + public void addTriggeredAbility(TriggeredAbility ability, GameEvent triggeringEvent) { if (ability.getControllerId() == null) { String sourceName = "no sourceId"; if (ability.getSourceId() != null) { @@ -1767,31 +1771,23 @@ public void addTriggeredAbility(TriggeredAbility ability) { if (newAbility.getSourceObjectZoneChangeCounter() == 0) { newAbility.setSourceObjectZoneChangeCounter(getState().getZoneChangeCounter(ability.getSourceId())); } + newAbility.setTriggerEvent(triggeringEvent); state.addTriggeredAbility(newAbility); } } @Override public UUID addDelayedTriggeredAbility(DelayedTriggeredAbility delayedAbility, Ability source) { - delayedAbility.setSourceId(source.getSourceId()); - delayedAbility.setControllerId(source.getControllerId()); + if (source != null) { + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + } // return addDelayedTriggeredAbility(delayedAbility); DelayedTriggeredAbility newAbility = delayedAbility.copy(); newAbility.newId(); - newAbility.setSourceObjectZoneChangeCounter(getState().getZoneChangeCounter(source.getSourceId())); - newAbility.initOnAdding(this); - // ability.init is called as the ability triggeres not now. - // If a FixedTarget pointer is already set from the effect setting up this delayed ability - // it has to be already initialized so it won't be overwitten as the ability triggers - getState().addDelayedTriggeredAbility(newAbility); - return newAbility.getId(); - } - - @Deprecated - @Override - public UUID addDelayedTriggeredAbility(DelayedTriggeredAbility delayedAbility) { - DelayedTriggeredAbility newAbility = delayedAbility.copy(); - newAbility.newId(); + if (source != null) { + newAbility.setSourceObjectZoneChangeCounter(getState().getZoneChangeCounter(source.getSourceId())); + } newAbility.initOnAdding(this); // ability.init is called as the ability triggeres not now. // If a FixedTarget pointer is already set from the effect setting up this delayed ability @@ -1945,48 +1941,67 @@ protected boolean checkStateBasedActions() { somethingHappened = true; } - // 704.5e If a copy of a spell is in a zone other than the stack, it ceases to exist. If a copy of a card is in any zone other than the stack or the battlefield, it ceases to exist. - // (Isochron Scepter) 12/1/2004: If you don't want to cast the copy, you can choose not to; the copy ceases to exist the next time state-based actions are checked. - Iterator copiedCards = this.getState().getCopiedCards().iterator(); - while (copiedCards.hasNext()) { - Card card = copiedCards.next(); - if (card instanceof SplitCardHalf || card instanceof AdventureCardSpell || card instanceof ModalDoubleFacesCardHalf) { - continue; // only the main card is moves, not the halves (cause halfes is not copied - it uses original card -- TODO: need to fix (bugs with same card copy)? - } - Zone zone = state.getZone(card.getId()); - if (zone != Zone.BATTLEFIELD && zone != Zone.STACK) { - // TODO: remember LKI of copied cards here after LKI rework - switch (zone) { - case GRAVEYARD: - for (Player player : getPlayers().values()) { - if (player.getGraveyard().contains(card.getId())) { - player.getGraveyard().remove(card); - break; - } + // 704.5e + // If a copy of a spell is in a zone other than the stack, it ceases to exist. + // If a copy of a card is in any zone other than the stack or the battlefield, it ceases to exist. + // (Isochron Scepter) 12/1/2004: If you don't want to cast the copy, you can choose not to; the copy ceases + // to exist the next time state-based actions are checked. + // + // Copied cards can be stored in GameState.copiedCards or in game state value (until LKI rework) + Set allCopiedCards = new HashSet<>(); + allCopiedCards.addAll(this.getState().getCopiedCards()); + Map stateSavedCopiedCards = this.getState().getValues(GameState.COPIED_CARD_KEY); + allCopiedCards.addAll(stateSavedCopiedCards.values() + .stream() + .map(object -> (Card) object) + .filter(Objects::nonNull) + .collect(Collectors.toList()) + ); + for (Card copiedCard : allCopiedCards) { + // 1. Zone must be checked from main card only cause mdf parts can have different zones + // (one side on battlefield, another side on outside) + // 2. Copied card creates in OUTSIDE zone and put to stack manually in the same code, + // so no SBA calls before real zone change (you will see here only unused cards like Isochron Scepter) + // (Isochron Scepter) 12/1/2004: If you don't want to cast the copy, you can choose not to; the copy ceases + // to exist the next time state-based actions are checked. + Zone zone = state.getZone(copiedCard.getMainCard().getId()); + if (zone == Zone.BATTLEFIELD || zone == Zone.STACK) { + continue; + } + // TODO: remember LKI of copied cards here after LKI rework + switch (zone) { + case GRAVEYARD: + for (Player player : getPlayers().values()) { + if (player.getGraveyard().contains(copiedCard.getId())) { + player.getGraveyard().remove(copiedCard); + break; } - break; - case HAND: - for (Player player : getPlayers().values()) { - if (player.getHand().contains(card.getId())) { - player.getHand().remove(card); - break; - } + } + break; + case HAND: + for (Player player : getPlayers().values()) { + if (player.getHand().contains(copiedCard.getId())) { + player.getHand().remove(copiedCard); + break; } - break; - case LIBRARY: - for (Player player : getPlayers().values()) { - if (player.getLibrary().getCard(card.getId(), this) != null) { - player.getLibrary().remove(card.getId(), this); - break; - } + } + break; + case LIBRARY: + for (Player player : getPlayers().values()) { + if (player.getLibrary().getCard(copiedCard.getId(), this) != null) { + player.getLibrary().remove(copiedCard.getId(), this); + break; } - break; - case EXILED: - getExile().removeCard(card, this); - break; - } - copiedCards.remove(); + } + break; + case EXILED: + getExile().removeCard(copiedCard, this); + break; } + + // remove copied card info + this.getState().getCopiedCards().remove(copiedCard); + this.getState().removeValue(GameState.COPIED_CARD_KEY + copiedCard.getId().toString()); } List legendary = new ArrayList<>(); @@ -2491,6 +2506,14 @@ public void fireGetAmountEvent(UUID playerId, String message, int min, int max) playerQueryEventSource.amount(playerId, message, min, max); } + @Override + public void fireGetMultiAmountEvent(UUID playerId, List messages, int min, int max, Map options) { + if (simulation) { + return; + } + playerQueryEventSource.multiAmount(playerId, messages, min, max, options); + } + @Override public void fireChooseChoiceEvent(UUID playerId, Choice choice) { if (simulation) { @@ -2952,7 +2975,7 @@ public MageObject getLastKnownInformation(UUID objectId, Zone zone, int zoneChan @Override public CardState getLastKnownInformationCard(UUID objectId, Zone zone) { - if (zone == Zone.GRAVEYARD) { + if (zone.isPublicZone()) { Map lkiCardStateMap = lkiCardState.get(zone); if (lkiCardStateMap != null) { CardState cardState = lkiCardStateMap.get(objectId); @@ -2983,39 +3006,25 @@ public void rememberLKI(UUID objectId, Zone zone, MageObject object) { if (object instanceof Permanent || object instanceof StackObject) { MageObject copy = object.copy(); - Map lkiMap = lki.get(zone); - if (lkiMap != null) { - lkiMap.put(objectId, copy); - } else { - Map newMap = new HashMap<>(); - newMap.put(objectId, copy); - lki.put(zone, newMap); - } + Map lkiMap = lki.computeIfAbsent(zone, k -> new HashMap<>()); + lkiMap.put(objectId, copy); + // remembers if a object was in a zone during the resolution of an effect // e.g. Wrath destroys all and you the question is is the replacement effect to apply because the source was also moved by the same effect // because it happens all at the same time the replacement effect has still to be applied Set idSet = shortLivingLKI.computeIfAbsent(zone, k -> new HashSet<>()); idSet.add(objectId); if (object instanceof Permanent) { - Map lkiExtendedMap = lkiExtended.get(objectId); - if (lkiExtendedMap != null) { - lkiExtendedMap.put(object.getZoneChangeCounter(this), copy); - } else { - lkiExtendedMap = new HashMap<>(); - lkiExtendedMap.put(object.getZoneChangeCounter(this), copy); - lkiExtended.put(objectId, lkiExtendedMap); - } + Map lkiExtendedMap = lkiExtended.computeIfAbsent(objectId, k -> new HashMap<>()); + lkiExtendedMap.put(object.getZoneChangeCounter(this), copy); } - } else if (Zone.GRAVEYARD.equals(zone)) { + } else if (zone.isPublicZone()) { // Remember card state in this public zone (mainly removed/gained abilities) - Map lkiMap = lkiCardState.get(zone); - if (lkiMap != null) { - lkiMap.put(objectId, getState().getCardState(objectId)); - } else { - Map newMap = new HashMap<>(); - newMap.put(objectId, getState().getCardState(objectId).copy()); - lkiCardState.put(zone, newMap); - } + // Must save all card parts (mdf, split) + CardUtil.getObjectParts(object).forEach(partId -> { + Map lkiMap = lkiCardState.computeIfAbsent(zone, k -> new HashMap<>()); + lkiMap.put(partId, getState().getCardState(partId).copy()); + }); } } @@ -3468,4 +3477,14 @@ public boolean isGameStopped() { return gameStopped; } + @Override + public String toString() { + Player activePayer = this.getPlayer(this.getActivePlayerId()); + StringBuilder sb = new StringBuilder() + .append(this.getGameType().toString()) + .append("; ").append(CardUtil.getTurnInfo(this)) + .append("; active: ").append((activePayer == null ? "none" : activePayer.getName())) + .append("; stack: ").append(this.getStack().toString()); + return sb.toString(); + } } diff --git a/Mage/src/main/java/mage/game/GameState.java b/Mage/src/main/java/mage/game/GameState.java index 85848a7825b5..8ff30e43cf3b 100644 --- a/Mage/src/main/java/mage/game/GameState.java +++ b/Mage/src/main/java/mage/game/GameState.java @@ -6,10 +6,7 @@ import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffects; import mage.abilities.effects.Effect; -import mage.cards.AdventureCard; -import mage.cards.Card; -import mage.cards.ModalDoubleFacesCard; -import mage.cards.SplitCard; +import mage.cards.*; import mage.constants.Zone; import mage.designations.Designation; import mage.filter.common.FilterCreaturePermanent; @@ -31,6 +28,7 @@ import mage.players.PlayerList; import mage.players.Players; import mage.target.Target; +import mage.util.CardUtil; import mage.util.Copyable; import mage.util.ThreadLocalStringBuilder; import mage.watchers.Watcher; @@ -56,7 +54,9 @@ public class GameState implements Serializable, Copyable { private static final Logger logger = Logger.getLogger(GameState.class); private static final ThreadLocalStringBuilder threadLocalBuilder = new ThreadLocalStringBuilder(1024); - public static final String COPIED_FROM_CARD_KEY = "CopiedFromCard"; + // save copied cards between game cycles (lki workaround) + // warning, do not use another keys with same starting text cause copy code search and clean all related values + public static final String COPIED_CARD_KEY = "CopiedCard"; private final Players players; private final PlayerList playerList; @@ -845,7 +845,12 @@ public boolean equals(Object obj) { } public void addCard(Card card) { - setZone(card.getId(), Zone.OUTSIDE); + // all new cards and tokens must enter from outside + addCard(card, Zone.OUTSIDE); + } + + private void addCard(Card card, Zone zone) { + setZone(card.getId(), zone); // add card specific abilities to game for (Ability ability : card.getInitAbilities()) { @@ -853,28 +858,6 @@ public void addCard(Card card) { } } - public void removeCopiedCard(Card card) { - if (copiedCards.containsKey(card.getId())) { - copiedCards.remove(card.getId()); - cardState.remove(card.getId()); - zones.remove(card.getId()); - zoneChangeCounter.remove(card.getId()); - } - // TODO Watchers? - // TODO Abilities? - if (card instanceof SplitCard) { - removeCopiedCard(((SplitCard) card).getLeftHalfCard()); - removeCopiedCard(((SplitCard) card).getRightHalfCard()); - } - if (card instanceof ModalDoubleFacesCard) { - removeCopiedCard(((ModalDoubleFacesCard) card).getLeftHalfCard()); - removeCopiedCard(((ModalDoubleFacesCard) card).getRightHalfCard()); - } - if (card instanceof AdventureCard) { - removeCopiedCard(((AdventureCard) card).getSpellCard()); - } - } - /** * Used for adding abilities that exist permanent on cards/permanents and * are not only gained for a certain time (e.g. until end of turn). @@ -1021,6 +1004,27 @@ public Object getValue(String valueId) { return values.get(valueId); } + /** + * Return values list starting with searching key. + *

+ * Usage example: if you want to find all saved values from related ability/effect + * + * @param startWithValue + * @return + */ + public Map getValues(String startWithValue) { + if (startWithValue == null || startWithValue.isEmpty()) { + throw new IllegalArgumentException("Can't use empty search value"); + } + Map res = new HashMap<>(); + for (Map.Entry entry : this.values.entrySet()) { + if (entry.getKey().startsWith(startWithValue)) { + res.put(entry.getKey(), entry.getValue()); + } + } + return res; + } + /** * Best only use immutable objects, otherwise the states/values of the * object may be changed by AI simulation or rollbacks, because the Value @@ -1035,6 +1039,15 @@ public void setValue(String valueId, Object value) { values.put(valueId, value); } + /** + * Remove saved value + * + * @param valueId + */ + public void removeValue(String valueId) { + values.remove(valueId); + } + /** * Other abilities are used to implement some special kind of continuous * effects that give abilities to non permanents. @@ -1081,7 +1094,8 @@ public void addOtherAbility(Card attachedTo, Ability ability) { * @param attachedTo * @param ability * @param copyAbility copies non MageSingleton abilities before adding to - * state + * state (allows to have multiple instances in one object, + * e.g. false param will simulate keyword/singletone) */ public void addOtherAbility(Card attachedTo, Ability ability, boolean copyAbility) { checkWrongDynamicAbilityUsage(attachedTo, ability); @@ -1090,7 +1104,10 @@ public void addOtherAbility(Card attachedTo, Ability ability, boolean copyAbilit if (ability instanceof MageSingleton || !copyAbility) { newAbility = ability; } else { + // must use new id, so you can add multiple instances of the same ability + // (example: gained Cascade from multiple Imoti, Celebrant of Bounty) newAbility = ability.copy(); + newAbility.newId(); } newAbility.setSourceId(attachedTo.getId()); newAbility.setControllerId(attachedTo.getOwnerId()); @@ -1247,47 +1264,92 @@ public Collection getCopiedCards() { return copiedCards.values(); } - public Card copyCard(Card cardToCopy, Ability source, Game game) { - // main card - Card copiedCard = cardToCopy.copy(); - copiedCard.assignNewId(); - copiedCard.setOwnerId(source.getControllerId()); - copiedCard.setCopy(true, cardToCopy); - copiedCards.put(copiedCard.getId(), copiedCard); - addCard(copiedCard); + /** + * Make full copy of the card and all of the card's parts and put to the game. + * + * @param mainCardToCopy + * @param newController + * @param game + * @return + */ + public Card copyCard(Card mainCardToCopy, UUID newController, Game game) { + // runtime check + if (!mainCardToCopy.getId().equals(mainCardToCopy.getMainCard().getId())) { + // copyCard allows for main card only, if you catch it then check your targeting code + throw new IllegalArgumentException("Wrong code usage. You can copy only main card."); + } + + // must copy all card's parts + // zcc and zone must be new cause zcc copy logic need card usage info here, but it haven't: + // * reason 1: copied land must be played (+1 zcc), but copied spell must be put on stack and cast (+2 zcc) + // * reason 2: copied card or spell can be used later as blueprint for real copies (see Epic ability) + List copiedParts = new ArrayList<>(); - // other faces + // main part (prepare must be called after other parts) + Card copiedCard = mainCardToCopy.copy(); + copiedParts.add(copiedCard); + + // other parts if (copiedCard instanceof SplitCard) { // left - Card leftCard = ((SplitCard) copiedCard).getLeftHalfCard(); // TODO: must be new ID (bugs with same card copy)? - copiedCards.put(leftCard.getId(), leftCard); - addCard(leftCard); + SplitCardHalf leftOriginal = ((SplitCard) copiedCard).getLeftHalfCard(); + SplitCardHalf leftCopied = leftOriginal.copy(); + prepareCardForCopy(leftOriginal, leftCopied, newController); + copiedParts.add(leftCopied); // right - Card rightCard = ((SplitCard) copiedCard).getRightHalfCard(); - copiedCards.put(rightCard.getId(), rightCard); - addCard(rightCard); + SplitCardHalf rightOriginal = ((SplitCard) copiedCard).getRightHalfCard(); + SplitCardHalf rightCopied = rightOriginal.copy(); + prepareCardForCopy(rightOriginal, rightCopied, newController); + copiedParts.add(rightCopied); + // sync parts + ((SplitCard) copiedCard).setParts(leftCopied, rightCopied); } else if (copiedCard instanceof ModalDoubleFacesCard) { // left - Card leftCard = ((ModalDoubleFacesCard) copiedCard).getLeftHalfCard(); // TODO: must be new ID (bugs with same card copy)? - copiedCards.put(leftCard.getId(), leftCard); - addCard(leftCard); + ModalDoubleFacesCardHalf leftOriginal = ((ModalDoubleFacesCard) copiedCard).getLeftHalfCard(); + ModalDoubleFacesCardHalf leftCopied = leftOriginal.copy(); + prepareCardForCopy(leftOriginal, leftCopied, newController); + copiedParts.add(leftCopied); // right - Card rightCard = ((ModalDoubleFacesCard) copiedCard).getRightHalfCard(); - copiedCards.put(rightCard.getId(), rightCard); - addCard(rightCard); + ModalDoubleFacesCardHalf rightOriginal = ((ModalDoubleFacesCard) copiedCard).getRightHalfCard(); + ModalDoubleFacesCardHalf rightCopied = rightOriginal.copy(); + prepareCardForCopy(rightOriginal, rightCopied, newController); + copiedParts.add(rightCopied); + // sync parts + ((ModalDoubleFacesCard) copiedCard).setParts(leftCopied, rightCopied); } else if (copiedCard instanceof AdventureCard) { - Card spellCard = ((AdventureCard) copiedCard).getSpellCard(); - copiedCards.put(spellCard.getId(), spellCard); - addCard(spellCard); + // right + AdventureCardSpell rightOriginal = ((AdventureCard) copiedCard).getSpellCard(); + AdventureCardSpell rightCopied = rightOriginal.copy(); + prepareCardForCopy(rightOriginal, rightCopied, newController); + copiedParts.add(rightCopied); + // sync parts + ((AdventureCard) copiedCard).setParts(rightCopied); } + // main part prepare (must be called after other parts cause it change ids for all) + prepareCardForCopy(mainCardToCopy, copiedCard, newController); + + // add all parts to the game + copiedParts.forEach(card -> { + copiedCards.put(card.getId(), card); + addCard(card); + }); + // copied cards removes from game after battlefield/stack leaves, so remember it here as workaround to fix freeze, see https://github.com/magefree/mage/issues/5437 // TODO: remove that workaround after LKI will be rewritten to support cross-steps/turns data transition and support copied cards - this.setValue(COPIED_FROM_CARD_KEY + copiedCard.getId(), cardToCopy.copy()); + copiedParts.forEach(card -> { + this.setValue(COPIED_CARD_KEY + card.getId(), card.copy()); + }); return copiedCard; } + private void prepareCardForCopy(Card originalCard, Card copiedCard, UUID newController) { + copiedCard.assignNewId(); + copiedCard.setOwnerId(newController); + copiedCard.setCopy(true, originalCard); + } + public int getNextPermanentOrderNumber() { return permanentOrderNumber++; } @@ -1323,4 +1385,9 @@ public void setManaBurn(boolean manaBurn) { public boolean isManaBurn() { return manaBurn; } + + @Override + public String toString() { + return CardUtil.getTurnInfo(this); + } } diff --git a/Mage/src/main/java/mage/game/combat/Combat.java b/Mage/src/main/java/mage/game/combat/Combat.java index 3784ff1c72a3..dc0abd48e7a7 100644 --- a/Mage/src/main/java/mage/game/combat/Combat.java +++ b/Mage/src/main/java/mage/game/combat/Combat.java @@ -1,8 +1,7 @@ package mage.game.combat; -import java.io.Serializable; -import java.util.*; import mage.MageObject; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.effects.RequirementEffect; import mage.abilities.effects.RestrictionEffect; @@ -23,11 +22,7 @@ import mage.filter.predicate.permanent.AttackingSameNotBandedPredicate; import mage.filter.predicate.permanent.PermanentIdPredicate; import mage.game.Game; -import mage.game.events.AttackerDeclaredEvent; -import mage.game.events.DeclareAttackerEvent; -import mage.game.events.DeclareBlockerEvent; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; +import mage.game.events.*; import mage.game.permanent.Permanent; import mage.players.Player; import mage.players.PlayerList; @@ -38,6 +33,9 @@ import mage.util.trace.TraceUtil; import org.apache.log4j.Logger; +import java.io.Serializable; +import java.util.*; + /** * @author BetaSteward_at_googlemail.com */ @@ -269,6 +267,7 @@ public void selectAttackers(Game game) { @SuppressWarnings("deprecation") public void resumeSelectAttackers(Game game) { + Map> morSetMap = new HashMap<>(); for (CombatGroup group : groups) { for (UUID attacker : group.getAttackers()) { if (attackersTappedByAttack.contains(attacker)) { @@ -282,10 +281,12 @@ public void resumeSelectAttackers(Game game) { // This can only be used to modify the event, the attack can't be replaced here game.replaceEvent(new AttackerDeclaredEvent(group.defenderId, attacker, attackingPlayerId)); game.addSimultaneousEvent(new AttackerDeclaredEvent(group.defenderId, attacker, attackingPlayerId)); + morSetMap.computeIfAbsent(group.defenderId, x -> new HashSet<>()).add(new MageObjectReference(attacker, game)); } } attackersTappedByAttack.clear(); + DefenderAttackedEvent.makeAddEvents(morSetMap, attackingPlayerId, game); game.addSimultaneousEvent(GameEvent.getEvent(GameEvent.EventType.DECLARED_ATTACKERS, attackingPlayerId, attackingPlayerId)); if (!game.isSimulation()) { Player player = game.getPlayer(attackingPlayerId); @@ -352,8 +353,8 @@ private void handleBanding(UUID creatureId, Game game) { if (game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_ATTACKERS, attackingPlayerId, attackingPlayerId)) || (!canBand && !canBandWithOther) || !player.chooseUse(Outcome.Benefit, - "Do you wish to " + (isBanded ? "band " + attacker.getLogName() - + " with another " : "form a band with " + attacker.getLogName() + " and an ") + (isBanded ? "Band " + attacker.getLogName() + + " with another " : "Form a band with " + attacker.getLogName() + " and an ") + "attacking creature?", null, game)) { break; } @@ -571,7 +572,7 @@ public void selectBlockers(Game game) { * Handle the blocker selection process * * @param blockController player that controls how to block, if null the - * defender is the controller + * defender is the controller * @param game */ public void selectBlockers(Player blockController, Ability source, Game game) { @@ -1388,7 +1389,7 @@ public void addBlockingGroup(UUID blockerId, UUID attackerId, UUID playerId, Gam * @param playerId * @param game * @param solveBanding check whether also add creatures banded with - * attackerId + * attackerId */ public void addBlockingGroup(UUID blockerId, UUID attackerId, UUID playerId, Game game, boolean solveBanding) { Permanent blocker = game.getPermanent(blockerId); diff --git a/Mage/src/main/java/mage/game/combat/CombatGroup.java b/Mage/src/main/java/mage/game/combat/CombatGroup.java index 4831dc23158c..831e2544d874 100644 --- a/Mage/src/main/java/mage/game/combat/CombatGroup.java +++ b/Mage/src/main/java/mage/game/combat/CombatGroup.java @@ -8,7 +8,6 @@ import mage.constants.AsThoughEffectType; import mage.constants.Outcome; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.events.BlockerDeclaredEvent; import mage.game.events.DeclareBlockerEvent; @@ -167,8 +166,7 @@ public void assignDamageToBlockers(boolean first, Game game) { } else { Player player = game.getPlayer(defenderAssignsCombatDamage(game) ? defendingPlayerId : attacker.getControllerId()); if ((attacker.getAbilities().containsKey(DamageAsThoughNotBlockedAbility.getInstance().getId()) && - player.chooseUse(Outcome.Damage, "Do you wish to assign damage for " - + attacker.getLogName() + " as though it weren't blocked?", null, game)) || + player.chooseUse(Outcome.Damage, "Have " + attacker.getLogName() + " assign damage as though it weren't blocked?", null, game)) || game.getContinuousEffects().asThough(attacker.getId(), AsThoughEffectType.DAMAGE_NOT_BLOCKED, null, attacker.getControllerId(), game) != null) { // for handling creatures like Thorn Elemental @@ -215,6 +213,12 @@ public void applyDamage(Game game) { permanent.applyDamage(game); } } + if (defenderIsPlaneswalker) { + Permanent permanent = game.getPermanent(defenderId); + if (permanent != null) { + permanent.applyDamage(game); + } + } } /** @@ -887,7 +891,7 @@ public boolean assignsDefendingPlayerAndOrDefendingCreaturesDividedDamage(Perman // 10/4/2004 If it is blocked but then all of its blockers are removed before combat damage is assigned, then it won't be able to deal combat damage and you won't be able to use its ability. // (same principle should apply if it's blocking and its blocked attacker is removed from combat) if (!((blocked && blockers.isEmpty() && isAttacking) || (attackers.isEmpty() && !isAttacking)) && canDamage(creature, first)) { - if (player.chooseUse(Outcome.Damage, "Do you wish to assign " + creature.getLogName() + "'s combat damage divided among defending player and/or any number of defending creatures?", null, game)) { + if (player.chooseUse(Outcome.Damage, "Have " + creature.getLogName() + " assign its combat damage divided among defending player and/or any number of defending creatures?", null, game)) { defendingPlayerAndOrDefendingCreaturesDividedDamage(creature, player, first, game, isAttacking); return true; } @@ -897,27 +901,6 @@ public boolean assignsDefendingPlayerAndOrDefendingCreaturesDividedDamage(Perman } private static int getLethalDamage(Permanent blocker, Permanent attacker, Game game) { - int lethalDamage; - if (attacker.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) { - lethalDamage = 1; - } else { - lethalDamage = getLethalDamage(blocker, game); - } - return lethalDamage; - } - - public static int getLethalDamage(Permanent damagedPermanent, Game game) { - List usePowerInsteadOfToughnessForDamageLethalityFilters = game.getState().getActivePowerInsteadOfToughnessForDamageLethalityFilters(); - /* - * for handling Zilortha, Strength Incarnate: - * 2020-04-17 - * Any time the game is checking whether damage is lethal or if a creature should be destroyed for having lethal damage marked on it, use the power of your creatures rather than their toughness to check the damage against. This includes being assigned trample damage, damage from Flame Spill, and so on. - */ - boolean usePowerInsteadOfToughnessForDamageLethality = usePowerInsteadOfToughnessForDamageLethalityFilters.stream() - .anyMatch(filter -> filter.match(damagedPermanent, game)); - int lethalDamageThreshold = usePowerInsteadOfToughnessForDamageLethality ? - // Zilortha, Strength Incarnate, 2020-04-17: A creature with 0 power isn’t destroyed unless it has at least 1 damage marked on it. - Math.max(damagedPermanent.getPower().getValue(), 1) : damagedPermanent.getToughness().getValue(); - return Math.max(lethalDamageThreshold - damagedPermanent.getDamage(), 0); + return blocker.getLethalDamage(attacker.getId(), game); } } diff --git a/Mage/src/main/java/mage/game/command/Commander.java b/Mage/src/main/java/mage/game/command/Commander.java index 1b783d1e80da..9dcb2af84f05 100644 --- a/Mage/src/main/java/mage/game/command/Commander.java +++ b/Mage/src/main/java/mage/game/command/Commander.java @@ -52,7 +52,9 @@ public Commander(Card card) { case MODAL_RIGHT: case ADVENTURE_SPELL: // can be used from command zone - abilities.add(new CastCommanderAbility(card, spellAbility)); + if (canUseAbilityFromCommandZone(spellAbility)) { + abilities.add(new CastCommanderAbility(card, spellAbility)); + } break; case FACE_DOWN_CREATURE: // dynamic added spell for alternative cost like cast as face down case SPLICE: // only from hand @@ -68,8 +70,10 @@ public Commander(Card card) { // replace play land with commander play land (to play from command zone) for (Ability ability : card.getAbilities()) { if (ability instanceof PlayLandAbility) { - Ability newAbility = new PlayLandAsCommanderAbility((PlayLandAbility) ability); - abilities.add(newAbility); + if (canUseAbilityFromCommandZone(ability)) { + Ability newAbility = new PlayLandAsCommanderAbility((PlayLandAbility) ability); + abilities.add(newAbility); + } } } @@ -81,11 +85,24 @@ public Commander(Card card) { } // all other abilities must be added to commander (example: triggers from command zone, alternative cost, etc) + // no changes to ability zone, so can add any Ability newAbility = ability.copy(); abilities.add(newAbility); } } + private boolean canUseAbilityFromCommandZone(Ability ability) { + // ability can be restricted by zone usage, so you must ignore it for commander (example: Escape or Jumpstart) + switch (ability.getZone()) { + case ALL: + case COMMAND: + case HAND: + return true; + default: + return false; + } + } + private Commander(final Commander commander) { this.sourceObject = commander.sourceObject; this.copy = commander.copy; @@ -232,8 +249,8 @@ public ManaCosts getManaCost() { } @Override - public int getConvertedManaCost() { - return sourceObject.getConvertedManaCost(); + public int getManaValue() { + return sourceObject.getManaValue(); } @Override diff --git a/Mage/src/main/java/mage/game/command/Emblem.java b/Mage/src/main/java/mage/game/command/Emblem.java index 2d42604bd9d3..2d1532a0b3ce 100644 --- a/Mage/src/main/java/mage/game/command/Emblem.java +++ b/Mage/src/main/java/mage/game/command/Emblem.java @@ -204,7 +204,7 @@ public ManaCosts getManaCost() { } @Override - public int getConvertedManaCost() { + public int getManaValue() { return 0; } diff --git a/Mage/src/main/java/mage/game/command/Plane.java b/Mage/src/main/java/mage/game/command/Plane.java index 98e75d83d7ff..cd2e4bf81650 100644 --- a/Mage/src/main/java/mage/game/command/Plane.java +++ b/Mage/src/main/java/mage/game/command/Plane.java @@ -213,7 +213,7 @@ public ManaCosts getManaCost() { } @Override - public int getConvertedManaCost() { + public int getManaValue() { return 0; } diff --git a/Mage/src/main/java/mage/game/command/emblems/AjaniSteadfastEmblem.java b/Mage/src/main/java/mage/game/command/emblems/AjaniSteadfastEmblem.java index ce2123de0984..fe662cca7bad 100644 --- a/Mage/src/main/java/mage/game/command/emblems/AjaniSteadfastEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/AjaniSteadfastEmblem.java @@ -58,9 +58,9 @@ public boolean applies(GameEvent event, Ability source, Game game) { return super.applies(event, source, game); } - if (event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER) { + if (event.getType() == GameEvent.EventType.DAMAGE_PERMANENT) { Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.isControlledBy(source.getControllerId())) { + if (permanent != null && permanent.isPlaneswalker() && permanent.isControlledBy(source.getControllerId())) { return super.applies(event, source, game); } } diff --git a/Mage/src/main/java/mage/game/command/emblems/ChandraTorchOfDefianceEmblem.java b/Mage/src/main/java/mage/game/command/emblems/ChandraTorchOfDefianceEmblem.java index 33a9673b992b..99be9a56d4a9 100644 --- a/Mage/src/main/java/mage/game/command/emblems/ChandraTorchOfDefianceEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/ChandraTorchOfDefianceEmblem.java @@ -7,6 +7,7 @@ import mage.abilities.effects.common.DamageTargetEffect; import mage.constants.Zone; import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import mage.game.command.Emblem; import mage.target.common.TargetAnyTarget; @@ -21,7 +22,7 @@ public ChandraTorchOfDefianceEmblem() { this.setName("Emblem Chandra"); Effect effect = new DamageTargetEffect(5); effect.setText("this emblem deals 5 damage to any target"); - Ability ability = new SpellCastControllerTriggeredAbility(Zone.COMMAND, effect, new FilterSpell("a spell"), false, false); + Ability ability = new SpellCastControllerTriggeredAbility(Zone.COMMAND, effect, StaticFilters.FILTER_SPELL_A, false, false); ability.addTarget(new TargetAnyTarget()); getAbilities().add(ability); } diff --git a/Mage/src/main/java/mage/game/command/emblems/DarettiScrapSavantEmblem.java b/Mage/src/main/java/mage/game/command/emblems/DarettiScrapSavantEmblem.java index 79e17f384afb..8e558b2e02b6 100644 --- a/Mage/src/main/java/mage/game/command/emblems/DarettiScrapSavantEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/DarettiScrapSavantEmblem.java @@ -56,8 +56,7 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getToZone() == Zone.GRAVEYARD - && zEvent.getFromZone() == Zone.BATTLEFIELD + if (zEvent.isDiesEvent() && zEvent.getTarget().isArtifact() && zEvent.getTarget().isOwnedBy(this.controllerId)) { this.getEffects().setTargetPointer(new FixedTarget(zEvent.getTargetId())); diff --git a/Mage/src/main/java/mage/game/command/emblems/GarrukUnleashedEmblem.java b/Mage/src/main/java/mage/game/command/emblems/GarrukUnleashedEmblem.java index 50c394c3153a..d994b275ef85 100644 --- a/Mage/src/main/java/mage/game/command/emblems/GarrukUnleashedEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/GarrukUnleashedEmblem.java @@ -15,7 +15,7 @@ public class GarrukUnleashedEmblem extends Emblem { public GarrukUnleashedEmblem() { this.setName("Emblem Garruk"); Effect effect = new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_CREATURE), false, true, Outcome.PutCreatureInPlay) - .setText("search your library for a creature card, put it onto the battlefield, then shuffle your library"); + .setText("search your library for a creature card, put it onto the battlefield, then shuffle"); this.getAbilities().add(new BeginningOfYourEndStepTriggeredAbility(Zone.COMMAND, effect, true)); } } diff --git a/Mage/src/main/java/mage/game/command/emblems/JaceTelepathUnboundEmblem.java b/Mage/src/main/java/mage/game/command/emblems/JaceTelepathUnboundEmblem.java index 34ab1500f1da..4e343d6898c9 100644 --- a/Mage/src/main/java/mage/game/command/emblems/JaceTelepathUnboundEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/JaceTelepathUnboundEmblem.java @@ -6,6 +6,7 @@ import mage.abilities.effects.common.MillCardsTargetEffect; import mage.constants.Zone; import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import mage.game.command.Emblem; import mage.target.common.TargetOpponent; @@ -19,7 +20,7 @@ public JaceTelepathUnboundEmblem() { this.setName("Emblem Jace"); Effect effect = new MillCardsTargetEffect(5); effect.setText("target opponent mills five cards"); - Ability ability = new SpellCastControllerTriggeredAbility(Zone.COMMAND, effect, new FilterSpell("a spell"), false, false); + Ability ability = new SpellCastControllerTriggeredAbility(Zone.COMMAND, effect, StaticFilters.FILTER_SPELL_A, false, false); ability.addTarget(new TargetOpponent()); getAbilities().add(ability); } diff --git a/Mage/src/main/java/mage/game/command/emblems/JayaBallardEmblem.java b/Mage/src/main/java/mage/game/command/emblems/JayaBallardEmblem.java index 41da08520cbf..d9a5e66a0738 100644 --- a/Mage/src/main/java/mage/game/command/emblems/JayaBallardEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/JayaBallardEmblem.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.game.command.emblems; import java.util.UUID; diff --git a/Mage/src/main/java/mage/game/command/emblems/LukkaWaywardBonderEmblem.java b/Mage/src/main/java/mage/game/command/emblems/LukkaWaywardBonderEmblem.java new file mode 100644 index 000000000000..7c0b4785a7f1 --- /dev/null +++ b/Mage/src/main/java/mage/game/command/emblems/LukkaWaywardBonderEmblem.java @@ -0,0 +1,65 @@ +package mage.game.command.emblems; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.command.Emblem; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetAnyTarget; + +/** + * @author TheElk801 + */ +public final class LukkaWaywardBonderEmblem extends Emblem { + + // −7: You get an emblem with "Whenever a creature enters the battlefield under your control, it deals damage equal to its power to any target." + public LukkaWaywardBonderEmblem() { + this.setName("Emblem Lukka"); + this.setExpansionSetCodeForImage("STX"); + Ability ability = new EntersBattlefieldControlledTriggeredAbility( + Zone.COMMAND, new LukkaWaywardBonderEmblemEffect(), + StaticFilters.FILTER_PERMANENT_CREATURE_A, false + ); + ability.addTarget(new TargetAnyTarget()); + this.getAbilities().add(ability); + } +} + +class LukkaWaywardBonderEmblemEffect extends OneShotEffect { + + LukkaWaywardBonderEmblemEffect() { + super(Outcome.Benefit); + staticText = "it deals damage equal to its power to any target"; + } + + private LukkaWaywardBonderEmblemEffect(final LukkaWaywardBonderEmblemEffect effect) { + super(effect); + } + + @Override + public LukkaWaywardBonderEmblemEffect copy() { + return new LukkaWaywardBonderEmblemEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = (Permanent) getValue("permanentEnteringBattlefield"); + if (permanent == null || permanent.getPower().getValue() < 1) { + return false; + } + Permanent targetPermanent = game.getPermanent(source.getFirstTarget()); + if (targetPermanent != null) { + return targetPermanent.damage(permanent.getPower().getValue(), permanent.getId(), source, game) > 0; + } + Player targetPlayer = game.getPlayer(source.getFirstTarget()); + if (targetPermanent != null) { + return targetPermanent.damage(permanent.getPower().getValue(), permanent.getId(), source, game) > 0; + } + return false; + } +} diff --git a/Mage/src/main/java/mage/game/command/emblems/MomirEmblem.java b/Mage/src/main/java/mage/game/command/emblems/MomirEmblem.java index fb07e4d0d890..62db3b11138b 100644 --- a/Mage/src/main/java/mage/game/command/emblems/MomirEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/MomirEmblem.java @@ -52,7 +52,7 @@ public MomirEffect() { public MomirEffect(MomirEffect effect) { super(effect); - staticText = "Create a token that's a copy of a creature card with converted mana cost X chosen at random"; + staticText = "Create a token that's a copy of a creature card with mana value X chosen at random"; } @Override @@ -70,10 +70,10 @@ public boolean apply(Game game, Ability source) { return true; } // should this be random across card names - CardCriteria criteria = new CardCriteria().types(CardType.CREATURE).convertedManaCost(value); + CardCriteria criteria = new CardCriteria().types(CardType.CREATURE).manaValue(value); List options = CardRepository.instance.findCards(criteria); if (options == null || options.isEmpty()) { - game.informPlayers("No random creature card with converted mana cost of " + value + " was found."); + game.informPlayers("No random creature card with mana value of " + value + " was found."); return false; } diff --git a/Mage/src/main/java/mage/game/command/emblems/NissaWhoShakesTheWorldEmblem.java b/Mage/src/main/java/mage/game/command/emblems/NissaWhoShakesTheWorldEmblem.java index e00b2a248044..163298dd416d 100644 --- a/Mage/src/main/java/mage/game/command/emblems/NissaWhoShakesTheWorldEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/NissaWhoShakesTheWorldEmblem.java @@ -20,7 +20,7 @@ public NissaWhoShakesTheWorldEmblem() { Zone.COMMAND, new GainAbilityAllEffect( IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield, - StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, false + StaticFilters.FILTER_CONTROLLED_PERMANENT_LANDS, false ) )); this.setExpansionSetCodeForImage("WAR"); diff --git a/Mage/src/main/java/mage/game/command/emblems/RowanKenrithEmblem.java b/Mage/src/main/java/mage/game/command/emblems/RowanKenrithEmblem.java index 2f08b15ed7b7..f10f06328c36 100644 --- a/Mage/src/main/java/mage/game/command/emblems/RowanKenrithEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/RowanKenrithEmblem.java @@ -1,19 +1,15 @@ package mage.game.command.emblems; -import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CopyStackAbilityEffect; import mage.abilities.mana.ActivatedManaAbilityImpl; -import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; import mage.game.command.Emblem; import mage.game.events.GameEvent; import mage.game.stack.StackAbility; -import mage.players.Player; /** - * * @author TheElk801 */ public final class RowanKenrithEmblem extends Emblem { @@ -28,10 +24,10 @@ public RowanKenrithEmblem() { class RowanKenrithEmblemTriggeredAbility extends TriggeredAbilityImpl { RowanKenrithEmblemTriggeredAbility() { - super(Zone.COMMAND, new RowanKenrithEmblemEffect(), false); + super(Zone.COMMAND, new CopyStackAbilityEffect(), false); } - RowanKenrithEmblemTriggeredAbility(final RowanKenrithEmblemTriggeredAbility ability) { + private RowanKenrithEmblemTriggeredAbility(final RowanKenrithEmblemTriggeredAbility ability) { super(ability); } @@ -47,15 +43,15 @@ public boolean checkEventType(GameEvent event, Game game) { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getPlayerId().equals(getControllerId())) { - StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); - if (stackAbility != null - && !(stackAbility.getStackAbility() instanceof ActivatedManaAbilityImpl)) { - game.getState().setValue("rowanStackAbility", stackAbility); - return true; - } + if (!event.getPlayerId().equals(getControllerId())) { + return false; + } + StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); + if (stackAbility == null || stackAbility.getStackAbility() instanceof ActivatedManaAbilityImpl) { + return false; } - return false; + this.getEffects().setValue("stackAbility", stackAbility); + return true; } @Override @@ -63,37 +59,3 @@ public String getRule() { return "Whenever you activate an ability that isn't a mana ability, copy it. You may choose new targets for the copy."; } } - -class RowanKenrithEmblemEffect extends OneShotEffect { - - RowanKenrithEmblemEffect() { - super(Outcome.Benefit); - this.staticText = ", copy it. You may choose new targets for the copy."; - } - - RowanKenrithEmblemEffect(final RowanKenrithEmblemEffect effect) { - super(effect); - } - - @Override - public RowanKenrithEmblemEffect copy() { - return new RowanKenrithEmblemEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player == null) { - return false; - } - StackAbility ability = (StackAbility) game.getState().getValue("rowanStackAbility"); - Player controller = game.getPlayer(source.getControllerId()); - if (ability != null - && controller != null) { - ability.createCopyOnStack(game, source, source.getControllerId(), true); - game.informPlayers(source.getSourceObjectIfItStillExists(game).getName() + " : " + controller.getLogName() + " copied activated ability"); - return true; - } - return false; - } -} diff --git a/Mage/src/main/java/mage/game/command/emblems/RowanScholarOfSparksEmblem.java b/Mage/src/main/java/mage/game/command/emblems/RowanScholarOfSparksEmblem.java new file mode 100644 index 000000000000..707e0edb3689 --- /dev/null +++ b/Mage/src/main/java/mage/game/command/emblems/RowanScholarOfSparksEmblem.java @@ -0,0 +1,25 @@ +package mage.game.command.emblems; + +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CopyTargetSpellEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.command.Emblem; + +/** + * @author TheElk801 + */ +public final class RowanScholarOfSparksEmblem extends Emblem { + + // −4: You get an emblem with "Whenever you cast an instant or sorcery spell, you may pay {2}. If you do, copy that spell. You may choose new targets for the copy." + public RowanScholarOfSparksEmblem() { + this.setName("Emblem Rowan"); + this.setExpansionSetCodeForImage("STX"); + this.getAbilities().add(new SpellCastControllerTriggeredAbility( + Zone.COMMAND, new DoIfCostPaid(new CopyTargetSpellEffect(true), new GenericManaCost(2)), + StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, false, true + )); + } +} diff --git a/Mage/src/main/java/mage/game/command/emblems/TeferiHeroOfDominariaEmblem.java b/Mage/src/main/java/mage/game/command/emblems/TeferiHeroOfDominariaEmblem.java index 4794fa16c065..40b7677b8775 100644 --- a/Mage/src/main/java/mage/game/command/emblems/TeferiHeroOfDominariaEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/TeferiHeroOfDominariaEmblem.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.game.command.emblems; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/game/command/emblems/TibaltCosmicImposterEmblem.java b/Mage/src/main/java/mage/game/command/emblems/TibaltCosmicImpostorEmblem.java similarity index 77% rename from Mage/src/main/java/mage/game/command/emblems/TibaltCosmicImposterEmblem.java rename to Mage/src/main/java/mage/game/command/emblems/TibaltCosmicImpostorEmblem.java index 4aba4a99b393..e41c129c7a04 100644 --- a/Mage/src/main/java/mage/game/command/emblems/TibaltCosmicImposterEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/TibaltCosmicImpostorEmblem.java @@ -18,23 +18,23 @@ /** * @author jeffwadsworth */ -public final class TibaltCosmicImposterEmblem extends Emblem { +public final class TibaltCosmicImpostorEmblem extends Emblem { // You may play cards exiled with Tibalt, Cosmic Impostor, and you may spend mana as though it were mana of any color to cast those spells." - public TibaltCosmicImposterEmblem() { + public TibaltCosmicImpostorEmblem() { setName("Emblem Tibalt"); - this.getAbilities().add(new SimpleStaticAbility(Zone.COMMAND, new TibaltCosmicImposterPlayFromExileEffect())); + this.getAbilities().add(new SimpleStaticAbility(Zone.COMMAND, new TibaltCosmicImpostorPlayFromExileEffect())); } } -class TibaltCosmicImposterPlayFromExileEffect extends AsThoughEffectImpl { +class TibaltCosmicImpostorPlayFromExileEffect extends AsThoughEffectImpl { - TibaltCosmicImposterPlayFromExileEffect() { + TibaltCosmicImpostorPlayFromExileEffect() { super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfGame, Outcome.Benefit); staticText = "You may play cards exiled with Tibalt, Cosmic Impostor, and you may spend mana as though it were mana of any color to cast those spells"; } - TibaltCosmicImposterPlayFromExileEffect(final TibaltCosmicImposterPlayFromExileEffect effect) { + TibaltCosmicImpostorPlayFromExileEffect(final TibaltCosmicImpostorPlayFromExileEffect effect) { super(effect); } @@ -44,8 +44,8 @@ public boolean apply(Game game, Ability source) { } @Override - public TibaltCosmicImposterPlayFromExileEffect copy() { - return new TibaltCosmicImposterPlayFromExileEffect(this); + public TibaltCosmicImpostorPlayFromExileEffect copy() { + return new TibaltCosmicImpostorPlayFromExileEffect(this); } @Override @@ -54,7 +54,7 @@ public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, if (tibaltEmblem == null) { return false; } - // the exile zone of the Tibalt, Cosmic Imposter that spawned the emblem only + // the exile zone of the Tibalt, Cosmic Impostor that spawned the emblem only UUID exileId = CardUtil.getExileZoneId(tibaltEmblem.getSourceObject().getId().toString(), game); if (exileId == null) { return false; @@ -74,7 +74,7 @@ public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, if (exile.contains(mainCardId) && affectedControllerId.equals(source.getControllerId()) && game.getState().getZone(mainCardId).equals(Zone.EXILED)) { - CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, cardInExile, Duration.Custom); + CardUtil.makeCardPlayable(game, source, cardInExile, Duration.Custom, true); return true; } return false; diff --git a/Mage/src/main/java/mage/game/command/emblems/VenserTheSojournerEmblem.java b/Mage/src/main/java/mage/game/command/emblems/VenserTheSojournerEmblem.java index c3c570a4d5ce..9d6168089586 100644 --- a/Mage/src/main/java/mage/game/command/emblems/VenserTheSojournerEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/VenserTheSojournerEmblem.java @@ -7,6 +7,7 @@ import mage.abilities.effects.common.ExileTargetEffect; import mage.constants.Zone; import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.command.Emblem; import mage.game.events.GameEvent; @@ -36,8 +37,6 @@ public VenserTheSojournerEmblem() { class VenserTheSojournerSpellCastTriggeredAbility extends TriggeredAbilityImpl { - private static final FilterSpell spellCard = new FilterSpell("a spell"); - protected FilterSpell filter; /** * If true, the source that triggered the ability will be set as target to @@ -46,12 +45,10 @@ class VenserTheSojournerSpellCastTriggeredAbility extends TriggeredAbilityImpl { public VenserTheSojournerSpellCastTriggeredAbility(Effect effect, boolean optional) { super(Zone.COMMAND, effect, optional); - this.filter = spellCard; } public VenserTheSojournerSpellCastTriggeredAbility(final VenserTheSojournerSpellCastTriggeredAbility ability) { super(ability); - filter = ability.filter; } @Override @@ -63,7 +60,7 @@ public boolean checkEventType(GameEvent event, Game game) { public boolean checkTrigger(GameEvent event, Game game) { if (event.getPlayerId().equals(this.getControllerId())) { Spell spell = game.getStack().getSpell(event.getTargetId()); - return spell != null && filter.match(spell, game); + return StaticFilters.FILTER_SPELL_A.match(spell, game); } return false; } diff --git a/Mage/src/main/java/mage/game/command/planes/EdgeOfMalacolPlane.java b/Mage/src/main/java/mage/game/command/planes/EdgeOfMalacolPlane.java index 06d74f100a14..ddf68e00b00a 100644 --- a/Mage/src/main/java/mage/game/command/planes/EdgeOfMalacolPlane.java +++ b/Mage/src/main/java/mage/game/command/planes/EdgeOfMalacolPlane.java @@ -99,7 +99,7 @@ public boolean applies(GameEvent event, Ability source, Game game) { return false; } Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && filter.match(permanent, game) && Objects.equals(permanent.getControllerId(), game.getActivePlayerId())) { + if (filter.match(permanent, game) && Objects.equals(permanent.getControllerId(), game.getActivePlayerId())) { UUID oldController = source.getControllerId(); source.setControllerId(game.getActivePlayerId()); Effect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance(2)); diff --git a/Mage/src/main/java/mage/game/command/planes/FeedingGroundsPlane.java b/Mage/src/main/java/mage/game/command/planes/FeedingGroundsPlane.java index 553e7ce747e6..6a7ab373dbf6 100644 --- a/Mage/src/main/java/mage/game/command/planes/FeedingGroundsPlane.java +++ b/Mage/src/main/java/mage/game/command/planes/FeedingGroundsPlane.java @@ -10,7 +10,7 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.MainPhaseStackEmptyCondition; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.dynamicvalue.common.TargetConvertedManaCost; +import mage.abilities.dynamicvalue.common.TargetManaValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.RollPlanarDieEffect; import mage.abilities.effects.common.cost.CostModificationEffectImpl; @@ -37,7 +37,7 @@ public class FeedingGroundsPlane extends Plane { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature"); - private static final String rule = "put X +1/+1 counters on target creature, where X is that creature's converted mana cost"; + private static final String rule = "put X +1/+1 counters on target creature, where X is that creature's mana value"; public FeedingGroundsPlane() { this.setPlaneType(Planes.PLANE_FEEDING_GROUNDS); @@ -48,7 +48,7 @@ public FeedingGroundsPlane() { this.getAbilities().add(ability); // Active player can roll the planar die: Whenever you roll {CHAOS}, target red or green creature gets X +1/+1 counters - Effect chaosEffect = new AddCountersTargetEffect(CounterType.P1P1.createInstance(), TargetConvertedManaCost.instance); + Effect chaosEffect = new AddCountersTargetEffect(CounterType.P1P1.createInstance(), TargetManaValue.instance); Target chaosTarget = new TargetCreaturePermanent(1, 1, filter, false); List chaosEffects = new ArrayList<>(); @@ -138,7 +138,7 @@ public boolean applies(Ability abilityToModify, Ability source, Game game) { } else { // used at least for flashback ability because Flashback ability doesn't use stack Card sourceCard = game.getCard(abilityToModify.getSourceId()); - return sourceCard != null && filter.match(sourceCard, game) && selectedByRuntimeData(sourceCard, source, game); + return filter.match(sourceCard, game) && selectedByRuntimeData(sourceCard, source, game); } } return false; diff --git a/Mage/src/main/java/mage/game/command/planes/FieldsOfSummerPlane.java b/Mage/src/main/java/mage/game/command/planes/FieldsOfSummerPlane.java index ae561f6dcab7..e4e07bf44265 100644 --- a/Mage/src/main/java/mage/game/command/planes/FieldsOfSummerPlane.java +++ b/Mage/src/main/java/mage/game/command/planes/FieldsOfSummerPlane.java @@ -14,6 +14,7 @@ import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect; import mage.constants.*; import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.command.Plane; import mage.players.Player; @@ -29,14 +30,13 @@ */ public class FieldsOfSummerPlane extends Plane { - private static final FilterSpell filter = new FilterSpell("a spell"); public FieldsOfSummerPlane() { this.setPlaneType(Planes.PLANE_FIELDS_OF_SUMMER); this.setExpansionSetCodeForImage("PCA"); // Whenever a player casts a spell, that player may gain 2 life - SpellCastAllTriggeredAbility ability = new SpellCastAllTriggeredAbility(Zone.COMMAND, new FieldsOfSummerEffect(), filter, false, SetTargetPointer.PLAYER); + SpellCastAllTriggeredAbility ability = new SpellCastAllTriggeredAbility(Zone.COMMAND, new FieldsOfSummerEffect(), StaticFilters.FILTER_SPELL_A, false, SetTargetPointer.PLAYER); this.getAbilities().add(ability); // Active player can roll the planar die: Whenever you roll {CHAOS}, you may gain 10 life diff --git a/Mage/src/main/java/mage/game/draft/RateCard.java b/Mage/src/main/java/mage/game/draft/RateCard.java index 9a27f142d696..367379977831 100644 --- a/Mage/src/main/java/mage/game/draft/RateCard.java +++ b/Mage/src/main/java/mage/game/draft/RateCard.java @@ -110,7 +110,7 @@ public static int rateCard(Card card, List allowedColors, boo } private static int isRemoval(Card card) { - if (card.isEnchantment() || card.isInstant() || card.isSorcery()) { + if (card.isEnchantment() || card.isInstantOrSorcery()) { for (Ability ability : card.getAbilities()) { for (Effect effect : ability.getEffects()) { @@ -317,10 +317,10 @@ private synchronized static void readFromFile(String path) { * @return */ private static int getManaCostScore(Card card, List allowedColors) { - int converted = card.getManaCost().convertedManaCost(); + int converted = card.getManaValue(); if (allowedColors == null) { int colorPenalty = 0; - for (String symbol : card.getManaCost().getSymbols()) { + for (String symbol : card.getManaCostSymbols()) { if (isColoredMana(symbol)) { colorPenalty++; } @@ -329,7 +329,7 @@ private static int getManaCostScore(Card card, List allowedCo } final Map singleCount = new HashMap<>(); int maxSingleCount = 0; - for (String symbol : card.getManaCost().getSymbols()) { + for (String symbol : card.getManaCostSymbols()) { int count = 0; symbol = symbol.replace("{", "").replace("}", ""); if (isColoredMana(symbol)) { @@ -385,7 +385,7 @@ public static boolean isColoredMana(String symbol) { */ public static int getColorManaCount(Card card) { int count = 0; - for (String symbol : card.getManaCost().getSymbols()) { + for (String symbol : card.getManaCostSymbols()) { if (isColoredMana(symbol)) { count++; } @@ -401,7 +401,7 @@ public static int getColorManaCount(Card card) { */ public static int getDifferentColorManaCount(Card card) { Set symbols = new HashSet<>(); - for (String symbol : card.getManaCost().getSymbols()) { + for (String symbol : card.getManaCostSymbols()) { if (isColoredMana(symbol)) { symbols.add(symbol); } diff --git a/Mage/src/main/java/mage/game/events/DamageCreatureEvent.java b/Mage/src/main/java/mage/game/events/DamageCreatureEvent.java deleted file mode 100644 index 9e3d1372db2b..000000000000 --- a/Mage/src/main/java/mage/game/events/DamageCreatureEvent.java +++ /dev/null @@ -1,14 +0,0 @@ -package mage.game.events; - -import java.util.UUID; - -/** - * - * @author BetaSteward_at_googlemail.com - */ -public class DamageCreatureEvent extends DamageEvent { - - public DamageCreatureEvent(UUID targetId, UUID damageSourceId, UUID targetControllerId, int amount, boolean preventable, boolean combat) { - super(GameEvent.EventType.DAMAGE_CREATURE, targetId, damageSourceId, targetControllerId, amount, preventable, combat); - } -} diff --git a/Mage/src/main/java/mage/game/events/DamageEvent.java b/Mage/src/main/java/mage/game/events/DamageEvent.java index 83b94ba5370b..431d1f74479c 100644 --- a/Mage/src/main/java/mage/game/events/DamageEvent.java +++ b/Mage/src/main/java/mage/game/events/DamageEvent.java @@ -3,12 +3,13 @@ import java.util.UUID; /** - * * @author BetaSteward_at_googlemail.com */ public abstract class DamageEvent extends GameEvent { protected boolean combat; + private boolean asThoughInfect = false; + private boolean asThoughWither = false; public DamageEvent(EventType type, UUID targetId, UUID damageSourceId, UUID targetControllerId, int amount, boolean preventable, boolean combat) { super(type, targetId, null, targetControllerId, amount, preventable); @@ -24,4 +25,19 @@ public boolean isPreventable() { return flag; } + public void setAsThoughInfect(boolean asThoughInfect) { + this.asThoughInfect = asThoughInfect; + } + + public boolean isAsThoughInfect() { + return asThoughInfect; + } + + public void setAsThoughWither(boolean asThoughWither) { + this.asThoughWither = asThoughWither; + } + + public boolean isAsThoughWither() { + return asThoughWither; + } } diff --git a/Mage/src/main/java/mage/game/events/DamagePermanentEvent.java b/Mage/src/main/java/mage/game/events/DamagePermanentEvent.java new file mode 100644 index 000000000000..e0866cebdc34 --- /dev/null +++ b/Mage/src/main/java/mage/game/events/DamagePermanentEvent.java @@ -0,0 +1,13 @@ +package mage.game.events; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public class DamagePermanentEvent extends DamageEvent { + + public DamagePermanentEvent(UUID targetId, UUID damageSourceId, UUID targetControllerId, int amount, boolean preventable, boolean combat) { + super(EventType.DAMAGE_PERMANENT, targetId, damageSourceId, targetControllerId, amount, preventable, combat); + } +} diff --git a/Mage/src/main/java/mage/game/events/DamagePlaneswalkerEvent.java b/Mage/src/main/java/mage/game/events/DamagePlaneswalkerEvent.java deleted file mode 100644 index 5c7481c810bc..000000000000 --- a/Mage/src/main/java/mage/game/events/DamagePlaneswalkerEvent.java +++ /dev/null @@ -1,14 +0,0 @@ -package mage.game.events; - -import java.util.UUID; - -/** - * - * @author BetaSteward_at_googlemail.com - */ -public class DamagePlaneswalkerEvent extends DamageEvent { - - public DamagePlaneswalkerEvent(UUID targetId, UUID damageSourceId, UUID targetControllerId, int amount, boolean preventable, boolean combat) { - super(GameEvent.EventType.DAMAGE_PLANESWALKER, targetId, damageSourceId, targetControllerId, amount, preventable, combat); - } -} diff --git a/Mage/src/main/java/mage/game/events/DamagedBatchEvent.java b/Mage/src/main/java/mage/game/events/DamagedBatchEvent.java index a5e9885dcda1..1d04ed124acd 100644 --- a/Mage/src/main/java/mage/game/events/DamagedBatchEvent.java +++ b/Mage/src/main/java/mage/game/events/DamagedBatchEvent.java @@ -33,11 +33,8 @@ public static DamagedBatchEvent makeEvent(DamagedEvent damagedEvent) { if (damagedEvent instanceof DamagedPlayerEvent) { event = new DamagedPlayerBatchEvent(); event.addEvent(damagedEvent); - } else if (damagedEvent instanceof DamagedCreatureEvent) { - event = new DamagedCreatureBatchEvent(); - event.addEvent(damagedEvent); - } else if (damagedEvent instanceof DamagedPlaneswalkerEvent) { - event = new DamagedPlaneswalkerBatchEvent(); + } else if (damagedEvent instanceof DamagedPermanentEvent) { + event = new DamagedPermanentBatchEvent(); event.addEvent(damagedEvent); } return event; diff --git a/Mage/src/main/java/mage/game/events/DamagedCreatureBatchEvent.java b/Mage/src/main/java/mage/game/events/DamagedCreatureBatchEvent.java deleted file mode 100644 index 57fb3afc2710..000000000000 --- a/Mage/src/main/java/mage/game/events/DamagedCreatureBatchEvent.java +++ /dev/null @@ -1,11 +0,0 @@ -package mage.game.events; - -/** - * @author TheElk801 - */ -public class DamagedCreatureBatchEvent extends DamagedBatchEvent { - - public DamagedCreatureBatchEvent() { - super(GameEvent.EventType.DAMAGED_CREATURE_BATCH, DamagedCreatureEvent.class); - } -} diff --git a/Mage/src/main/java/mage/game/events/DamagedCreatureEvent.java b/Mage/src/main/java/mage/game/events/DamagedCreatureEvent.java deleted file mode 100644 index 7272b5643e9a..000000000000 --- a/Mage/src/main/java/mage/game/events/DamagedCreatureEvent.java +++ /dev/null @@ -1,16 +0,0 @@ - - -package mage.game.events; - -import java.util.UUID; - -/** - * - * @author BetaSteward_at_googlemail.com - */ -public class DamagedCreatureEvent extends DamagedEvent { - - public DamagedCreatureEvent(UUID targetId, UUID attackerId, UUID playerId, int amount, boolean combat) { - super(GameEvent.EventType.DAMAGED_CREATURE, targetId, attackerId, playerId, amount, combat); - } -} diff --git a/Mage/src/main/java/mage/game/events/DamagedPermanentBatchEvent.java b/Mage/src/main/java/mage/game/events/DamagedPermanentBatchEvent.java new file mode 100644 index 000000000000..7ee26d5efdf2 --- /dev/null +++ b/Mage/src/main/java/mage/game/events/DamagedPermanentBatchEvent.java @@ -0,0 +1,11 @@ +package mage.game.events; + +/** + * @author TheElk801 + */ +public class DamagedPermanentBatchEvent extends DamagedBatchEvent { + + public DamagedPermanentBatchEvent() { + super(EventType.DAMAGED_PERMANENT_BATCH, DamagedPermanentEvent.class); + } +} diff --git a/Mage/src/main/java/mage/game/events/DamagedPermanentEvent.java b/Mage/src/main/java/mage/game/events/DamagedPermanentEvent.java new file mode 100644 index 000000000000..9c9798775143 --- /dev/null +++ b/Mage/src/main/java/mage/game/events/DamagedPermanentEvent.java @@ -0,0 +1,13 @@ +package mage.game.events; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public class DamagedPermanentEvent extends DamagedEvent { + + public DamagedPermanentEvent(UUID targetId, UUID attackerId, UUID playerId, int amount, boolean combat) { + super(EventType.DAMAGED_PERMANENT, targetId, attackerId, playerId, amount, combat); + } +} diff --git a/Mage/src/main/java/mage/game/events/DamagedPlaneswalkerBatchEvent.java b/Mage/src/main/java/mage/game/events/DamagedPlaneswalkerBatchEvent.java deleted file mode 100644 index 9f74c2edb639..000000000000 --- a/Mage/src/main/java/mage/game/events/DamagedPlaneswalkerBatchEvent.java +++ /dev/null @@ -1,11 +0,0 @@ -package mage.game.events; - -/** - * @author TheElk801 - */ -public class DamagedPlaneswalkerBatchEvent extends DamagedBatchEvent { - - public DamagedPlaneswalkerBatchEvent() { - super(GameEvent.EventType.DAMAGED_PLANESWALKER_BATCH, DamagedPlaneswalkerEvent.class); - } -} diff --git a/Mage/src/main/java/mage/game/events/DamagedPlaneswalkerEvent.java b/Mage/src/main/java/mage/game/events/DamagedPlaneswalkerEvent.java deleted file mode 100644 index ed3092047cba..000000000000 --- a/Mage/src/main/java/mage/game/events/DamagedPlaneswalkerEvent.java +++ /dev/null @@ -1,16 +0,0 @@ -package mage.game.events; - -import java.util.UUID; - -/** - * - * @author BetaSteward_at_googlemail.com - */ -public class DamagedPlaneswalkerEvent extends DamagedEvent { - - public DamagedPlaneswalkerEvent(UUID targetId, UUID attackerId, UUID playerId, int amount, boolean combat) { - super(GameEvent.EventType.DAMAGED_PLANESWALKER, targetId, null, playerId, amount, combat); - this.setSourceId(attackerId); - } - -} diff --git a/Mage/src/main/java/mage/game/events/DefenderAttackedEvent.java b/Mage/src/main/java/mage/game/events/DefenderAttackedEvent.java new file mode 100644 index 000000000000..2f215c36977b --- /dev/null +++ b/Mage/src/main/java/mage/game/events/DefenderAttackedEvent.java @@ -0,0 +1,37 @@ +package mage.game.events; + +import mage.MageObjectReference; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public class DefenderAttackedEvent extends GameEvent { + + private final Set morSet = new HashSet<>(); + + public DefenderAttackedEvent(UUID targetId, UUID playerId) { + super(EventType.DEFENDER_ATTACKED, targetId, null, playerId); + + } + + public static void makeAddEvents(Map> morMapSet, UUID attackingPlayerId, Game game) { + for (Map.Entry> entry : morMapSet.entrySet()) { + DefenderAttackedEvent event = new DefenderAttackedEvent(entry.getKey(), attackingPlayerId); + event.morSet.addAll(entry.getValue()); + game.addSimultaneousEvent(event); + } + } + + public List getAttackers(Game game) { + return morSet + .stream() + .map(mor -> mor.getPermanent(game)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } +} diff --git a/Mage/src/main/java/mage/game/events/DiscardedCardsEvent.java b/Mage/src/main/java/mage/game/events/DiscardedCardsEvent.java new file mode 100644 index 000000000000..0bef43f65604 --- /dev/null +++ b/Mage/src/main/java/mage/game/events/DiscardedCardsEvent.java @@ -0,0 +1,25 @@ +package mage.game.events; + +import mage.abilities.Ability; +import mage.cards.Cards; +import mage.cards.CardsImpl; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public class DiscardedCardsEvent extends GameEvent { + + private final Cards discardedCards; + + public DiscardedCardsEvent(Ability source, UUID playerId, int amount, Cards discardedCards) { + super(EventType.DISCARDED_CARDS, null, source, playerId, amount, false); + this.discardedCards = new CardsImpl(discardedCards); + } + + public Cards getDiscardedCards() { + return discardedCards; + } +} diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java index 7f3ea4619110..ad1f6d481c8c 100644 --- a/Mage/src/main/java/mage/game/events/GameEvent.java +++ b/Mage/src/main/java/mage/game/events/GameEvent.java @@ -65,7 +65,7 @@ public enum EventType { //player events /* ZONE_CHANGE targetId id of the zone changing object - sourceId sourceId of the ability with the object moving effect + sourceId sourceId of the ability with the object moving effect (WARNING, can be null if it move of fizzled spells) playerId controller of the moved object amount not used for this event flag not used for this event @@ -188,6 +188,7 @@ flag event is result of effect (1) or result of cost (0) playerId player that tries to use this ability */ TRIGGERED_ABILITY, + ABILITY_TRIGGERED, RESOLVING_ABILITY, /* COPY_STACKOBJECT targetId id of the spell/ability to copy @@ -274,6 +275,7 @@ playerId player who try to targeting (can be different from source ability's amount not used for this event flag not used for this event */ + DEFENDER_ATTACKED, DECLARING_BLOCKERS, DECLARED_BLOCKERS, DECLARE_BLOCKER, @@ -301,6 +303,7 @@ playerId player who try to targeting (can be different from source ability's DIDNT_PAY_CUMULATIVE_UPKEEP, LIFE_PAID, CASCADE_LAND, + LEARN, //permanent events ENTERS_THE_BATTLEFIELD_SELF, /* 616.1a If any of the replacement and/or prevention effects are self-replacement effects (see rule 614.15), one of them must be chosen. If not, proceed to rule 616.1b. */ @@ -359,8 +362,9 @@ playerId player who makes the exert (can be different from permanent's contro flag not used for this event */ OPTION_USED, - DAMAGE_CREATURE, DAMAGED_CREATURE, DAMAGED_CREATURE_BATCH, - DAMAGE_PLANESWALKER, DAMAGED_PLANESWALKER, DAMAGED_PLANESWALKER_BATCH, + DAMAGE_PERMANENT, + DAMAGED_PERMANENT, + DAMAGED_PERMANENT_BATCH, DESTROY_PERMANENT, /* DESTROY_PERMANENT_BY_LEGENDARY_RULE targetId id of the permanent to destroy @@ -436,6 +440,14 @@ playerId player who makes the exert (can be different from permanent's contro //combat events COMBAT_DAMAGE_APPLIED, SELECTED_ATTACKER, SELECTED_BLOCKER, + /* voting + targetId player who voting + sourceId sourceId of the effect doing the voting + playerId player who deciding about voting, can be changed by replace events + amount not used for this event + flag not used for this event + */ + VOTE, VOTED, //custom events CUSTOM_EVENT } @@ -492,8 +504,7 @@ public static GameEvent getEvent(UUID customEventType, UUID targetId, Ability so return new GameEvent(customEventType, targetId, source, playerId); } - private GameEvent(EventType type, UUID customEventType, UUID targetId, Ability source, UUID playerId, int amount, boolean flag) - { + private GameEvent(EventType type, UUID customEventType, UUID targetId, Ability source, UUID playerId, int amount, boolean flag) { this(type, customEventType, targetId, source, playerId, amount, flag, null); } diff --git a/Mage/src/main/java/mage/game/events/PlayerQueryEvent.java b/Mage/src/main/java/mage/game/events/PlayerQueryEvent.java index 4aaf42d0338c..4b906c6811b6 100644 --- a/Mage/src/main/java/mage/game/events/PlayerQueryEvent.java +++ b/Mage/src/main/java/mage/game/events/PlayerQueryEvent.java @@ -35,6 +35,7 @@ public enum QueryType { PLAY_MANA, PLAY_X_MANA, AMOUNT, + MULTI_AMOUNT, PICK_CARD, CONSTRUCT, CHOOSE_PILE, @@ -58,8 +59,11 @@ public enum QueryType { private List pile1; private List pile2; private Choice choice; + private List messages; - private PlayerQueryEvent(UUID playerId, String message, List abilities, Set choices, Set targets, Cards cards, QueryType queryType, int min, int max, boolean required, Map options) { + private PlayerQueryEvent(UUID playerId, String message, List abilities, Set choices, + Set targets, Cards cards, QueryType queryType, int min, int max, boolean required, + Map options, List messages) { super(playerId); this.queryType = queryType; this.message = message; @@ -77,6 +81,7 @@ private PlayerQueryEvent(UUID playerId, String message, List this.options = options; } this.options.put("queryType", queryType); + this.messages = messages; } private PlayerQueryEvent(UUID playerId, String message, List booster, QueryType queryType, int time) { @@ -143,7 +148,7 @@ public static PlayerQueryEvent askEvent(UUID playerId, String message, Ability s } options.put("originalId", source.getOriginalId()); } - return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.ASK, 0, 0, false, options); + return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.ASK, 0, 0, false, options, null); } public static PlayerQueryEvent chooseAbilityEvent(UUID playerId, String message, String objectName, List choices) { @@ -152,7 +157,7 @@ public static PlayerQueryEvent chooseAbilityEvent(UUID playerId, String message, nameAsSet = new HashSet<>(); nameAsSet.add(objectName); } - return new PlayerQueryEvent(playerId, message, choices, nameAsSet, null, null, QueryType.CHOOSE_ABILITY, 0, 0, false, null); + return new PlayerQueryEvent(playerId, message, choices, nameAsSet, null, null, QueryType.CHOOSE_ABILITY, 0, 0, false, null, null); } public static PlayerQueryEvent choosePileEvent(UUID playerId, String message, List pile1, List pile2) { @@ -168,19 +173,19 @@ public static PlayerQueryEvent chooseChoiceEvent(UUID playerId, Choice choice) { } public static PlayerQueryEvent targetEvent(UUID playerId, String message, Set targets, boolean required) { - return new PlayerQueryEvent(playerId, message, null, null, targets, null, QueryType.PICK_TARGET, 0, 0, required, null); + return new PlayerQueryEvent(playerId, message, null, null, targets, null, QueryType.PICK_TARGET, 0, 0, required, null, null); } public static PlayerQueryEvent targetEvent(UUID playerId, String message, Set targets, boolean required, Map options) { - return new PlayerQueryEvent(playerId, message, null, null, targets, null, QueryType.PICK_TARGET, 0, 0, required, options); + return new PlayerQueryEvent(playerId, message, null, null, targets, null, QueryType.PICK_TARGET, 0, 0, required, options, null); } public static PlayerQueryEvent targetEvent(UUID playerId, String message, Cards cards, boolean required, Map options) { - return new PlayerQueryEvent(playerId, message, null, null, null, cards, QueryType.PICK_TARGET, 0, 0, required, options); + return new PlayerQueryEvent(playerId, message, null, null, null, cards, QueryType.PICK_TARGET, 0, 0, required, options, null); } public static PlayerQueryEvent targetEvent(UUID playerId, String message, List abilities) { - return new PlayerQueryEvent(playerId, message, abilities, null, null, null, QueryType.PICK_ABILITY, 0, 0, true, null); + return new PlayerQueryEvent(playerId, message, abilities, null, null, null, QueryType.PICK_ABILITY, 0, 0, true, null, null); } public static PlayerQueryEvent targetEvent(UUID playerId, String message, List perms, boolean required) { @@ -188,23 +193,27 @@ public static PlayerQueryEvent targetEvent(UUID playerId, String message, List

options) { - return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.SELECT, 0, 0, false, options); + return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.SELECT, 0, 0, false, options, null); } public static PlayerQueryEvent playManaEvent(UUID playerId, String message, Map options) { - return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.PLAY_MANA, 0, 0, false, options); + return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.PLAY_MANA, 0, 0, false, options, null); } public static PlayerQueryEvent playXManaEvent(UUID playerId, String message) { - return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.PLAY_X_MANA, 0, 0, false, null); + return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.PLAY_X_MANA, 0, 0, false, null, null); } public static PlayerQueryEvent amountEvent(UUID playerId, String message, int min, int max) { - return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.AMOUNT, min, max, false, null); + return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.AMOUNT, min, max, false, null, null); + } + + public static PlayerQueryEvent multiAmountEvent(UUID playerId, List messages, int min, int max, Map options) { + return new PlayerQueryEvent(playerId, null, null, null, null, null, QueryType.MULTI_AMOUNT, min, max, false, options, messages); } public static PlayerQueryEvent pickCard(UUID playerId, String message, List booster, int time) { @@ -287,4 +296,7 @@ public Choice getChoice() { return choice; } + public List getMessages() { + return messages; + } } diff --git a/Mage/src/main/java/mage/game/events/PlayerQueryEventSource.java b/Mage/src/main/java/mage/game/events/PlayerQueryEventSource.java index 87dd6f45ec9a..b2aac5ec3059 100644 --- a/Mage/src/main/java/mage/game/events/PlayerQueryEventSource.java +++ b/Mage/src/main/java/mage/game/events/PlayerQueryEventSource.java @@ -84,6 +84,10 @@ public void amount(UUID playerId, String message, int min, int max) { dispatcher.fireEvent(PlayerQueryEvent.amountEvent(playerId, message, min, max)); } + public void multiAmount(UUID playerId, List messages, int min, int max, Map options) { + dispatcher.fireEvent(PlayerQueryEvent.multiAmountEvent(playerId, messages, min, max, options)); + } + public void chooseChoice(UUID playerId, Choice choice) { dispatcher.fireEvent(PlayerQueryEvent.chooseChoiceEvent(playerId, choice)); } diff --git a/Mage/src/main/java/mage/game/events/VoteEvent.java b/Mage/src/main/java/mage/game/events/VoteEvent.java new file mode 100644 index 000000000000..fe9ce377f5e5 --- /dev/null +++ b/Mage/src/main/java/mage/game/events/VoteEvent.java @@ -0,0 +1,34 @@ +package mage.game.events; + +import mage.abilities.Ability; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public class VoteEvent extends GameEvent { + + private int extraVotes = 0; // example: you get an additional vote + private int optionalExtraVotes = 0; // example: you may vote an additional time + + public VoteEvent(UUID playerId, Ability source) { + super(EventType.VOTE, playerId, source, playerId); + } + + public void incrementExtraVotes() { + extraVotes++; + } + + public void incrementOptionalExtraVotes() { + optionalExtraVotes++; + } + + public int getExtraVotes() { + return extraVotes; + } + + public int getOptionalExtraVotes() { + return optionalExtraVotes; + } +} diff --git a/Mage/src/main/java/mage/game/events/VotedEvent.java b/Mage/src/main/java/mage/game/events/VotedEvent.java new file mode 100644 index 000000000000..87d20d30e7f2 --- /dev/null +++ b/Mage/src/main/java/mage/game/events/VotedEvent.java @@ -0,0 +1,24 @@ +package mage.game.events; + +import mage.abilities.Ability; +import mage.choices.VoteHandler; + +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public class VotedEvent extends GameEvent { + + private final VoteHandler voteHandler; + + public VotedEvent(Ability source, VoteHandler voteHandler) { + super(EventType.VOTED, source.getSourceId(), source, source.getControllerId()); + this.voteHandler = voteHandler; + } + + public Set getDidntVote(UUID playerId) { + return voteHandler.getDidntVote(playerId); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/Battlefield.java b/Mage/src/main/java/mage/game/permanent/Battlefield.java index 4e63f5749845..a4722586833e 100644 --- a/Mage/src/main/java/mage/game/permanent/Battlefield.java +++ b/Mage/src/main/java/mage/game/permanent/Battlefield.java @@ -382,4 +382,9 @@ public int countTokens(UUID controllerId) { .mapToInt(x -> x ? 1 : 0) .sum(); } + + @Override + public String toString() { + return "Permanents: " + field.size(); + } } diff --git a/Mage/src/main/java/mage/game/permanent/Permanent.java b/Mage/src/main/java/mage/game/permanent/Permanent.java index 1e1194037565..f1f195758cd3 100644 --- a/Mage/src/main/java/mage/game/permanent/Permanent.java +++ b/Mage/src/main/java/mage/game/permanent/Permanent.java @@ -101,7 +101,7 @@ public interface Permanent extends Card, Controllable { /** * @param attachment - * @param source can be null for default checks like state base + * @param source can be null for default checks like state base * @param game * @param silentMode - use it to ignore warning message for users (e.g. for * checking only) @@ -123,8 +123,8 @@ public interface Permanent extends Card, Controllable { * Uses in replace events only * * @param damage - * @param attackerId id of the permanent or player who make damage (source.getSourceId() in most cases) - * @param source can be null for default game actions like combat + * @param attackerId id of the permanent or player who make damage (source.getSourceId() in most cases) + * @param source can be null for default game actions like combat * @param game * @param combat * @param preventable @@ -137,8 +137,8 @@ public interface Permanent extends Card, Controllable { * Uses in combat only to deal damage at the same time * * @param damage - * @param attackerId id of the permanent or player who make damage (source.getSourceId() in most cases) - * @param source can be null for default game actions like combat + * @param attackerId id of the permanent or player who make damage (source.getSourceId() in most cases) + * @param source can be null for default game actions like combat * @param game * @param preventable * @param combat @@ -150,6 +150,8 @@ public interface Permanent extends Card, Controllable { int applyDamage(Game game); + int getLethalDamage(UUID attackerId, Game game); + void removeAllDamage(Game game); void reset(Game game); @@ -159,7 +161,6 @@ public interface Permanent extends Card, Controllable { boolean destroy(Ability source, Game game, boolean noRegen); /** - * * @param source can be null for state base actions * @param game * @return diff --git a/Mage/src/main/java/mage/game/permanent/PermanentCard.java b/Mage/src/main/java/mage/game/permanent/PermanentCard.java index 3366c8784e6d..50d286c5df18 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentCard.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentCard.java @@ -174,30 +174,6 @@ public boolean turnFaceUp(Ability source, Game game, UUID playerId) { return false; } - @Override - public void adjustTargets(Ability ability, Game game) { - if (this.isTransformed() && card.getSecondCardFace() != null) { - card.getSecondCardFace().adjustTargets(ability, game); - } else { - if (this.isCopy()) { - // if COPIED card have adjuster then it's must be called instead own -- see OathOfLieges tests - // raise null error on wrong copy - this.getCopyFrom().adjustTargets(ability, game); - } else { - card.adjustTargets(ability, game); - } - } - } - - @Override - public void adjustCosts(Ability ability, Game game) { - if (this.isTransformed() && card.getSecondCardFace() != null) { - card.getSecondCardFace().adjustCosts(ability, game); - } else { - card.adjustCosts(ability, game); - } - } - @Override public ManaCosts getManaCost() { if (faceDown) { // face down permanent has always {0} mana costs @@ -208,18 +184,18 @@ public ManaCosts getManaCost() { } @Override - public int getConvertedManaCost() { + public int getManaValue() { if (isTransformed()) { // 711.4b While a double-faced permanent's back face is up, it has only the characteristics of its back face. // However, its converted mana cost is calculated using the mana cost of its front face. This is a change from previous rules. // If a permanent is copying the back face of a double-faced card (even if the card representing that copy // is itself a double-faced card), the converted mana cost of that permanent is 0. - return getCard().getConvertedManaCost(); + return getCard().getManaValue(); } if (faceDown) { // game not neccessary - return getManaCost().convertedManaCost(); + return getManaCost().manaValue(); } - return super.getConvertedManaCost(); + return super.getManaValue(); } diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index 7e5365d3428c..61c3aa51ec64 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -50,15 +50,17 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { private static final Logger logger = Logger.getLogger(PermanentImpl.class); - static class MarkedDamageInfo implements Serializable { + private static class MarkedDamageInfo implements Serializable { - public MarkedDamageInfo(Counter counter, MageObject sourceObject) { + private final Counter counter; + private final MageObject sourceObject; + private final boolean addCounters; + + private MarkedDamageInfo(Counter counter, MageObject sourceObject, boolean addCounters) { this.counter = counter; this.sourceObject = sourceObject; + this.addCounters = addCounters; } - - Counter counter; - MageObject sourceObject; } private static final ThreadLocalStringBuilder threadLocalBuilder = new ThreadLocalStringBuilder(300); @@ -146,7 +148,7 @@ public PermanentImpl(final PermanentImpl permanent) { if (permanent.markedDamage != null) { markedDamage = new ArrayList<>(); for (MarkedDamageInfo mdi : permanent.markedDamage) { - markedDamage.add(new MarkedDamageInfo(mdi.counter.copy(), mdi.sourceObject)); + markedDamage.add(new MarkedDamageInfo(mdi.counter.copy(), mdi.sourceObject, mdi.addCounters)); } } if (permanent.info != null) { @@ -864,71 +866,127 @@ public int damage(int damage, UUID attackerId, Ability source, Game game, boolea */ private int doDamage(int damageAmount, UUID attackerId, Ability source, Game game, boolean preventable, boolean combat, boolean markDamage, List appliedEffects) { int damageDone = 0; - if (damageAmount > 0 && canDamage(game.getObject(attackerId), game)) { - if (this.isPlaneswalker()) { - damageDone = damagePlaneswalker(damageAmount, attackerId, source, game, preventable, combat, markDamage, appliedEffects); - } else { - damageDone = damageCreature(damageAmount, attackerId, source, game, preventable, combat, markDamage, appliedEffects); - } - if (damageDone > 0) { - UUID sourceControllerId = null; - Abilities sourceAbilities = null; - MageObject attacker = game.getPermanentOrLKIBattlefield(attackerId); - if (attacker == null) { - StackObject stackObject = game.getStack().getStackObject(attackerId); - if (stackObject != null) { - attacker = stackObject.getStackAbility().getSourceObject(game); - } else { - attacker = game.getObject(attackerId); - } - if (attacker instanceof Spell) { - sourceAbilities = ((Spell) attacker).getAbilities(game); - sourceControllerId = ((Spell) attacker).getControllerId(); - } else if (attacker instanceof Card) { - sourceAbilities = ((Card) attacker).getAbilities(game); - sourceControllerId = ((Card) attacker).getOwnerId(); - } else if (attacker instanceof CommandObject) { - sourceControllerId = ((CommandObject) attacker).getControllerId(); - sourceAbilities = attacker.getAbilities(); - } else { - attacker = null; - } + if (damageAmount < 1 || !canDamage(game.getObject(attackerId), game)) { + return 0; + } + DamageEvent event = new DamagePermanentEvent(objectId, attackerId, controllerId, damageAmount, preventable, combat); + event.setAppliedEffects(appliedEffects); + if (game.replaceEvent(event)) { + return 0; + } + int actualDamage = checkProtectionAbilities(event, attackerId, source, game); + if (actualDamage < 1) { + return 0; + } + int lethal = getLethalDamage(attackerId, game); + MageObject attacker = game.getObject(attackerId); + if (this.isCreature()) { + if (checkWither(event, attacker, game)) { + if (markDamage) { + // mark damage only + markDamage(CounterType.M1M1.createInstance(actualDamage), attacker, true); } else { - sourceAbilities = ((Permanent) attacker).getAbilities(game); - sourceControllerId = ((Permanent) attacker).getControllerId(); - } - if (attacker != null && sourceAbilities != null) { - if (sourceAbilities.containsKey(LifelinkAbility.getInstance().getId())) { - if (markDamage) { - game.getPermanent(attackerId).markLifelink(damageDone); - } else { - Player player = game.getPlayer(sourceControllerId); - player.gainLife(damageDone, game, source); - } - } - if (sourceAbilities.containsKey(DeathtouchAbility.getInstance().getId())) { - deathtouched = true; - } - if (dealtDamageByThisTurn == null) { - dealtDamageByThisTurn = new HashSet<>(); + Ability damageSourceAbility = null; + if (attacker instanceof Permanent) { + damageSourceAbility = ((Permanent) attacker).getSpellAbility(); } - // Unstable ability - Earl of Squirrel - if (sourceAbilities.containsKey(SquirrellinkAbility.getInstance().getId())) { - Player player = game.getPlayer(sourceControllerId); - new SquirrelToken().putOntoBattlefield(damageDone, game, source, player.getId()); - } - dealtDamageByThisTurn.add(new MageObjectReference(attacker, game)); + // deal damage immediately + addCounters(CounterType.M1M1.createInstance(actualDamage), game.getControllerId(attackerId), damageSourceAbility, game); } - if (attacker == null) { - game.informPlayers(getLogName() + " gets " + damageDone + " damage"); + } else { + this.damage = CardUtil.overflowInc(this.damage, actualDamage); + } + } + if (this.isPlaneswalker()) { + int loyalty = getCounters(game).getCount(CounterType.LOYALTY); + int countersToRemove = Math.min(actualDamage, loyalty); + if (attacker != null && markDamage) { + markDamage(CounterType.LOYALTY.createInstance(countersToRemove), attacker, false); + } else { + removeCounters(CounterType.LOYALTY.getName(), countersToRemove, source, game); + } + } + DamagedEvent damagedEvent = new DamagedPermanentEvent(this.getId(), attackerId, this.getControllerId(), actualDamage, combat); + damagedEvent.setExcess(actualDamage - lethal); + game.fireEvent(damagedEvent); + game.getState().addSimultaneousDamage(damagedEvent, game); + damageDone = actualDamage; + if (damageDone < 1) { + return 0; + } + UUID sourceControllerId = null; + Abilities sourceAbilities = null; + attacker = game.getPermanentOrLKIBattlefield(attackerId); + if (attacker == null) { + StackObject stackObject = game.getStack().getStackObject(attackerId); + if (stackObject != null) { + attacker = stackObject.getStackAbility().getSourceObject(game); + } else { + attacker = game.getObject(attackerId); + } + if (attacker instanceof Spell) { + sourceAbilities = ((Spell) attacker).getAbilities(game); + sourceControllerId = ((Spell) attacker).getControllerId(); + } else if (attacker instanceof Card) { + sourceAbilities = ((Card) attacker).getAbilities(game); + sourceControllerId = ((Card) attacker).getOwnerId(); + } else if (attacker instanceof CommandObject) { + sourceControllerId = ((CommandObject) attacker).getControllerId(); + sourceAbilities = attacker.getAbilities(); + } else { + attacker = null; + } + } else { + sourceAbilities = ((Permanent) attacker).getAbilities(game); + sourceControllerId = ((Permanent) attacker).getControllerId(); + } + if (attacker != null && sourceAbilities != null) { + if (sourceAbilities.containsKey(LifelinkAbility.getInstance().getId())) { + if (markDamage) { + game.getPermanent(attackerId).markLifelink(damageDone); } else { - game.informPlayers(attacker.getLogName() + " deals " + damageDone + " damage to " + getLogName()); + Player player = game.getPlayer(sourceControllerId); + player.gainLife(damageDone, game, source); } } + if (sourceAbilities.containsKey(DeathtouchAbility.getInstance().getId())) { + deathtouched = true; + } + if (dealtDamageByThisTurn == null) { + dealtDamageByThisTurn = new HashSet<>(); + } + // Unstable ability - Earl of Squirrel + if (sourceAbilities.containsKey(SquirrellinkAbility.getInstance().getId())) { + Player player = game.getPlayer(sourceControllerId); + new SquirrelToken().putOntoBattlefield(damageDone, game, source, player.getId()); + } + dealtDamageByThisTurn.add(new MageObjectReference(attacker, game)); + } + if (attacker == null) { + game.informPlayers(getLogName() + " gets " + damageDone + " damage"); + } else { + game.informPlayers(attacker.getLogName() + " deals " + damageDone + " damage to " + getLogName()); } return damageDone; } + private static boolean checkWither(DamageEvent event, MageObject attacker, Game game) { + if (event.isAsThoughWither() || event.isAsThoughInfect()) { + return true; + } + if (attacker == null) { + return false; + } + Abilities abilities; + if (attacker instanceof Card) { + abilities = ((Card) attacker).getAbilities(game); + } else { + abilities = attacker.getAbilities(); + } + return abilities.containsKey(InfectAbility.getInstance().getId()) + || abilities.containsKey(WitherAbility.getInstance().getId()); + } + @Override public void markLifelink(int damage) { markedLifelink += damage; @@ -960,79 +1018,44 @@ public int applyDamage(Game game) { } else if (mdi.sourceObject instanceof Permanent) { source = ((Permanent) mdi.sourceObject).getSpellAbility(); } - addCounters(mdi.counter, game.getControllerId(mdi.sourceObject.getId()), source, game); + if (mdi.addCounters) { + addCounters(mdi.counter, game.getControllerId(mdi.sourceObject.getId()), source, game); + } else { + removeCounters(mdi.counter, source, game); + } } markedDamage.clear(); return 0; } @Override - public void removeAllDamage(Game game) { - damage = 0; - deathtouched = false; - } - - protected int damagePlaneswalker(int damage, UUID attackerId, Ability source, Game game, boolean preventable, boolean combat, boolean markDamage, List appliedEffects) { - GameEvent event = new DamagePlaneswalkerEvent(objectId, attackerId, controllerId, damage, preventable, combat); - event.setAppliedEffects(appliedEffects); - if (game.replaceEvent(event)) { - return 0; + public int getLethalDamage(UUID attackerId, Game game) { + int lethal = Integer.MAX_VALUE; + if (this.isCreature()) { + if (game.getState().getActivePowerInsteadOfToughnessForDamageLethalityFilters().stream().anyMatch(f -> f.match(this, game))) { + lethal = Math.min(lethal, power.getValue()); + } else { + lethal = Math.min(lethal, toughness.getValue()); + } + lethal = Math.max(lethal - this.damage, 0); + Card attacker = game.getPermanent(attackerId); + if (attacker == null) { + attacker = game.getCard(attackerId); + } + if (attacker != null && attacker.getAbilities(game).containsKey(DeathtouchAbility.getInstance().getId())) { + lethal = Math.min(1, lethal); + } } - int actualDamage = checkProtectionAbilities(event, attackerId, source, game); - if (actualDamage <= 0) { - return 0; + if (this.isPlaneswalker()) { + lethal = Math.min(lethal, this.getCounters(game).getCount(CounterType.LOYALTY)); } - int countersToRemove = Math.min(actualDamage, getCounters(game).getCount(CounterType.LOYALTY)); - removeCounters(CounterType.LOYALTY.getName(), countersToRemove, source, game); - DamagedEvent damagedEvent = new DamagedPlaneswalkerEvent(objectId, attackerId, controllerId, actualDamage, combat); - damagedEvent.setExcess(actualDamage - countersToRemove); - game.fireEvent(damagedEvent); - game.getState().addSimultaneousDamage(damagedEvent, game); - return actualDamage; + return lethal; } - protected int damageCreature(int damage, UUID attackerId, Ability source, Game game, boolean preventable, boolean combat, boolean markDamage, List appliedEffects) { - GameEvent event = new DamageCreatureEvent(this.getId(), attackerId, this.getControllerId(), damage, preventable, combat); - event.setAppliedEffects(appliedEffects); - if (game.replaceEvent(event)) { - return 0; - } - int actualDamage = checkProtectionAbilities(event, attackerId, source, game); - if (actualDamage <= 0) { - return 0; - } - MageObject attacker = game.getObject(attackerId); - int lethal = 0; - if (game.getState().getActivePowerInsteadOfToughnessForDamageLethalityFilters().stream().anyMatch(f -> f.match(this, game))) { - lethal = power.getValue(); - } else { - lethal = toughness.getValue(); - } - lethal = Math.max(lethal - this.damage, 0); - if (attacker.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) { - lethal = Math.min(1, lethal); - } - if (attacker != null && (attacker.getAbilities().containsKey(InfectAbility.getInstance().getId()) - || attacker.getAbilities().containsKey(WitherAbility.getInstance().getId()))) { - if (markDamage) { - // mark damage only - markDamage(CounterType.M1M1.createInstance(actualDamage), attacker); - } else { - Ability damageSourceAbility = null; - if (attacker instanceof Permanent) { - damageSourceAbility = ((Permanent) attacker).getSpellAbility(); - } - // deal damage immediately - addCounters(CounterType.M1M1.createInstance(actualDamage), game.getControllerId(attackerId), damageSourceAbility, game); - } - } else { - this.damage = CardUtil.overflowInc(this.damage, actualDamage); - } - DamagedEvent damagedEvent = new DamagedCreatureEvent(this.getId(), attackerId, this.getControllerId(), actualDamage, combat); - damagedEvent.setExcess(actualDamage - lethal); - game.fireEvent(damagedEvent); - game.getState().addSimultaneousDamage(damagedEvent, game); - return actualDamage; + @Override + public void removeAllDamage(Game game) { + damage = 0; + deathtouched = false; } private int checkProtectionAbilities(GameEvent event, UUID attackerId, Ability source, Game game) { @@ -1049,11 +1072,11 @@ private int checkProtectionAbilities(GameEvent event, UUID attackerId, Ability s return event.getAmount(); } - private void markDamage(Counter counter, MageObject source) { + private void markDamage(Counter counter, MageObject source, boolean addCounters) { if (markedDamage == null) { markedDamage = new ArrayList<>(); } - markedDamage.add(new MarkedDamageInfo(counter, source)); + markedDamage.add(new MarkedDamageInfo(counter, source, addCounters)); } @Override diff --git a/Mage/src/main/java/mage/game/permanent/PermanentMeld.java b/Mage/src/main/java/mage/game/permanent/PermanentMeld.java index 6b2728d1404d..1c7dff31aef9 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentMeld.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentMeld.java @@ -16,11 +16,11 @@ public PermanentMeld(Card card, UUID controllerId, Game game) { } @Override - public int getConvertedManaCost() { + public int getManaValue() { if (this.isCopy()) { return 0; } else { - return this.getCard().getConvertedManaCost(); + return this.getCard().getManaValue(); } } } diff --git a/Mage/src/main/java/mage/game/permanent/PermanentToken.java b/Mage/src/main/java/mage/game/permanent/PermanentToken.java index a0d755bec9ea..49c6294e9ca4 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentToken.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentToken.java @@ -102,24 +102,6 @@ public PermanentToken copy() { return new PermanentToken(this); } - @Override - public void adjustTargets(Ability ability, Game game) { - if (getToken().getCopySourceCard() != null) { - getToken().getCopySourceCard().adjustTargets(ability, game); - } else { - super.adjustTargets(ability, game); - } - } - - @Override - public void adjustCosts(Ability ability, Game game) { - if (getToken().getCopySourceCard() != null) { - getToken().getCopySourceCard().adjustCosts(ability, game); - } else { - super.adjustCosts(ability, game); - } - } - @Override public void updateZoneChangeCounter(Game game, ZoneChangeEvent event) { // token must change zcc on enters to battlefield (like cards do with stack), diff --git a/Mage/src/main/java/mage/game/permanent/token/AssemblyWorkerToken.java b/Mage/src/main/java/mage/game/permanent/token/AssemblyWorkerToken.java index 9376200c6d88..dfb513411a05 100644 --- a/Mage/src/main/java/mage/game/permanent/token/AssemblyWorkerToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/AssemblyWorkerToken.java @@ -5,6 +5,8 @@ import mage.constants.SubType; import mage.MageInt; +import java.util.Arrays; + /** * * @author spjspj @@ -12,12 +14,15 @@ public final class AssemblyWorkerToken extends TokenImpl { public AssemblyWorkerToken() { - super("Assembly-Worker", "2/2 Assembly-Worker artifact creature"); + super("Assembly-Worker", "2/2 colorless Assembly-Worker artifact creature token"); cardType.add(CardType.ARTIFACT); cardType.add(CardType.CREATURE); - this.subtype.add(SubType.ASSEMBLY_WORKER); + subtype.add(SubType.ASSEMBLY_WORKER); + power = new MageInt(2); toughness = new MageInt(2); + + availableImageSetCodes = Arrays.asList("4ED", "ATQ", "DDF", "EMA", "MED", "TSR"); } public AssemblyWorkerToken(final AssemblyWorkerToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/BearToken.java b/Mage/src/main/java/mage/game/permanent/token/BearToken.java index 8740baa4f120..aaaf7313f9e6 100644 --- a/Mage/src/main/java/mage/game/permanent/token/BearToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/BearToken.java @@ -19,7 +19,7 @@ public BearToken() { power = new MageInt(2); toughness = new MageInt(2); - availableImageSetCodes = Arrays.asList("C15", "JUD", "LGN", "ODY", "ONS", "VMA", "MH1", "ELD", "KHM"); + availableImageSetCodes = Arrays.asList("C15", "JUD", "ODY", "ONS", "VMA", "MH1", "ELD", "KHM"); } public BearToken(final BearToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/BelzenlokDemonToken.java b/Mage/src/main/java/mage/game/permanent/token/BelzenlokDemonToken.java index 50e8f0c15005..7965e2b21b5c 100644 --- a/Mage/src/main/java/mage/game/permanent/token/BelzenlokDemonToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/BelzenlokDemonToken.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.players.Player; diff --git a/Mage/src/main/java/mage/game/permanent/token/BloodAvatarToken.java b/Mage/src/main/java/mage/game/permanent/token/BloodAvatarToken.java new file mode 100644 index 000000000000..ae42520f5709 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/BloodAvatarToken.java @@ -0,0 +1,38 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.keyword.HasteAbility; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; + +/** + * @author TheElk801 + */ +public final class BloodAvatarToken extends TokenImpl { + + public BloodAvatarToken() { + super("Avatar", "3/6 black and red Avatar creature token with haste and \"Whenever this creature attacks, it deals 3 damage to each opponent.\""); + cardType.add(CardType.CREATURE); + color.setBlack(true); + color.setRed(true); + subtype.add(SubType.AVATAR); + power = new MageInt(3); + toughness = new MageInt(6); + addAbility(HasteAbility.getInstance()); + addAbility(new AttacksTriggeredAbility( + new DamagePlayersEffect(3, TargetController.OPPONENT), false, + "Whenever this creature attacks, it deals 3 damage to each opponent." + )); + } + + private BloodAvatarToken(final BloodAvatarToken token) { + super(token); + } + + public BloodAvatarToken copy() { + return new BloodAvatarToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/CloudSpriteToken.java b/Mage/src/main/java/mage/game/permanent/token/CloudSpriteToken.java index ecf5ef1b3b95..4794f24207d3 100644 --- a/Mage/src/main/java/mage/game/permanent/token/CloudSpriteToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/CloudSpriteToken.java @@ -7,6 +7,8 @@ import mage.abilities.common.CanBlockOnlyFlyingAbility; import mage.abilities.keyword.FlyingAbility; +import java.util.Arrays; + /** * * @author spjspj @@ -14,16 +16,20 @@ public final class CloudSpriteToken extends TokenImpl { public CloudSpriteToken() { - super("Cloud Sprite", "1/1 blue faerie creature token named Cloud Sprite with flying and \"Cloud Sprite can block only creatures with flying.\""); - this.setOriginalExpansionSetCode("FUT"); + super("Cloud Sprite", "1/1 blue Faerie creature token named Cloud Sprite. It has flying and \"Cloud Sprite can block only creatures with flying.\""); cardType.add(CardType.CREATURE); color.setBlue(true); subtype.add(SubType.FAERIE); power = new MageInt(1); toughness = new MageInt(1); + // Flying this.addAbility(FlyingAbility.getInstance()); + + // Cloud Sprite can block only creatures with flying. this.addAbility(new CanBlockOnlyFlyingAbility()); + + availableImageSetCodes = Arrays.asList("TSR"); } public CloudSpriteToken(final CloudSpriteToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/DeadlyGrubToken.java b/Mage/src/main/java/mage/game/permanent/token/DeadlyGrubInsectToken.java similarity index 59% rename from Mage/src/main/java/mage/game/permanent/token/DeadlyGrubToken.java rename to Mage/src/main/java/mage/game/permanent/token/DeadlyGrubInsectToken.java index bb64e98d0813..479718031b92 100644 --- a/Mage/src/main/java/mage/game/permanent/token/DeadlyGrubToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/DeadlyGrubInsectToken.java @@ -6,27 +6,33 @@ import mage.MageInt; import mage.abilities.keyword.ShroudAbility; +import java.util.Arrays; + /** * * @author spjspj */ -public final class DeadlyGrubToken extends TokenImpl { +public final class DeadlyGrubInsectToken extends TokenImpl { - public DeadlyGrubToken() { + public DeadlyGrubInsectToken() { super("Insect", "6/1 green Insect creature token with shroud"); cardType.add(CardType.CREATURE); color.setGreen(true); subtype.add(SubType.INSECT); power = new MageInt(6); toughness = new MageInt(1); + + // Shroud this.addAbility(ShroudAbility.getInstance()); + + availableImageSetCodes = Arrays.asList("PLC", "TSR"); } - public DeadlyGrubToken(final DeadlyGrubToken token) { + public DeadlyGrubInsectToken(final DeadlyGrubInsectToken token) { super(token); } - public DeadlyGrubToken copy() { - return new DeadlyGrubToken(this); + public DeadlyGrubInsectToken copy() { + return new DeadlyGrubInsectToken(this); } } diff --git a/Mage/src/main/java/mage/game/permanent/token/FreyaliseLlanowarsFuryToken.java b/Mage/src/main/java/mage/game/permanent/token/ElfDruidToken.java similarity index 51% rename from Mage/src/main/java/mage/game/permanent/token/FreyaliseLlanowarsFuryToken.java rename to Mage/src/main/java/mage/game/permanent/token/ElfDruidToken.java index 2f2c3b25ed63..38c925dbeb79 100644 --- a/Mage/src/main/java/mage/game/permanent/token/FreyaliseLlanowarsFuryToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/ElfDruidToken.java @@ -1,9 +1,8 @@ package mage.game.permanent.token; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; + import mage.constants.CardType; import mage.constants.SubType; import mage.MageInt; @@ -14,26 +13,18 @@ * * @author spjspj */ -public final class FreyaliseLlanowarsFuryToken extends TokenImpl { - - static final private List tokenImageSets = new ArrayList<>(); +public final class ElfDruidToken extends TokenImpl { - static { - tokenImageSets.addAll(Arrays.asList("C14", "CMA")); - } - - public FreyaliseLlanowarsFuryToken() { + public ElfDruidToken() { this(null, 0); } - public FreyaliseLlanowarsFuryToken(String setCode) { + public ElfDruidToken(String setCode) { this(setCode, 0); } - public FreyaliseLlanowarsFuryToken(String setCode, int tokenType) { + public ElfDruidToken(String setCode, int tokenType) { super("Elf Druid", "1/1 green Elf Druid creature token with \"{T}: Add {G}.\""); - availableImageSetCodes = tokenImageSets; - setOriginalExpansionSetCode(setCode); this.cardType.add(CardType.CREATURE); this.color = ObjectColor.GREEN; this.subtype.add(SubType.ELF); @@ -44,13 +35,15 @@ public FreyaliseLlanowarsFuryToken(String setCode, int tokenType) { // {T}: Add {G}. this.addAbility(new GreenManaAbility()); + + availableImageSetCodes = Arrays.asList("C14"); } - public FreyaliseLlanowarsFuryToken(final FreyaliseLlanowarsFuryToken token) { + public ElfDruidToken(final ElfDruidToken token) { super(token); } - public FreyaliseLlanowarsFuryToken copy() { - return new FreyaliseLlanowarsFuryToken(this); + public ElfDruidToken copy() { + return new ElfDruidToken(this); } } diff --git a/Mage/src/main/java/mage/game/permanent/token/FesteringGoblinToken.java b/Mage/src/main/java/mage/game/permanent/token/FesteringGoblinToken.java index 5629aad50c6d..6f85ef6a2c92 100644 --- a/Mage/src/main/java/mage/game/permanent/token/FesteringGoblinToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/FesteringGoblinToken.java @@ -18,7 +18,6 @@ public final class FesteringGoblinToken extends TokenImpl { public FesteringGoblinToken() { super("Festering Goblin", "1/1 black Zombie Goblin creature token named Festering Goblin with \"When Festering Goblin dies, target creature gets -1/-1 until end of turn.\""); - this.setOriginalExpansionSetCode("FUT"); cardType.add(CardType.CREATURE); color.setBlack(true); subtype.add(SubType.ZOMBIE); diff --git a/Mage/src/main/java/mage/game/permanent/token/FungusBeastToken.java b/Mage/src/main/java/mage/game/permanent/token/FungusBeastToken.java new file mode 100644 index 000000000000..822495d0aff1 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/FungusBeastToken.java @@ -0,0 +1,31 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.TrampleAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class FungusBeastToken extends TokenImpl { + + public FungusBeastToken() { + super("Fungus Beast", "4/4 green Fungus Beast creature token with trample"); + cardType.add(CardType.CREATURE); + color.setGreen(true); + subtype.add(SubType.FUNGUS); + subtype.add(SubType.BEAST); + power = new MageInt(4); + toughness = new MageInt(4); + addAbility(TrampleAbility.getInstance()); + } + + private FungusBeastToken(final FungusBeastToken token) { + super(token); + } + + public FungusBeastToken copy() { + return new FungusBeastToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/GiantToken.java b/Mage/src/main/java/mage/game/permanent/token/GiantToken.java index f33b82b956fa..1101b1d6a936 100644 --- a/Mage/src/main/java/mage/game/permanent/token/GiantToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/GiantToken.java @@ -5,6 +5,8 @@ import mage.constants.SubType; import mage.MageInt; +import java.util.Arrays; + /** * * @author spjspj @@ -18,6 +20,8 @@ public GiantToken() { color.setRed(true); power = new MageInt(4); toughness = new MageInt(4); + + availableImageSetCodes = Arrays.asList("TSR"); } public GiantToken(final GiantToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/GoblinToken.java b/Mage/src/main/java/mage/game/permanent/token/GoblinToken.java index 5eae26f2d386..8ee53c5be3d2 100644 --- a/Mage/src/main/java/mage/game/permanent/token/GoblinToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/GoblinToken.java @@ -14,16 +14,10 @@ */ public final class GoblinToken extends TokenImpl { - static final private List tokenImageSets = new ArrayList<>(); - - static { - tokenImageSets.addAll(Arrays.asList("10E", "ALA", "SOM", "M10", "NPH", "M13", "RTR", - "MMA", "M15", "C14", "KTK", "EVG", "DTK", "ORI", "DDG", "DDN", "EVG", "MM2", - "MM3", "EMA", "C16", "DOM", "ANA", "RNA", "WAR", "MH1")); - } - public GoblinToken(boolean withHaste) { this(); + + // token image don't have haste info so it's ok to use same class for different versions if (withHaste) { addAbility(HasteAbility.getInstance()); this.description = "1/1 red Goblin creature token with haste"; @@ -31,22 +25,16 @@ public GoblinToken(boolean withHaste) { } public GoblinToken() { - this(null, 0); - } - - public GoblinToken(String setCode) { - this(setCode, 0); - } - - public GoblinToken(String setCode, int tokenType) { super("Goblin", "1/1 red Goblin creature token"); - availableImageSetCodes = tokenImageSets; - setOriginalExpansionSetCode(setCode); cardType.add(CardType.CREATURE); subtype.add(SubType.GOBLIN); color.setRed(true); power = new MageInt(1); toughness = new MageInt(1); + + availableImageSetCodes = Arrays.asList("10E", "ALA", "SOM", "M10", "NPH", "M13", "RTR", + "MMA", "M15", "C14", "KTK", "EVG", "DTK", "ORI", "DDG", "DDN", "EVG", "MM2", + "MM3", "EMA", "C16", "DOM", "ANA", "RNA", "WAR", "MH1", "TSR"); } public GoblinToken(final GoblinToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/GoldForgeGarrisonGolemToken.java b/Mage/src/main/java/mage/game/permanent/token/GoldForgeGarrisonGolemToken.java index bf2398fdc259..fcdc4d081da1 100644 --- a/Mage/src/main/java/mage/game/permanent/token/GoldForgeGarrisonGolemToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/GoldForgeGarrisonGolemToken.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.game.permanent.token; import mage.MageInt; diff --git a/Mage/src/main/java/mage/game/permanent/token/GoldmeadowHarrierToken.java b/Mage/src/main/java/mage/game/permanent/token/GoldmeadowHarrierToken.java index 14dcc4203eb3..835566acf1d8 100644 --- a/Mage/src/main/java/mage/game/permanent/token/GoldmeadowHarrierToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/GoldmeadowHarrierToken.java @@ -20,7 +20,6 @@ public final class GoldmeadowHarrierToken extends TokenImpl { public GoldmeadowHarrierToken() { super("Goldmeadow Harrier", "1/1 white Kithkin Soldier creature token named Goldmeadow Harrier with \"{W}, {T}: Tap target creature.\""); - this.setOriginalExpansionSetCode("FUT"); cardType.add(CardType.CREATURE); color.setWhite(true); subtype.add(SubType.KITHKIN); diff --git a/Mage/src/main/java/mage/game/permanent/token/GolemFlyingToken.java b/Mage/src/main/java/mage/game/permanent/token/GolemFlyingToken.java new file mode 100644 index 000000000000..61574d3fae31 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/GolemFlyingToken.java @@ -0,0 +1,30 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.FlyingAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class GolemFlyingToken extends TokenImpl { + + public GolemFlyingToken() { + super("Golem", "3/3 colorless Golem artifact creature token with flying"); + cardType.add(CardType.ARTIFACT); + cardType.add(CardType.CREATURE); + subtype.add(SubType.GOLEM); + power = new MageInt(3); + toughness = new MageInt(3); + addAbility(FlyingAbility.getInstance()); + } + + private GolemFlyingToken(final GolemFlyingToken token) { + super(token); + } + + public GolemFlyingToken copy() { + return new GolemFlyingToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/GolemTrampleToken.java b/Mage/src/main/java/mage/game/permanent/token/GolemTrampleToken.java new file mode 100644 index 000000000000..30ff351e522c --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/GolemTrampleToken.java @@ -0,0 +1,30 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.TrampleAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class GolemTrampleToken extends TokenImpl { + + public GolemTrampleToken() { + super("Golem", "3/3 colorless Golem artifact creature token with trample"); + cardType.add(CardType.ARTIFACT); + cardType.add(CardType.CREATURE); + subtype.add(SubType.GOLEM); + power = new MageInt(3); + toughness = new MageInt(3); + addAbility(TrampleAbility.getInstance()); + } + + private GolemTrampleToken(final GolemTrampleToken token) { + super(token); + } + + public GolemTrampleToken copy() { + return new GolemTrampleToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/GolemVigilanceToken.java b/Mage/src/main/java/mage/game/permanent/token/GolemVigilanceToken.java new file mode 100644 index 000000000000..50285e1e221c --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/GolemVigilanceToken.java @@ -0,0 +1,30 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.VigilanceAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class GolemVigilanceToken extends TokenImpl { + + public GolemVigilanceToken() { + super("Golem", "3/3 colorless Golem artifact creature token with vigilance"); + cardType.add(CardType.ARTIFACT); + cardType.add(CardType.CREATURE); + subtype.add(SubType.GOLEM); + power = new MageInt(3); + toughness = new MageInt(3); + addAbility(VigilanceAbility.getInstance()); + } + + private GolemVigilanceToken(final GolemVigilanceToken token) { + super(token); + } + + public GolemVigilanceToken copy() { + return new GolemVigilanceToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/GriffinToken.java b/Mage/src/main/java/mage/game/permanent/token/GriffinToken.java index 4914ca065bab..0fae4a810227 100644 --- a/Mage/src/main/java/mage/game/permanent/token/GriffinToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/GriffinToken.java @@ -19,9 +19,11 @@ public GriffinToken() { subtype.add(SubType.GRIFFIN); power = new MageInt(2); toughness = new MageInt(2); + + // Flying this.addAbility(FlyingAbility.getInstance()); - availableImageSetCodes.addAll(Arrays.asList("DDG", "DDH", "DDL", "TSP", "M21")); + availableImageSetCodes = Arrays.asList("DDG", "DDH", "DDL", "TSP", "M21", "TSR"); } public GriffinToken(final GriffinToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/KherKeepKoboldToken.java b/Mage/src/main/java/mage/game/permanent/token/KherKeepKoboldToken.java index 9a247da94ead..c2b71ce24aad 100644 --- a/Mage/src/main/java/mage/game/permanent/token/KherKeepKoboldToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/KherKeepKoboldToken.java @@ -5,6 +5,8 @@ import mage.constants.SubType; import mage.MageInt; +import java.util.Arrays; + /** * * @author spjspj @@ -18,6 +20,8 @@ public KherKeepKoboldToken() { subtype.add(SubType.KOBOLD); power = new MageInt(0); toughness = new MageInt(1); + + availableImageSetCodes = Arrays.asList("TSR"); } public KherKeepKoboldToken(final KherKeepKoboldToken token) { super(token); diff --git a/Mage/src/main/java/mage/game/permanent/token/KithkinSoldierToken.java b/Mage/src/main/java/mage/game/permanent/token/KithkinSoldierToken.java index 7520803743c5..4c199dc9e664 100644 --- a/Mage/src/main/java/mage/game/permanent/token/KithkinSoldierToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/KithkinSoldierToken.java @@ -20,7 +20,7 @@ public KithkinSoldierToken() { power = new MageInt(1); toughness = new MageInt(1); - availableImageSetCodes = Arrays.asList("CMD", "EVE", "FUT", "LRW", "MMA", "MOR", "SHM", "MMA", "KHC"); + availableImageSetCodes = Arrays.asList("CMD", "EVE", "LRW", "MMA", "MOR", "SHM", "MMA", "KHC"); } public KithkinSoldierToken(final KithkinSoldierToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/LlanowarElvesToken.java b/Mage/src/main/java/mage/game/permanent/token/LlanowarElvesToken.java index ed4dff01d659..4dc4bd75fb9d 100644 --- a/Mage/src/main/java/mage/game/permanent/token/LlanowarElvesToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/LlanowarElvesToken.java @@ -1,11 +1,12 @@ - - package mage.game.permanent.token; + import mage.constants.CardType; import mage.constants.SubType; import mage.MageInt; import mage.abilities.mana.GreenManaAbility; +import java.util.Arrays; + /** * * @author spjspj @@ -13,8 +14,7 @@ public final class LlanowarElvesToken extends TokenImpl { public LlanowarElvesToken() { - super("Llanowar Elves", "1/1 green Elf Druid creature token named Llanowar Elves with \"{T}: Add {G}.\""); - this.setOriginalExpansionSetCode("FUT"); + super("Llanowar Elves", "1/1 green Elf Druid creature token named Llanowar Elves. It has \"{T}: Add {G}.\""); cardType.add(CardType.CREATURE); color.setGreen(true); subtype.add(SubType.ELF); @@ -22,7 +22,10 @@ public LlanowarElvesToken() { power = new MageInt(1); toughness = new MageInt(1); + // {T}: Add {G}. this.addAbility(new GreenManaAbility()); + + availableImageSetCodes = Arrays.asList("TSR"); } public LlanowarElvesToken(final LlanowarElvesToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/LoreholdToken.java b/Mage/src/main/java/mage/game/permanent/token/LoreholdToken.java new file mode 100644 index 000000000000..90b354e65084 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/LoreholdToken.java @@ -0,0 +1,30 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class LoreholdToken extends TokenImpl { + + public LoreholdToken() { + super("Spirit", "3/2 red and white Spirit creature token"); + cardType.add(CardType.CREATURE); + color.setRed(true); + color.setWhite(true); + subtype.add(SubType.SPIRIT); + power = new MageInt(3); + toughness = new MageInt(2); + } + + private LoreholdToken(final LoreholdToken token) { + super(token); + } + + @Override + public LoreholdToken copy() { + return new LoreholdToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/MerfolkHexproofToken.java b/Mage/src/main/java/mage/game/permanent/token/MerfolkHexproofToken.java index 3b91c9067f00..debaf39b8a93 100644 --- a/Mage/src/main/java/mage/game/permanent/token/MerfolkHexproofToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/MerfolkHexproofToken.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.game.permanent.token; import mage.MageInt; diff --git a/Mage/src/main/java/mage/game/permanent/token/MetallicSliverToken.java b/Mage/src/main/java/mage/game/permanent/token/MetallicSliverToken.java new file mode 100644 index 000000000000..7ee8dfc3652b --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/MetallicSliverToken.java @@ -0,0 +1,34 @@ + + +package mage.game.permanent.token; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.MageInt; + +import java.util.Arrays; + +/** + * + * @author spjspj + */ +public final class MetallicSliverToken extends TokenImpl { + + public MetallicSliverToken() { + super("Metallic Sliver", "1/1 colorless Sliver artifact creature token named Metallic Sliver"); + cardType.add(CardType.CREATURE); + cardType.add(CardType.ARTIFACT); + subtype.add(SubType.SLIVER); + power = new MageInt(1); + toughness = new MageInt(1); + + availableImageSetCodes = Arrays.asList("TSR"); + } + + public MetallicSliverToken(final MetallicSliverToken token) { + super(token); + } + + public MetallicSliverToken copy() { + return new MetallicSliverToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/PegasusToken.java b/Mage/src/main/java/mage/game/permanent/token/PegasusToken.java index 61f32a87db31..119fee8afe2d 100644 --- a/Mage/src/main/java/mage/game/permanent/token/PegasusToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/PegasusToken.java @@ -22,7 +22,7 @@ public PegasusToken() { addAbility(FlyingAbility.getInstance()); - availableImageSetCodes = Arrays.asList("C14", "C19", "CMD", "EXO", "GPT", "MIR", "TMP", "TPR", "TSP", "THB", "KHC"); + availableImageSetCodes = Arrays.asList("C14", "C19", "CMD", "EXO", "GPT", "MIR", "TMP", "TSP", "THB", "KHC"); } public PegasusToken(final PegasusToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/PenumbraSpiderToken.java b/Mage/src/main/java/mage/game/permanent/token/PenumbraSpiderToken.java index 80340d7db29f..60fd2885593d 100644 --- a/Mage/src/main/java/mage/game/permanent/token/PenumbraSpiderToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/PenumbraSpiderToken.java @@ -1,4 +1,3 @@ - package mage.game.permanent.token; import mage.constants.CardType; @@ -6,6 +5,8 @@ import mage.MageInt; import mage.abilities.keyword.ReachAbility; +import java.util.Arrays; + /** * * @author spjspj @@ -20,7 +21,10 @@ public PenumbraSpiderToken() { subtype.add(SubType.SPIDER); power = new MageInt(2); toughness = new MageInt(4); + addAbility(ReachAbility.getInstance()); + + availableImageSetCodes = Arrays.asList("MMA", "PC2", "TSP", "TSR"); } public PenumbraSpiderToken(final PenumbraSpiderToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/PhyrexianRebirthHorrorToken.java b/Mage/src/main/java/mage/game/permanent/token/PhyrexianRebirthHorrorToken.java index ffafe4ca4e57..ef7fbf537d34 100644 --- a/Mage/src/main/java/mage/game/permanent/token/PhyrexianRebirthHorrorToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/PhyrexianRebirthHorrorToken.java @@ -11,13 +11,13 @@ */ public final class PhyrexianRebirthHorrorToken extends TokenImpl { - public PhyrexianRebirthHorrorToken() { + public PhyrexianRebirthHorrorToken(int power, int toughness) { super("Horror", "X/X colorless Horror artifact creature token"); - cardType.add(CardType.ARTIFACT); - cardType.add(CardType.CREATURE); - subtype.add(SubType.HORROR); - power = new MageInt(0); - toughness = new MageInt(0); + this.cardType.add(CardType.ARTIFACT); + this.cardType.add(CardType.CREATURE); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(power); + this.toughness = new MageInt(toughness); availableImageSetCodes = Arrays.asList("C18", "C19", "MBS", "CMR"); } diff --git a/Mage/src/main/java/mage/game/permanent/token/PongifyApeToken.java b/Mage/src/main/java/mage/game/permanent/token/PongifyApeToken.java index 377fbc706a81..16a31ed60a8d 100644 --- a/Mage/src/main/java/mage/game/permanent/token/PongifyApeToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/PongifyApeToken.java @@ -5,6 +5,8 @@ import mage.constants.SubType; import mage.MageInt; +import java.util.Arrays; + /** * * @author spjspj @@ -18,6 +20,8 @@ public PongifyApeToken() { subtype.add(SubType.APE); power = new MageInt(3); toughness = new MageInt(3); + + availableImageSetCodes = Arrays.asList("10E", "C14", "GVL", "DDD", "DDG", "PLC", "ULG", "UNH", "TSR"); } public PongifyApeToken(final PongifyApeToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/PrismariToken.java b/Mage/src/main/java/mage/game/permanent/token/PrismariToken.java new file mode 100644 index 000000000000..90faca6bc200 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/PrismariToken.java @@ -0,0 +1,30 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class PrismariToken extends TokenImpl { + + public PrismariToken() { + super("Elemental", "4/4 blue and red Elemental creature token"); + cardType.add(CardType.CREATURE); + color.setBlue(true); + color.setRed(true); + subtype.add(SubType.ELEMENTAL); + power = new MageInt(4); + toughness = new MageInt(4); + } + + private PrismariToken(final PrismariToken token) { + super(token); + } + + @Override + public PrismariToken copy() { + return new PrismariToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/QuandrixToken.java b/Mage/src/main/java/mage/game/permanent/token/QuandrixToken.java new file mode 100644 index 000000000000..88a3b218ea65 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/QuandrixToken.java @@ -0,0 +1,85 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.OneShotEffect; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.Arrays; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class QuandrixToken extends TokenImpl { + + public QuandrixToken() { + super("Fractal", "0/0 green and blue Fractal creature token"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.FRACTAL); + color.setGreen(true); + color.setBlue(true); + power = new MageInt(0); + toughness = new MageInt(0); + + availableImageSetCodes = Arrays.asList("STX"); + } + + private QuandrixToken(final QuandrixToken token) { + super(token); + } + + public QuandrixToken copy() { + return new QuandrixToken(this); + } + + public static QuandrixTokenEffect getEffect(DynamicValue xValue, String text) { + return new QuandrixTokenEffect(xValue, text); + } + + private static final class QuandrixTokenEffect extends OneShotEffect { + + private final DynamicValue xValue; + + private QuandrixTokenEffect(DynamicValue xValue, String text) { + super(Outcome.Benefit); + this.xValue = xValue; + this.staticText = "create a 0/0 green and blue Fractal creature token. " + text; + } + + private QuandrixTokenEffect(final QuandrixTokenEffect effect) { + super(effect); + this.xValue = effect.xValue; + } + + @Override + public QuandrixTokenEffect copy() { + return new QuandrixTokenEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Token token = new QuandrixToken(); + token.putOntoBattlefield(1, game, source, source.getControllerId()); + int value = xValue.calculate(game, source, this); + if (value < 1) { + return true; + } + for (UUID tokenId : token.getLastAddedTokenIds()) { + Permanent permanent = game.getPermanent(tokenId); + if (permanent == null) { + continue; + } + permanent.addCounters(CounterType.P1P1.createInstance(value), source.getControllerId(), source, game); + } + return true; + } + } + +} diff --git a/Mage/src/main/java/mage/game/permanent/token/RatToken.java b/Mage/src/main/java/mage/game/permanent/token/RatToken.java index 372b8e1eb5b8..460967726db0 100644 --- a/Mage/src/main/java/mage/game/permanent/token/RatToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/RatToken.java @@ -19,7 +19,7 @@ public RatToken() { power = new MageInt(1); toughness = new MageInt(1); - availableImageSetCodes = Arrays.asList("C17", "CHK", "GTC", "SHM", "STH", "TPR", "ELD", "ZNC"); + availableImageSetCodes = Arrays.asList("C17", "CHK", "GTC", "SHM", "ELD", "ZNC"); } public RatToken(final RatToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/ReflectionPureToken.java b/Mage/src/main/java/mage/game/permanent/token/ReflectionPureToken.java index f77b3997040e..15ed5bebd339 100644 --- a/Mage/src/main/java/mage/game/permanent/token/ReflectionPureToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/ReflectionPureToken.java @@ -16,7 +16,7 @@ public ReflectionPureToken() { } public ReflectionPureToken(int xValue) { - super("Reflection", "X/X white Reflection creature token, where X is the converted mana cost of that spell"); + super("Reflection", "X/X white Reflection creature token, where X is the mana value of that spell"); this.setOriginalExpansionSetCode("INV"); cardType.add(CardType.CREATURE); color.setWhite(true); diff --git a/Mage/src/main/java/mage/game/permanent/token/RenownedWeaverSpiderToken.java b/Mage/src/main/java/mage/game/permanent/token/RenownedWeaverSpiderToken.java index 4fc671fca7b5..cc91c55382d8 100644 --- a/Mage/src/main/java/mage/game/permanent/token/RenownedWeaverSpiderToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/RenownedWeaverSpiderToken.java @@ -7,6 +7,8 @@ import mage.ObjectColor; import mage.abilities.keyword.ReachAbility; +import java.util.Arrays; + /** * * @author spjspj @@ -15,14 +17,16 @@ public final class RenownedWeaverSpiderToken extends TokenImpl { public RenownedWeaverSpiderToken() { super("Spider", "1/3 green Spider enchantment creature token with reach"); - this.setOriginalExpansionSetCode("JOU"); cardType.add(CardType.ENCHANTMENT); cardType.add(CardType.CREATURE); color.setColor(ObjectColor.GREEN); subtype.add(SubType.SPIDER); power = new MageInt(1); toughness = new MageInt(3); + this.addAbility(ReachAbility.getInstance()); + + availableImageSetCodes = Arrays.asList("JOU"); } public RenownedWeaverSpiderToken(final RenownedWeaverSpiderToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/RiftmarkedKnightToken.java b/Mage/src/main/java/mage/game/permanent/token/RiftmarkedKnightToken.java index 59b2e3cb2d6d..28114c002887 100644 --- a/Mage/src/main/java/mage/game/permanent/token/RiftmarkedKnightToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/RiftmarkedKnightToken.java @@ -9,6 +9,8 @@ import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.ProtectionAbility; +import java.util.Arrays; + /** * * @author spjspj @@ -22,9 +24,12 @@ public RiftmarkedKnightToken() { subtype.add(SubType.KNIGHT); power = new MageInt(2); toughness = new MageInt(2); - this.addAbility(ProtectionAbility.from(ObjectColor.WHITE)); + this.addAbility(new FlankingAbility()); + this.addAbility(ProtectionAbility.from(ObjectColor.WHITE)); this.addAbility(HasteAbility.getInstance()); + + availableImageSetCodes = Arrays.asList("PLC", "TSR"); } public RiftmarkedKnightToken(final RiftmarkedKnightToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/SaprolingToken.java b/Mage/src/main/java/mage/game/permanent/token/SaprolingToken.java index 492bc669454c..4c486de7d93f 100644 --- a/Mage/src/main/java/mage/game/permanent/token/SaprolingToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/SaprolingToken.java @@ -44,7 +44,8 @@ public final class SaprolingToken extends TokenImpl { "C20", "M21", "ZNC", - "CMR" + "CMR", + "TSR" )); } diff --git a/Mage/src/main/java/mage/game/permanent/token/SengirNosferatuBatToken.java b/Mage/src/main/java/mage/game/permanent/token/SengirNosferatuBatToken.java index 1a7d90f50efd..409067f34bc0 100644 --- a/Mage/src/main/java/mage/game/permanent/token/SengirNosferatuBatToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/SengirNosferatuBatToken.java @@ -1,9 +1,5 @@ - - package mage.game.permanent.token; -import java.util.UUID; -import mage.constants.CardType; -import mage.constants.SubType; + import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -12,7 +8,9 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.Card; +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; @@ -21,8 +19,9 @@ import mage.target.Target; import mage.target.common.TargetCardInExile; +import java.util.Arrays; + /** - * * @author spjspj */ public final class SengirNosferatuBatToken extends TokenImpl { @@ -34,12 +33,18 @@ public SengirNosferatuBatToken() { subtype.add(SubType.BAT); power = new MageInt(1); toughness = new MageInt(2); + + // Flying this.addAbility(FlyingAbility.getInstance()); + + // {1}{B}, Sacrifice this creature: Return an exiled card named Sengir Nosferatu to the battlefield under its owner’s control. ReturnSengirNosferatuEffect effect = new ReturnSengirNosferatuEffect(); effect.setText("Return an exiled card named Sengir Nosferatu to the battlefield under its owner's control."); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{1}{B}")); ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); + + availableImageSetCodes = Arrays.asList("TSP", "TSR"); } public SengirNosferatuBatToken(final SengirNosferatuBatToken token) { @@ -59,11 +64,11 @@ class ReturnSengirNosferatuEffect extends OneShotEffect { filter.add(new NamePredicate("Sengir Nosferatu")); } - public ReturnSengirNosferatuEffect() { + ReturnSengirNosferatuEffect() { super(Outcome.Benefit); } - public ReturnSengirNosferatuEffect(final ReturnSengirNosferatuEffect effect) { + private ReturnSengirNosferatuEffect(final ReturnSengirNosferatuEffect effect) { super(effect); } @@ -74,20 +79,20 @@ public ReturnSengirNosferatuEffect copy() { @Override public boolean apply(Game game, Ability source) { - UUID controllerId = source.getControllerId(); + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } Target target = new TargetCardInExile(filter); target.setNotTarget(true); - if (!target.canChoose(source.getSourceId(), controllerId, game)) { + if (!target.canChoose(source.getSourceId(), source.getControllerId(), game)) { return false; } - Player player = game.getPlayer(controllerId); - if (player != null) { - player.chooseTarget(Outcome.PutCreatureInPlay, target, source, game); - Card card = game.getCard(target.getTargets().get(0)); - if (card != null) { - return card.moveToZone(Zone.BATTLEFIELD, source, game, false); - } - } - return false; + player.chooseTarget(Outcome.PutCreatureInPlay, target, source, game); + Card card = game.getCard(target.getFirstTarget()); + return card != null && player.moveCards( + card, Zone.BATTLEFIELD, source, game, false, + false, true, null + ); } } diff --git a/Mage/src/main/java/mage/game/permanent/token/SilverquillToken.java b/Mage/src/main/java/mage/game/permanent/token/SilverquillToken.java new file mode 100644 index 000000000000..8e653834becd --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/SilverquillToken.java @@ -0,0 +1,32 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.FlyingAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class SilverquillToken extends TokenImpl { + + public SilverquillToken() { + super("Inkling", "2/1 white and black Inkling creature token with flying"); + cardType.add(CardType.CREATURE); + color.setWhite(true); + color.setBlack(true); + subtype.add(SubType.INKLING); + power = new MageInt(2); + toughness = new MageInt(1); + addAbility(FlyingAbility.getInstance()); + } + + private SilverquillToken(final SilverquillToken token) { + super(token); + } + + @Override + public SilverquillToken copy() { + return new SilverquillToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/SliversmithToken.java b/Mage/src/main/java/mage/game/permanent/token/SliversmithToken.java deleted file mode 100644 index 868a8f5e2f1f..000000000000 --- a/Mage/src/main/java/mage/game/permanent/token/SliversmithToken.java +++ /dev/null @@ -1,31 +0,0 @@ - - -package mage.game.permanent.token; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.MageInt; - -/** - * - * @author spjspj - */ -public final class SliversmithToken extends TokenImpl { - - public SliversmithToken() { - super("Metallic Sliver", "1/1 colorless Sliver creature token named Metallic Sliver"); - cardType.add(CardType.CREATURE); - cardType.add(CardType.ARTIFACT); - subtype.add(SubType.SLIVER); - power = new MageInt(1); - toughness = new MageInt(1); - this.setOriginalExpansionSetCode("FUT"); - } - - public SliversmithToken(final SliversmithToken token) { - super(token); - } - - public SliversmithToken copy() { - return new SliversmithToken(this); - } -} diff --git a/Mage/src/main/java/mage/game/permanent/token/SoldierToken.java b/Mage/src/main/java/mage/game/permanent/token/SoldierToken.java index 14a288dcd525..3c80ef10b36f 100644 --- a/Mage/src/main/java/mage/game/permanent/token/SoldierToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/SoldierToken.java @@ -20,8 +20,9 @@ public SoldierToken() { power = new MageInt(1); toughness = new MageInt(1); - availableImageSetCodes = Arrays.asList("10E", "M15", "C14", "ORI", "ALA", "DDF", "THS", "M12", "M13", "MM2", "MMA", "RTR", - "SOM", "DDO", "M10", "ORI", "EMN", "EMA", "CN2", "C16", "MM3", "E01", "DOM", "MH1", "M20", "C20", "M21", "CMR", "KHC"); + availableImageSetCodes = Arrays.asList("10E", "M15", "C14", "ORI", "ALA", "DDF", "THS", "M12", "M13", + "MM2", "MMA", "RTR", "SOM", "DDO", "M10", "ORI", "EMN", "EMA", "CN2", "C16", "MM3", "E01", + "DOM", "MH1", "M20", "C20", "M21", "CMR", "KHC", "TSR"); } public SoldierToken(final SoldierToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/SparkElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/SparkElementalToken.java index 29b6b565e294..1cc0a5189061 100644 --- a/Mage/src/main/java/mage/game/permanent/token/SparkElementalToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/SparkElementalToken.java @@ -1,5 +1,3 @@ - - package mage.game.permanent.token; import mage.constants.CardType; import mage.constants.SubType; @@ -18,7 +16,6 @@ public final class SparkElementalToken extends TokenImpl { public SparkElementalToken() { super("Spark Elemental", "3/1 red Elemental creature token named Spark Elemental with trample, haste, and \"At the beginning of the end step, sacrifice Spark Elemental.\""); - this.setOriginalExpansionSetCode("FUT"); cardType.add(CardType.CREATURE); color.setRed(true); subtype.add(SubType.ELEMENTAL); diff --git a/Mage/src/main/java/mage/game/permanent/token/TIEFighterToken.java b/Mage/src/main/java/mage/game/permanent/token/TIEFighterToken.java index dff557c87aea..3caaebb5898a 100644 --- a/Mage/src/main/java/mage/game/permanent/token/TIEFighterToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/TIEFighterToken.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.game.permanent.token; import mage.abilities.keyword.SpaceflightAbility; diff --git a/Mage/src/main/java/mage/game/permanent/token/ThopterColorlessToken.java b/Mage/src/main/java/mage/game/permanent/token/ThopterColorlessToken.java index 3355fddd401d..2051205c8c2c 100644 --- a/Mage/src/main/java/mage/game/permanent/token/ThopterColorlessToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/ThopterColorlessToken.java @@ -23,7 +23,7 @@ public ThopterColorlessToken() { addAbility(FlyingAbility.getInstance()); - availableImageSetCodes = Arrays.asList("C18", "EXO", "KLD", "MBS", "ORI", "TPR", "VMA", "M19", "ZNC", "KHC"); + availableImageSetCodes = Arrays.asList("C18", "EXO", "KLD", "MBS", "ORI", "VMA", "M19", "ZNC", "KHC"); } @Override diff --git a/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java b/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java index 8b78ea3b22ff..e2a9d18e9902 100644 --- a/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java +++ b/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java @@ -18,6 +18,13 @@ import java.util.List; import java.util.Locale; import java.util.UUID; +import mage.abilities.SpellAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.target.Target; public abstract class TokenImpl extends MageObjectImpl implements Token { @@ -180,7 +187,7 @@ public boolean putOntoBattlefield(int amount, Game game, Ability source, UUID co if (event.getAmount() > tokenSlots) { game.informPlayers( "The token limit per player is " + MAX_TOKENS_PER_GAME + ", " + controller.getName() - + " will only create " + tokenSlots + " tokens." + + " will only create " + tokenSlots + " tokens." ); } event.setAmount(Math.min(event.getAmount(), tokenSlots)); @@ -249,6 +256,66 @@ private static void putOntoBattlefieldHelper(CreateTokenEvent event, Game game, game.addSimultaneousEvent(new CreatedTokenEvent(source, (PermanentToken) permanent)); } + // handle auras coming into the battlefield + // code refactored from CopyPermanentEffect + if (permanent.getSubtype().contains(SubType.AURA)) { + Outcome auraOutcome = Outcome.BoostCreature; + Target auraTarget = null; + + // attach - search effect in spell ability (example: cast Utopia Sprawl, cast Estrid's Invocation on it) + for (Ability ability : permanent.getAbilities()) { + if (!(ability instanceof SpellAbility)) { + continue; + } + auraOutcome = ability.getEffects().getOutcome(ability); + for (Effect effect : ability.getEffects()) { + if (!(effect instanceof AttachEffect)) { + continue; + } + if (permanent.getSpellAbility().getTargets().size() > 0) { + auraTarget = permanent.getSpellAbility().getTargets().get(0); + } + } + } + + // enchant - search in all abilities (example: cast Estrid's Invocation on enchanted creature by Estrid, the Masked second ability, cast Estrid's Invocation on it) + if (auraTarget == null) { + for (Ability ability : permanent.getAbilities()) { + if (!(ability instanceof EnchantAbility)) { + continue; + } + auraOutcome = ability.getEffects().getOutcome(ability); + if (ability.getTargets().size() > 0) { // Animate Dead don't have targets + auraTarget = ability.getTargets().get(0); + } + } + } + + // if this is a copy of a copy, the copy's target has been copied and needs to be cleared + if (auraTarget == null) { + break; + } + // clear selected target + if (auraTarget.getFirstTarget() != null) { + auraTarget.remove(auraTarget.getFirstTarget()); + } + + // select new target + auraTarget.setNotTarget(true); + if (!controller.choose(auraOutcome, auraTarget, source.getSourceId(), game)) { + break; + } + UUID targetId = auraTarget.getFirstTarget(); + Permanent targetPermanent = game.getPermanent(targetId); + Player targetPlayer = game.getPlayer(targetId); + if (targetPermanent != null) { + targetPermanent.addAttachment(permanent.getId(), source, game); + } else if (targetPlayer != null) { + targetPlayer.addAttachment(permanent.getId(), source, game); + } + } + // end of aura code : just remove this line if everything works out well + // must attack if (attacking && game.getCombat() != null && game.getActivePlayerId().equals(permanent.getControllerId())) { game.getCombat().addAttackingCreature(permanent.getId(), game, attackedPlayer); diff --git a/Mage/src/main/java/mage/game/permanent/token/TuktukTheReturnedToken.java b/Mage/src/main/java/mage/game/permanent/token/TuktukTheReturnedToken.java index b4dc4df3c1e3..b2c3cf598e14 100644 --- a/Mage/src/main/java/mage/game/permanent/token/TuktukTheReturnedToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/TuktukTheReturnedToken.java @@ -1,13 +1,11 @@ - - package mage.game.permanent.token; + +import mage.MageInt; import mage.constants.CardType; import mage.constants.SubType; -import mage.MageInt; import mage.constants.SuperType; /** - * * @author spjspj */ public final class TuktukTheReturnedToken extends TokenImpl { @@ -15,7 +13,7 @@ public final class TuktukTheReturnedToken extends TokenImpl { public TuktukTheReturnedToken() { this("ROE"); } - + public TuktukTheReturnedToken(String setCode) { super("Tuktuk the Returned", "legendary 5/5 colorless Goblin Golem artifact creature token"); setOriginalExpansionSetCode(setCode); @@ -23,6 +21,7 @@ public TuktukTheReturnedToken(String setCode) { cardType.add(CardType.CREATURE); addSuperType(SuperType.LEGENDARY); subtype.add(SubType.GOBLIN); + subtype.add(SubType.GOLEM); power = new MageInt(5); toughness = new MageInt(5); } diff --git a/Mage/src/main/java/mage/game/permanent/token/WitherbloomToken.java b/Mage/src/main/java/mage/game/permanent/token/WitherbloomToken.java new file mode 100644 index 000000000000..55b88117d67d --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/WitherbloomToken.java @@ -0,0 +1,33 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.common.GainLifeEffect; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class WitherbloomToken extends TokenImpl { + + public WitherbloomToken() { + super("Pest", "1/1 black and green Pest creature token with \"When this creature dies, you gain 1 life.\""); + cardType.add(CardType.CREATURE); + color.setBlack(true); + color.setGreen(true); + subtype.add(SubType.PEST); + power = new MageInt(1); + toughness = new MageInt(1); + + this.addAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(1))); + } + + private WitherbloomToken(final WitherbloomToken token) { + super(token); + } + + public WitherbloomToken copy() { + return new WitherbloomToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java index 9030c886ca61..3d9287d60b0f 100644 --- a/Mage/src/main/java/mage/game/stack/Spell.java +++ b/Mage/src/main/java/mage/game/stack/Spell.java @@ -17,12 +17,11 @@ import mage.counters.Counter; import mage.counters.Counters; import mage.filter.FilterMana; +import mage.filter.predicate.mageobject.MageObjectReferencePredicate; import mage.game.Game; import mage.game.GameState; import mage.game.MageObjectAttribute; import mage.game.events.CopiedStackObjectEvent; -import mage.game.events.CopyStackObjectEvent; -import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; @@ -32,6 +31,7 @@ import mage.util.GameLog; import mage.util.ManaUtil; import mage.util.SubTypes; +import mage.util.functions.StackObjectCopyApplier; import org.apache.log4j.Logger; import java.util.*; @@ -39,7 +39,7 @@ /** * @author BetaSteward_at_googlemail.com */ -public class Spell extends StackObjImpl implements Card { +public class Spell extends StackObjectImpl implements Card { private static final Logger logger = Logger.getLogger(Spell.class); @@ -201,7 +201,7 @@ public boolean resolve(Game game) { turnController.controlPlayersTurn(game, controller.getId()); } } - if (this.isInstant() || this.isSorcery()) { + if (this.isInstantOrSorcery()) { int index = 0; result = false; boolean legalParts = false; @@ -403,8 +403,8 @@ public void counter(Ability source, Game game) { @Override public void counter(Ability source, Game game, Zone zone, boolean owner, ZoneDetail zoneDetail) { - // source can be null for fizzled spells, found only one place with that usage -- Rebound Ability: - // event.getSourceId().equals(source.getSourceId()) + // source can be null for fizzled spells, don't use that code in your ZONE_CHANGE watchers/triggers: + // event.getSourceId().equals // TODO: so later it must be replaced to another technics with non null source UUID counteringSourceId = (source == null ? null : source.getSourceId()); this.countered = true; @@ -622,7 +622,7 @@ public ManaCosts getManaCost() { * @return */ @Override - public int getConvertedManaCost() { + public int getManaValue() { int cmc = 0; if (faceDown) { return 0; @@ -630,7 +630,7 @@ public int getConvertedManaCost() { for (SpellAbility spellAbility : spellAbilities) { cmc += spellAbility.getConvertedXManaCost(getCard()); } - cmc += getCard().getManaCost().convertedManaCost(); + cmc += getCard().getManaCost().manaValue(); return cmc; } @@ -766,8 +766,31 @@ public Spell copy() { return new Spell(this); } - public Spell copySpell(UUID newController, Game game) { - Spell spellCopy = new Spell(this.card, this.ability.copySpell(), this.controllerId, this.fromZone, game); + /** + * Copy current spell on stack, but do not put copy back to stack (you can modify and put it later) + *

+ * Warning, don't forget to call CopyStackObjectEvent and CopiedStackObjectEvent before and after copy + * CopyStackObjectEvent can change new copies amount, see Twinning Staff + *

+ * Warning, don't forget to call spell.setZone before push to stack + * + * @param game + * @param newController controller of the copied spell + * @return + */ + public Spell copySpell(Game game, Ability source, UUID newController) { + // copied spells must use copied cards + // spell can be from card's part (mdf/adventure), but you must copy FULL card + Card copiedMainCard = game.copyCard(this.card.getMainCard(), source, newController); + // find copied part + Map mapOldToNew = CardUtil.getOriginalToCopiedPartsMap(this.card.getMainCard(), copiedMainCard); + if (!mapOldToNew.containsKey(this.card.getId())) { + throw new IllegalStateException("Can't find card id after main card copy: " + copiedMainCard.getName()); + } + Card copiedPart = (Card) mapOldToNew.get(this.card.getId()); + + // copy spell + Spell spellCopy = new Spell(copiedPart, this.ability.copySpell(this.card, copiedPart), this.controllerId, this.fromZone, game); boolean firstDone = false; for (SpellAbility spellAbility : this.getSpellAbilities()) { if (!firstDone) { @@ -939,6 +962,12 @@ public void setCopy(boolean isCopy, MageObject copyFrom) { this.copyFrom = (copyFrom != null ? copyFrom.copy() : null); } + /** + * Game processing a copies as normal cards, so you don't need to check spell's copy for move/exile. + * Use this only in exceptional situations or to skip unaffected code/choices. + * + * @return + */ @Override public boolean isCopy() { return this.copy; @@ -1006,6 +1035,7 @@ public FilterMana getColorIdentity() { @Override public void setZone(Zone zone, Game game) { card.setZone(zone, game); + game.getState().setZone(this.getId(), zone); } @Override @@ -1027,27 +1057,19 @@ public void checkForCountersToAdd(Permanent permanent, Ability source, Game game } @Override - public StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets) { - return createCopyOnStack(game, source, newControllerId, chooseNewTargets, 1); - } - - @Override - public StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets, int amount) { - Spell spellCopy = null; - GameEvent gameEvent = new CopyStackObjectEvent(source, this, newControllerId, amount); - if (game.replaceEvent(gameEvent)) { - return null; + public void createSingleCopy(UUID newControllerId, StackObjectCopyApplier applier, MageObjectReferencePredicate predicate, Game game, Ability source, boolean chooseNewTargets) { + Spell spellCopy = this.copySpell(game, source, newControllerId); + if (applier != null) { + applier.modifySpell(spellCopy, game); } - for (int i = 0; i < gameEvent.getAmount(); i++) { - spellCopy = this.copySpell(newControllerId, game); - game.getState().setZone(spellCopy.getId(), Zone.STACK); // required for targeting ex: Nivmagus Elemental - game.getStack().push(spellCopy); - if (chooseNewTargets) { - spellCopy.chooseNewTargets(game, newControllerId); - } - game.fireEvent(new CopiedStackObjectEvent(this, spellCopy, newControllerId)); + spellCopy.setZone(Zone.STACK, game); // required for targeting ex: Nivmagus Elemental + game.getStack().push(spellCopy); + if (predicate != null) { + spellCopy.chooseNewTargets(game, newControllerId, true, false, predicate); + } else if (chooseNewTargets || applier != null) { // if applier is non-null but predicate is null then it's extra + spellCopy.chooseNewTargets(game, newControllerId); } - return spellCopy; + game.fireEvent(new CopiedStackObjectEvent(this, spellCopy, newControllerId)); } @Override @@ -1101,4 +1123,8 @@ public void looseAllAbilities(Game game) { throw new UnsupportedOperationException("Spells should not loose all abilities. Check if this operation is correct."); } + @Override + public String toString() { + return ability.toString(); + } } diff --git a/Mage/src/main/java/mage/game/stack/SpellStack.java b/Mage/src/main/java/mage/game/stack/SpellStack.java index 7a8bb227ea29..8a355a85dce7 100644 --- a/Mage/src/main/java/mage/game/stack/SpellStack.java +++ b/Mage/src/main/java/mage/game/stack/SpellStack.java @@ -144,4 +144,8 @@ public Date getDateLastAdded() { return dateLastAdded; } + @Override + public String toString() { + return this.size() + (this.isEmpty() ? "" : " (top: " + this.getFirst().toString() + ")"); + } } diff --git a/Mage/src/main/java/mage/game/stack/StackAbility.java b/Mage/src/main/java/mage/game/stack/StackAbility.java index 5627022adf24..c954f9f16770 100644 --- a/Mage/src/main/java/mage/game/stack/StackAbility.java +++ b/Mage/src/main/java/mage/game/stack/StackAbility.java @@ -20,9 +20,9 @@ import mage.cards.Card; import mage.cards.FrameStyle; import mage.constants.*; +import mage.filter.predicate.mageobject.MageObjectReferencePredicate; import mage.game.Game; import mage.game.events.CopiedStackObjectEvent; -import mage.game.events.CopyStackObjectEvent; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; @@ -32,6 +32,7 @@ import mage.target.targetadjustment.TargetAdjuster; import mage.util.GameLog; import mage.util.SubTypes; +import mage.util.functions.StackObjectCopyApplier; import mage.watchers.Watcher; import java.util.ArrayList; @@ -42,7 +43,7 @@ /** * @author BetaSteward_at_googlemail.com */ -public class StackAbility extends StackObjImpl implements Ability { +public class StackAbility extends StackObjectImpl implements Ability { private static final ArrayList emptyCardType = new ArrayList<>(); private static final List emptyString = new ArrayList<>(); @@ -222,6 +223,11 @@ public ManaCosts getManaCost() { return emptyCost; } + @Override + public List getManaCostSymbols() { + return super.getManaCostSymbols(); + } + @Override public MageInt getPower() { return MageInt.EmptyMageInt; @@ -267,7 +273,7 @@ public Costs getCosts() { } @Override - public int getConvertedManaCost() { + public int getManaValue() { // Activated abilities have an "activation cost" but they don't have a characteristic related to that while on the stack. // There are certain effects that interact with the cost to activate an ability (e.g., Training Grounds, Power Artifact) // but nothing that looks for that quality of an ability once it's on the stack. @@ -435,8 +441,8 @@ public Modes getModes() { } @Override - public boolean canChooseTarget(Game game) { - return ability.canChooseTarget(game); + public boolean canChooseTarget(Game game, UUID playerId) { + return ability.canChooseTarget(game, playerId); } @Override @@ -591,32 +597,17 @@ public void setCanFizzle(boolean canFizzle) { } @Override - public StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets) { - return createCopyOnStack(game, source, newControllerId, chooseNewTargets, 1); - } - - public StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets, int amount) { - StackAbility newStackAbility = null; - GameEvent gameEvent = new CopyStackObjectEvent(source, this, newControllerId, amount); - if (game.replaceEvent(gameEvent)) { - return null; - } - for (int i = 0; i < gameEvent.getAmount(); i++) { - Ability newAbility = this.copy(); - newAbility.newId(); - newStackAbility = new StackAbility(newAbility, newControllerId); - game.getStack().push(newStackAbility); - if (chooseNewTargets && !newAbility.getTargets().isEmpty()) { - Player controller = game.getPlayer(newControllerId); - Outcome outcome = newAbility.getEffects().getOutcome(newAbility); - if (controller.chooseUse(outcome, "Choose new targets?", source, game)) { - newAbility.getTargets().clearChosen(); - newAbility.getTargets().chooseTargets(outcome, newControllerId, newAbility, false, game, false); - } - } - game.fireEvent(new CopiedStackObjectEvent(this, newStackAbility, newControllerId)); + public void createSingleCopy(UUID newControllerId, StackObjectCopyApplier applier, MageObjectReferencePredicate predicate, Game game, Ability source, boolean chooseNewTargets) { + Ability newAbility = this.copy(); + newAbility.newId(); + StackAbility newStackAbility = new StackAbility(newAbility, newControllerId); + game.getStack().push(newStackAbility); + if (predicate != null) { + newStackAbility.chooseNewTargets(game, newControllerId, true, false, predicate); + } else if (chooseNewTargets || applier != null) { // if applier is non-null but predicate is null then it's extra + newStackAbility.chooseNewTargets(game, newControllerId); } - return newStackAbility; + game.fireEvent(new CopiedStackObjectEvent(this, newStackAbility, newControllerId)); } @Override diff --git a/Mage/src/main/java/mage/game/stack/StackObject.java b/Mage/src/main/java/mage/game/stack/StackObject.java index 9517243ce403..7e078e61ef0d 100644 --- a/Mage/src/main/java/mage/game/stack/StackObject.java +++ b/Mage/src/main/java/mage/game/stack/StackObject.java @@ -4,9 +4,11 @@ import mage.abilities.Ability; import mage.constants.Zone; import mage.constants.ZoneDetail; -import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicate; +import mage.filter.predicate.mageobject.MageObjectReferencePredicate; import mage.game.Controllable; import mage.game.Game; +import mage.util.functions.StackObjectCopyApplier; import java.util.UUID; @@ -17,7 +19,6 @@ public interface StackObject extends MageObject, Controllable { UUID getSourceId(); /** - * * @param source null for fizzled events (sourceId will be null) * @param game */ @@ -27,11 +28,17 @@ public interface StackObject extends MageObject, Controllable { Ability getStackAbility(); - boolean chooseNewTargets(Game game, UUID playerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget); + boolean chooseNewTargets(Game game, UUID playerId, boolean forceChange, boolean onlyOneTarget, Predicate extraPredicate); - StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets); + boolean canTarget(Game game, UUID targetId); - StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets, int amount); + void createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets); + + void createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets, int amount); + + void createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets, int amount, StackObjectCopyApplier applier); + + void createSingleCopy(UUID newControllerId, StackObjectCopyApplier applier, MageObjectReferencePredicate predicate, Game game, Ability source, boolean chooseNewTargets); boolean isTargetChanged(); diff --git a/Mage/src/main/java/mage/game/stack/StackObjImpl.java b/Mage/src/main/java/mage/game/stack/StackObjectImpl.java similarity index 66% rename from Mage/src/main/java/mage/game/stack/StackObjImpl.java rename to Mage/src/main/java/mage/game/stack/StackObjectImpl.java index 2bfcbbf5c230..d67130d9e181 100644 --- a/Mage/src/main/java/mage/game/stack/StackObjImpl.java +++ b/Mage/src/main/java/mage/game/stack/StackObjectImpl.java @@ -6,24 +6,144 @@ import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.dynamicvalue.common.StaticValue; +import mage.choices.Choice; +import mage.choices.ChoiceImpl; import mage.constants.Outcome; -import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicate; +import mage.filter.predicate.mageobject.MageObjectReferencePredicate; import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.game.events.CopyStackObjectEvent; +import mage.game.events.GameEvent; import mage.players.Player; import mage.target.Target; import mage.target.TargetAmount; +import mage.util.CardUtil; +import mage.util.functions.StackObjectCopyApplier; -import java.util.Set; -import java.util.UUID; +import java.util.*; +import java.util.stream.Collectors; /** * @author LevelX2 */ -public abstract class StackObjImpl implements StackObject { +public abstract class StackObjectImpl implements StackObject { protected boolean targetChanged; // for Psychic Battle + @Override + public void createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets) { + createCopyOnStack(game, source, newControllerId, chooseNewTargets, 1); + } + + @Override + public void createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets, int amount) { + createCopyOnStack(game, source, newControllerId, chooseNewTargets, amount, null); + } + + private static final class PredicateIterator implements Iterator { + private final StackObjectCopyApplier applier; + private final Player player; + private final int amount; + private final Game game; + private Map predicateMap = null; + private int anyCount = 0; + private int setCount = 0; + private Iterator iterator = null; + private Choice choice = null; + + private PredicateIterator(Game game, UUID newControllerId, int amount, StackObjectCopyApplier applier) { + this.applier = applier; + this.player = game.getPlayer(newControllerId); + this.amount = amount; + this.game = game; + } + + @Override + public boolean hasNext() { + return true; + } + + private void makeMap() { + if (predicateMap != null) { + return; + } + predicateMap = new HashMap<>(); + + for (int i = 0; i < amount; i++) { + MageObjectReferencePredicate predicate = applier.getNextPredicate(); + if (predicate == null) { + anyCount++; + String message = "Any target"; + if (anyCount > 1) { + message += " (" + anyCount + ")"; + } + predicateMap.put(message, predicate); + continue; + } + setCount++; + predicateMap.put(predicate.getName(game), predicate); + } + if ((setCount == 1 && anyCount == 0) || setCount == 0) { + iterator = predicateMap.values().stream().collect(Collectors.toList()).iterator(); + } + } + + private void makeChoice() { + if (choice != null) { + return; + } + choice = new ChoiceImpl(false); + choice.setMessage("Choose the order of copies to go on the stack"); + choice.setSubMessage("Press cancel to put the rest in any order"); + choice.setChoices(new HashSet<>(predicateMap.keySet())); + } + + @Override + public MageObjectReferencePredicate next() { + if (player == null || applier == null) { + return null; + } + makeMap(); + if (iterator != null) { + return iterator.hasNext() ? iterator.next() : null; + } + makeChoice(); + if (choice.getChoices().size() < 2) { + iterator = choice.getChoices().stream().map(predicateMap::get).iterator(); + return next(); + } + choice.clearChoice(); + player.choose(Outcome.AIDontUseIt, choice, game); + String chosen = choice.getChoice(); + if (chosen == null) { + iterator = choice.getChoices().stream().map(predicateMap::get).iterator(); + return next(); + } + choice.getChoices().remove(chosen); + return predicateMap.get(chosen); + } + } + + @Override + public void createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets, int amount, StackObjectCopyApplier applier) { + GameEvent gameEvent = new CopyStackObjectEvent(source, this, newControllerId, amount); + if (game.replaceEvent(gameEvent)) { + return; + } + Iterator predicates = new PredicateIterator(game, newControllerId, gameEvent.getAmount(), applier); + for (int i = 0; i < gameEvent.getAmount(); i++) { + createSingleCopy(newControllerId, applier, predicates.next(), game, source, chooseNewTargets); + } + Player player = game.getPlayer(newControllerId); + if (player == null) { + return; + } + game.informPlayers( + player.getName() + " created " + CardUtil.numberToText(gameEvent.getAmount(), "a") + + " cop" + (gameEvent.getAmount() == 1 ? "y" : "ies") + " of " + getIdName() + ); + } + /** * Choose new targets for a stack Object * @@ -94,12 +214,12 @@ public boolean chooseNewTargets(Game game, UUID playerId) { * targetId * @param onlyOneTarget - 114.6b one target must be changed to another * target - * @param filterNewTarget restriction for the new target, if null nothing is + * @param extraPredicate restriction for the new target, if null nothing is * cheched * @return */ @Override - public boolean chooseNewTargets(Game game, UUID targetControllerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget) { + public boolean chooseNewTargets(Game game, UUID targetControllerId, boolean forceChange, boolean onlyOneTarget, Predicate extraPredicate) { Player targetController = game.getPlayer(targetControllerId); if (targetController != null) { StringBuilder oldTargetDescription = new StringBuilder(); @@ -118,7 +238,7 @@ public boolean chooseNewTargets(Game game, UUID targetControllerId, boolean forc ability.getModes().setActiveMode(mode); oldTargetDescription.append(ability.getTargetDescription(mode.getTargets(), game)); for (Target target : mode.getTargets()) { - Target newTarget = chooseNewTarget(targetController, ability, mode, target, forceChange, filterNewTarget, game); + Target newTarget = chooseNewTarget(targetController, ability, mode, target, forceChange, extraPredicate, game); // clear the old target and copy all targets from new target target.clearChosen(); for (UUID targetId : newTarget.getTargets()) { @@ -149,8 +269,13 @@ public boolean chooseNewTargets(Game game, UUID targetControllerId, boolean forc * @param game * @return */ - private Target chooseNewTarget(Player targetController, Ability ability, Mode mode, Target target, boolean forceChange, FilterPermanent filterNewTarget, Game game) { + private Target chooseNewTarget(Player targetController, Ability ability, Mode mode, Target target, boolean forceChange, Predicate predicate, Game game) { Target newTarget = target.copy(); + if (predicate != null) { + newTarget.getFilter().add(predicate); + // If adding a predicate, there will only be one choice and therefore target can be automatic + newTarget.setRandom(true); + } newTarget.setEventReporting(false); if (!targetController.getId().equals(getControllerId())) { newTarget.setTargetController(targetController.getId()); // target controller for the change is different from spell controller @@ -181,15 +306,6 @@ private Target chooseNewTarget(Player targetController, Ability ability, Mode mo newTarget.chooseTarget(outcome, getControllerId(), ability, game); - // check target restriction TODO: add multiple target checks - if (newTarget.getFirstTarget() != null && filterNewTarget != null) { - Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget()); - if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) { - game.informPlayer(targetController, "Target does not fullfil the target requirements (" + filterNewTarget.getMessage() + ')'); - newTarget.clearChosen(); - } - } - // workaround to stop infinite AI choose (remove after chooseTarget can be called with extra filter to disable some ids) if (iteration > 10) { break; @@ -200,6 +316,11 @@ private Target chooseNewTarget(Player targetController, Ability ability, Mode mo } else { // build a target definition with exactly one possible target to select that replaces old target Target tempTarget = target.copy(); + if (predicate != null) { + tempTarget.getFilter().add(predicate); + // If adding a predicate, there will only be one choice and therefore target can be automatic + tempTarget.setRandom(true); + } tempTarget.setEventReporting(false); if (target instanceof TargetAmount) { ((TargetAmount) tempTarget).setAmountDefinition(StaticValue.get(target.getTargetAmount(targetId))); @@ -242,12 +363,6 @@ private Target chooseNewTarget(Player targetController, Ability ability, Mode mo // keep the old newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, true); } - } else if (newTarget.getFirstTarget() != null && filterNewTarget != null) { - Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget()); - if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) { - game.informPlayer(targetController, "This target does not fullfil the target requirements (" + filterNewTarget.getMessage() + ')'); - again = true; - } } else { // valid target was selected, add it to the new target definition newTarget.addTarget(tempTarget.getFirstTarget(), target.getTargetAmount(targetId), ability, game, true); @@ -263,6 +378,30 @@ private Target chooseNewTarget(Player targetController, Ability ability, Mode mo return newTarget; } + @Override + public boolean canTarget(Game game, UUID targetId) { + Abilities objectAbilities = new AbilitiesImpl<>(); + if (this instanceof Spell) { + objectAbilities.addAll(((Spell) this).getSpellAbilities()); + } else { + objectAbilities.add(getStackAbility()); + } + for (Ability ability : objectAbilities) { + if (ability.getModes() + .getSelectedModes() + .stream() + .map(ability.getModes()::get) + .filter(Objects::nonNull) + .map(Mode::getTargets) + .flatMap(Collection::stream) + .filter(t -> !t.isNotTarget()) + .anyMatch(t -> t.canTarget(ability.getControllerId(), targetId, ability, game))) { + return true; + } + } + return false; + } + private String getNamesOftargets(UUID targetId, Game game) { MageObject object = game.getObject(targetId); String name = null; diff --git a/Mage/src/main/java/mage/players/Library.java b/Mage/src/main/java/mage/players/Library.java index 5ff04b98e9e1..e08aab6741bc 100644 --- a/Mage/src/main/java/mage/players/Library.java +++ b/Mage/src/main/java/mage/players/Library.java @@ -251,4 +251,9 @@ public int getCardPosition(UUID cardId) { } return -1; } + + @Override + public String toString() { + return "Cards: " + library.size(); + } } diff --git a/Mage/src/main/java/mage/players/ManaPool.java b/Mage/src/main/java/mage/players/ManaPool.java index 0d46942b27e8..a68d1edc4f25 100644 --- a/Mage/src/main/java/mage/players/ManaPool.java +++ b/Mage/src/main/java/mage/players/ManaPool.java @@ -1,6 +1,7 @@ package mage.players; import mage.ConditionalMana; +import mage.Emptiable; import mage.MageObject; import mage.Mana; import mage.abilities.Ability; @@ -37,6 +38,7 @@ public class ManaPool implements Serializable { private final List poolBookmark = new ArrayList<>(); // mana pool bookmark for rollback purposes private final Set doNotEmptyManaTypes = new HashSet<>(); + private boolean manaBecomesColorless = false; private static final class ConditionalManaInfo { private final ManaType manaType; @@ -73,6 +75,7 @@ public ManaPool(final ManaPool pool) { } this.doNotEmptyManaTypes.addAll(pool.doNotEmptyManaTypes); this.lastPaymentWasSnow = pool.lastPaymentWasSnow; + this.manaBecomesColorless = pool.manaBecomesColorless; } public int getRed() { @@ -229,12 +232,21 @@ public int getColorless() { public void clearEmptyManaPoolRules() { doNotEmptyManaTypes.clear(); + this.manaBecomesColorless = false; } public void addDoNotEmptyManaType(ManaType manaType) { doNotEmptyManaTypes.add(manaType); } + public void setManaBecomesColorless(boolean manaBecomesColorless) { + this.manaBecomesColorless = manaBecomesColorless; + } + + public boolean isManaBecomesColorless() { + return manaBecomesColorless; + } + public void init() { manaItems.clear(); } @@ -246,35 +258,15 @@ public int emptyPool(Game game) { ManaPoolItem item = it.next(); ConditionalMana conditionalItem = item.getConditionalMana(); for (ManaType manaType : ManaType.values()) { - if (!doNotEmptyManaTypes.contains(manaType)) { - if (item.get(manaType) > 0) { - if (item.getDuration() != Duration.EndOfTurn - || game.getPhase().getType() == TurnPhase.END) { - if (game.replaceEvent(new GameEvent(GameEvent.EventType.EMPTY_MANA_POOL, playerId, null, playerId))) { - int amount = item.get(manaType); - item.clear(manaType); - item.add(ManaType.COLORLESS, amount); - } else { - total += item.get(manaType); - item.clear(manaType); - } - } - } - if (conditionalItem != null) { - if (conditionalItem.get(manaType) > 0) { - if (item.getDuration() != Duration.EndOfTurn - || game.getPhase().getType() == TurnPhase.END) { - if (game.replaceEvent(new GameEvent(GameEvent.EventType.EMPTY_MANA_POOL, playerId, null, playerId))) { - int amount = conditionalItem.get(manaType); - conditionalItem.clear(manaType); - conditionalItem.add(ManaType.COLORLESS, amount); - } else { - total += conditionalItem.get(manaType); - conditionalItem.clear(manaType); - } - } - } - } + if (doNotEmptyManaTypes.contains(manaType)) { + continue; + } + if (item.get(manaType) > 0) { + total += emptyItem(item, item, game, manaType); + } + if (conditionalItem != null + && conditionalItem.get(manaType) > 0) { + total += emptyItem(item, conditionalItem, game, manaType); } } if (item.count() == 0) { @@ -284,6 +276,22 @@ public int emptyPool(Game game) { return total; } + private int emptyItem(ManaPoolItem item, Emptiable toEmpty, Game game, ManaType manaType) { + if (item.getDuration() == Duration.EndOfTurn + && game.getPhase().getType() != TurnPhase.END) { + return 0; + } + if (!manaBecomesColorless) { + int amount = toEmpty.get(manaType); + toEmpty.clear(manaType); + return amount; + } + int amount = toEmpty.get(manaType); + toEmpty.clear(manaType); + toEmpty.add(ManaType.COLORLESS, amount); + return 0; + } + public Mana getMana() { Mana m = new Mana(); for (ManaPoolItem item : manaItems) { @@ -331,15 +339,28 @@ public void addMana(Mana manaToAdd, Game game, Ability source, boolean emptyOnTu Mana mana = manaToAdd.copy(); if (!game.replaceEvent(new ManaEvent(EventType.ADD_MANA, source.getId(), source, playerId, mana))) { if (mana instanceof ConditionalMana) { - ManaPoolItem item = new ManaPoolItem((ConditionalMana) mana, source.getSourceObject(game), - ((ConditionalMana) mana).getManaProducerOriginalId() != null - ? ((ConditionalMana) mana).getManaProducerOriginalId() : source.getOriginalId()); + ConditionalMana conditionalMana = (ConditionalMana) mana; + ManaPoolItem item = new ManaPoolItem( + conditionalMana, + source.getSourceObject(game), + conditionalMana.getManaProducerOriginalId() != null ? conditionalMana.getManaProducerOriginalId() : source.getOriginalId() + ); if (emptyOnTurnsEnd) { item.setDuration(Duration.EndOfTurn); } this.manaItems.add(item); } else { - ManaPoolItem item = new ManaPoolItem(mana.getRed(), mana.getGreen(), mana.getBlue(), mana.getWhite(), mana.getBlack(), mana.getGeneric() + mana.getColorless(), source.getSourceObject(game), source.getOriginalId(), mana.getFlag()); + ManaPoolItem item = new ManaPoolItem( + mana.getRed(), + mana.getGreen(), + mana.getBlue(), + mana.getWhite(), + mana.getBlack(), + mana.getGeneric() + mana.getColorless(), + source.getSourceObject(game), + source.getOriginalId(), + mana.getFlag() + ); if (emptyOnTurnsEnd) { item.setDuration(Duration.EndOfTurn); } @@ -510,4 +531,9 @@ public int getColoredAmount(ManaType manaType) { public boolean getLastPaymentWasSnow() { return lastPaymentWasSnow; } + + @Override + public String toString() { + return getMana().toString(); + } } diff --git a/Mage/src/main/java/mage/players/ManaPoolItem.java b/Mage/src/main/java/mage/players/ManaPoolItem.java index ba75b8798e94..5b18b4d53a0b 100644 --- a/Mage/src/main/java/mage/players/ManaPoolItem.java +++ b/Mage/src/main/java/mage/players/ManaPoolItem.java @@ -5,6 +5,7 @@ import mage.ConditionalMana; import mage.MageObject; import mage.Mana; +import mage.Emptiable; import mage.constants.Duration; import mage.constants.ManaType; @@ -12,7 +13,7 @@ * * @author BetaSteward_at_googlemail.com */ -public class ManaPoolItem implements Serializable { +public class ManaPoolItem implements Serializable, Emptiable { private int red = 0; private int green = 0; diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java index f9723f918def..3d9b815069dc 100644 --- a/Mage/src/main/java/mage/players/Player.java +++ b/Mage/src/main/java/mage/players/Player.java @@ -48,8 +48,33 @@ */ public interface Player extends MageItem, Copyable { + /** + * Current player is real life player (human). Try to use in GUI and network engine only. + *

+ * WARNING, you must use isComputer instead isHuman in card's code (for good Human/AI logic testing in unit tests) + * TODO: check combat code and other and replace isHuman to isComputer usage if possible (if AI support that actions) + * + * @return + */ boolean isHuman(); + boolean isTestsMode(); + + /** + * Current player is AI. Use it in card's code and all other places. + *

+ * It help to split Human/AI logic and test both by unit tests. + *

+ * Usage example: AI hint to skip or auto-calculate choices instead call of real choose dialogs + * - unit tests for Human logic: call normal commands + * - unit tests for AI logic: call aiXXX commands + * + * @return + */ + default boolean isComputer() { + return !isHuman(); + } + String getName(); String getLogName(); @@ -101,6 +126,8 @@ public interface Player extends MageItem, Copyable { */ int gainLife(int amount, Game game, Ability source); + void exchangeLife(Player player, Ability source, Game game); + int damage(int damage, UUID attackerId, Ability source, Game game); int damage(int damage, UUID attackerId, Ability source, Game game, boolean combatDamage, boolean preventable); @@ -314,8 +341,6 @@ public interface Player extends MageItem, Copyable { void setGameUnderYourControl(boolean value, boolean fullRestore); - boolean isTestMode(); - void setTestMode(boolean value); void addAction(String action); @@ -365,6 +390,20 @@ public interface Player extends MageItem, Copyable { boolean cast(SpellAbility ability, Game game, boolean noMana, ApprovingObject approvingObject); + /** + * Force player to choose spell ability to cast. Use it in effects while casting cards. + *

+ * Commands order in all use cases: + * - PlayFromNotOwnHandZone - true (if you put main id then all parts allows, if you put part id then only part allows) + * - chooseAbilityForCast + * - cast + * - PlayFromNotOwnHandZone - false + * + * @param card + * @param game + * @param noMana + * @return + */ SpellAbility chooseAbilityForCast(Card card, Game game, boolean noMana); boolean putInHand(Card card, Game game); @@ -458,6 +497,8 @@ public interface Player extends MageItem, Copyable { Cards discard(int amount, boolean random, boolean payForCost, Ability source, Game game); + Cards discard(int minAmount, int maxAmount, boolean payForCost, Ability source, Game game); + Cards discard(Cards cards, boolean payForCost, Ability source, Game game); void discardToMax(Game game); @@ -667,6 +708,19 @@ default int announceXMana(int min, int max, String message, Game game, Ability a int getAmount(int min, int max, String message, Game game); + /** + * Player distributes amount among multiple options + * + * @param outcome AI hint + * @param messages List of options to distribute amount among + * @param min Minimum value per option + * @param max Total amount to be distributed + * @param type MultiAmountType enum to set dialog options such as title and header + * @param game Game + * @return List of integers with size equal to messages.size(). The sum of the integers is equal to max. + */ + List getMultiAmount(Outcome outcome, List messages, int min, int max, MultiAmountType type, Game game); + void sideboard(Match match, Deck deck); void construct(Tournament tournament, Deck deck); @@ -693,6 +747,8 @@ default int announceXMana(int min, int max, String message, Game game, Ability a void untap(Game game); + void updateRange(Game game); + ManaOptions getManaAvailable(Game game); void addAvailableTriggeredMana(List netManaAvailable); @@ -793,7 +849,7 @@ default int announceXMana(int min, int max, String message, Game game, Ability a boolean moveCards(Card card, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects); - boolean moveCards(Set cards, Zone toZone, Ability source, Game game); + boolean moveCards(Set cards, Zone toZone, Ability source, Game game); /** * Universal method to move cards from one zone to another. Do not mix @@ -812,7 +868,7 @@ default int announceXMana(int min, int max, String message, Game game, Ability a * @param appliedEffects * @return */ - boolean moveCards(Set cards, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects); + boolean moveCards(Set cards, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects); boolean moveCardsToExile(Card card, Ability source, Game game, boolean withName, UUID exileId, String exileZoneName); @@ -869,7 +925,7 @@ default int announceXMana(int min, int max, String message, Game game, Ability a * @param fromZone if null, this info isn't postet * @return Set that were successful moved to graveyard */ - Set moveCardsToGraveyardWithInfo(Set cards, Ability source, Game game, Zone fromZone); + Set moveCardsToGraveyardWithInfo(Set cards, Ability source, Game game, Zone fromZone); /** * Uses card.moveToZone and posts a inform message about moving the card to diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 771e1b8bad61..3ee256dbc296 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -483,7 +483,7 @@ public Counters getCounters() { @Override public void beginTurn(Game game) { this.landsPlayed = 0; - updateRangeOfInfluence(game); + updateRange(game); } @Override @@ -491,7 +491,8 @@ public RangeOfInfluence getRange() { return range; } - protected void updateRangeOfInfluence(Game game) { + @Override + public void updateRange(Game game) { // 20100423 - 801.2c // 801.2c The particular players within each player’s range of influence are determined as each turn begins. inRange.clear(); @@ -523,6 +524,13 @@ private Set getAllNearPlayers(Game game, boolean needPrevious) { @Override public Set getInRange() { + if (inRange.isEmpty()) { + // runtime check: inRange filled on beginTurn, but unit tests adds cards by cheat engine before game starting, + // so inRange will be empty and some ETB effects can be broken (example: Spark Double puts direct to battlefield). + // Cheat engine already have a workaround, so that error must not be visible in normal situation. + throw new IllegalStateException("Wrong code usage (game is not started, but you call getInRange in some effects)."); + } + return inRange; } @@ -545,7 +553,7 @@ public void controlPlayersTurn(Game game, UUID playerId) { new LoseControlOnOtherPlayersControllerEffect(this.getLogName(), player.getLogName())); ability.setSourceId(getId()); ability.setControllerId(getId()); - game.addDelayedTriggeredAbility(ability); + game.addDelayedTriggeredAbility(ability, null); } } @@ -712,20 +720,20 @@ public boolean removeFromLibrary(Card card, Game game) { @Override public Card discardOne(boolean random, boolean payForCost, Ability source, Game game) { - Cards cards = discard(1, random, payForCost, source, game); - if (cards.isEmpty()) { - return null; - } - return cards.getRandom(game); + return discard(1, random, payForCost, source, game).getRandom(game); } @Override public Cards discard(int amount, boolean random, boolean payForCost, Ability source, Game game) { - Cards discardedCards = doDiscardWithoutFinalEvent(amount, random, payForCost, source, game); - if (!discardedCards.isEmpty()) { - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.DISCARDED_CARDS, null, source, playerId, discardedCards.size())); + if (random) { + return discard(getRandomToDiscard(amount, source, game), payForCost, source, game); } - return discardedCards; + return discard(amount, amount, payForCost, source, game); + } + + @Override + public Cards discard(int minAmount, int maxAmount, boolean payForCost, Ability source, Game game) { + return discard(getToDiscard(minAmount, maxAmount, source, game), payForCost, source, game); } @Override @@ -740,53 +748,46 @@ public Cards discard(Cards cards, boolean payForCost, Ability source, Game game) } } if (!discardedCards.isEmpty()) { - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.DISCARDED_CARDS, null, source, playerId, discardedCards.size())); + game.fireEvent(new DiscardedCardsEvent(source, playerId, discardedCards.size(), discardedCards)); } return discardedCards; } - private Cards doDiscardWithoutFinalEvent(int amount, boolean random, boolean payForCost, Ability source, Game game) { - Cards discardedCards = new CardsImpl(); - if (amount <= 0) { - return discardedCards; - } + @Override + public boolean discard(Card card, boolean payForCost, Ability source, Game game) { + return doDiscard(card, source, game, payForCost, true); + } - // all without dialogs - if (this.getHand().size() == 1 || this.getHand().size() == amount) { - List cardsToDiscard = new ArrayList<>(this.getHand()); - for (UUID id : cardsToDiscard) { - if (doDiscard(this.getHand().get(id, game), source, game, payForCost, false)) { - discardedCards.add(id); - } - } - return discardedCards; + private Cards getToDiscard(int minAmount, int maxAmount, Ability source, Game game) { + Cards toDiscard = new CardsImpl(); + if (minAmount > maxAmount) { + return getToDiscard(maxAmount, minAmount, source, game); } - - if (random) { - for (int i = 0; i < amount; i++) { - Card card = this.getHand().getRandom(game); - if (doDiscard(card, source, game, payForCost, false)) { - discardedCards.add(card); - } - } - } else { - int possibleAmount = Math.min(getHand().size(), amount); - TargetDiscard target = new TargetDiscard(possibleAmount, possibleAmount, - new FilterCard(CardUtil.numberToText(possibleAmount, "a") - + " card" + (possibleAmount > 1 ? "s" : "")), playerId); - choose(Outcome.Discard, target, source == null ? null : source.getSourceId(), game); - for (UUID cardId : target.getTargets()) { - if (doDiscard(this.getHand().get(cardId, game), source, game, payForCost, false)) { - discardedCards.add(cardId); - } - } + if (maxAmount < 1) { + return toDiscard; } - return discardedCards; + if (getHand().size() <= minAmount) { + toDiscard.addAll(getHand()); + return toDiscard; + } + TargetDiscard target = new TargetDiscard(minAmount, maxAmount, StaticFilters.FILTER_CARD, getId()); + choose(Outcome.Discard, target, source != null ? source.getSourceId() : null, game); + toDiscard.addAll(target.getTargets()); + return toDiscard; } - @Override - public boolean discard(Card card, boolean payForCost, Ability source, Game game) { - return doDiscard(card, source, game, payForCost, true); + private Cards getRandomToDiscard(int amount, Ability source, Game game) { + Cards toDiscard = new CardsImpl(); + Cards hand = getHand().copy(); + for (int i = 0; i < amount; i++) { + if (hand.isEmpty()) { + break; + } + Card card = hand.getRandom(game); + hand.remove(card); + toDiscard.add(card); + } + return toDiscard; } private boolean doDiscard(Card card, Ability source, Game game, boolean payForCost, boolean fireFinalEvent) { @@ -825,7 +826,7 @@ the moment before the cost was paid (see rule 717, "Handling Illegal Actions"). game.fireEvent(GameEvent.getEvent(GameEvent.EventType.DISCARDED_CARD, card.getId(), source, playerId)); if (fireFinalEvent) { - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.DISCARDED_CARDS, null, source, playerId, 1)); + game.fireEvent(new DiscardedCardsEvent(source, playerId, 1, new CardsImpl(card))); } return true; } @@ -1495,7 +1496,8 @@ public boolean triggerAbility(TriggeredAbility triggeredAbility, Game game) { if (sourceObject != null) { sourceObject.adjustTargets(ability, game); } - if (ability.canChooseTarget(game)) { + UUID triggerId = null; + if (ability.canChooseTarget(game, playerId)) { if (ability.isUsesStack()) { game.getStack().push(new StackAbility(ability, playerId)); } @@ -1508,59 +1510,89 @@ public boolean triggerAbility(TriggeredAbility triggeredAbility, Game game) { if (!ability.isUsesStack()) { ability.resolve(game); } else { - game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, - ability.getId(), ability, ability.getControllerId())); + game.fireEvent(new GameEvent( + GameEvent.EventType.TRIGGERED_ABILITY, + ability.getId(), ability, ability.getControllerId() + )); + triggerId = ability.getId(); } game.removeBookmark(bookmark); return true; } } restoreState(bookmark, triggeredAbility.getRule(), game); // why restore is needed here? (to remove the triggered ability from the stack because of no possible targets) + GameEvent event = new GameEvent( + GameEvent.EventType.ABILITY_TRIGGERED, + triggerId, ability, ability.getControllerId() + ); + game.getState().setValue(event.getId().toString(), ability.getTriggerEvent()); + game.fireEvent(event); return false; } + /** + * Return spells for possible cast + * Uses in GUI to show only playable spells for choosing from the card + * (example: effect allow to cast card and player must choose the spell ability) + * + * @param playerId + * @param object + * @param zone + * @param game + * @return + */ public static LinkedHashMap getSpellAbilities(UUID playerId, MageObject object, Zone zone, Game game) { + // it uses simple check from spellCanBeActivatedRegularlyNow + // reason: no approved info here (e.g. forced to choose spell ability from cast card) LinkedHashMap useable = new LinkedHashMap<>(); - for (Ability ability : object.getAbilities()) { + Abilities allAbilities; + if (object instanceof Card) { + allAbilities = ((Card) object).getAbilities(game); + } else { + allAbilities = object.getAbilities(); + } + + for (Ability ability : allAbilities) { if (ability instanceof SpellAbility) { switch (((SpellAbility) ability).getSpellAbilityType()) { case BASE_ALTERNATE: - ActivationStatus as = ((SpellAbility) ability).canActivate(playerId, game); - if (as.canActivate()) { + if (((SpellAbility) ability).spellCanBeActivatedRegularlyNow(playerId, game)) { useable.put(ability.getId(), (SpellAbility) ability); // example: Chandra, Torch of Defiance +1 loyal ability } return useable; case SPLIT_FUSED: if (zone == Zone.HAND) { - if (ability.canChooseTarget(game)) { + if (ability.canChooseTarget(game, playerId)) { useable.put(ability.getId(), (SpellAbility) ability); } } case SPLIT: - if (((SplitCard) object).getLeftHalfCard().getSpellAbility().canChooseTarget(game)) { + if (((SplitCard) object).getLeftHalfCard().getSpellAbility().canChooseTarget(game, playerId)) { useable.put(((SplitCard) object).getLeftHalfCard().getSpellAbility().getId(), ((SplitCard) object).getLeftHalfCard().getSpellAbility()); } - if (((SplitCard) object).getRightHalfCard().getSpellAbility().canChooseTarget(game)) { + if (((SplitCard) object).getRightHalfCard().getSpellAbility().canChooseTarget(game, playerId)) { useable.put(((SplitCard) object).getRightHalfCard().getSpellAbility().getId(), ((SplitCard) object).getRightHalfCard().getSpellAbility()); } return useable; case SPLIT_AFTERMATH: if (zone == Zone.GRAVEYARD) { - if (((SplitCard) object).getRightHalfCard().getSpellAbility().canChooseTarget(game)) { + if (((SplitCard) object).getRightHalfCard().getSpellAbility().canChooseTarget(game, playerId)) { useable.put(((SplitCard) object).getRightHalfCard().getSpellAbility().getId(), ((SplitCard) object).getRightHalfCard().getSpellAbility()); } } else { - if (((SplitCard) object).getLeftHalfCard().getSpellAbility().canChooseTarget(game)) { + if (((SplitCard) object).getLeftHalfCard().getSpellAbility().canChooseTarget(game, playerId)) { useable.put(((SplitCard) object).getLeftHalfCard().getSpellAbility().getId(), ((SplitCard) object).getLeftHalfCard().getSpellAbility()); } } return useable; default: - useable.put(ability.getId(), (SpellAbility) ability); + if (((SpellAbility) ability).spellCanBeActivatedRegularlyNow(playerId, game)) { + useable.put(ability.getId(), (SpellAbility) ability); + } } } } @@ -2034,6 +2066,18 @@ public int gainLife(int amount, Game game, Ability source) { return 0; } + @Override + public void exchangeLife(Player player, Ability source, Game game) { + int lifePlayer1 = getLife(); + int lifePlayer2 = player.getLife(); + if ((lifePlayer1 != lifePlayer2 && this.isLifeTotalCanChange() && player.isLifeTotalCanChange()) + && (lifePlayer1 >= lifePlayer2 || (this.isCanGainLife() && player.isCanLoseLife())) + && (lifePlayer1 <= lifePlayer2 || (this.isCanLoseLife() && player.isCanGainLife()))) { + this.setLife(lifePlayer2, game, source); + player.setLife(lifePlayer1, game, source); + } + } + @Override public int damage(int damage, UUID attackerId, Ability source, Game game) { return doDamage(damage, attackerId, source, game, false, true, null); @@ -2054,75 +2098,76 @@ private int doDamage(int damage, UUID attackerId, Ability source, Game game, boo return 0; } - if (damage > 0) { - if (canDamage(game.getObject(attackerId), game)) { - GameEvent event = new DamagePlayerEvent(playerId, - attackerId, playerId, damage, preventable, combatDamage); - event.setAppliedEffects(appliedEffects); - if (!game.replaceEvent(event)) { - int actualDamage = event.getAmount(); - if (actualDamage > 0) { - UUID sourceControllerId = null; - Abilities sourceAbilities = null; - MageObject attacker = game.getPermanentOrLKIBattlefield(attackerId); - if (attacker == null) { - StackObject stackObject = game.getStack().getStackObject(attackerId); - if (stackObject != null) { - attacker = stackObject.getStackAbility().getSourceObject(game); - } else { - attacker = game.getObject(attackerId); - } - if (attacker instanceof Spell) { - sourceAbilities = ((Spell) attacker).getAbilities(game); - sourceControllerId = ((Spell) attacker).getControllerId(); - } else if (attacker instanceof Card) { - sourceAbilities = ((Card) attacker).getAbilities(game); - sourceControllerId = ((Card) attacker).getOwnerId(); - } else if (attacker instanceof CommandObject) { - sourceControllerId = ((CommandObject) attacker).getControllerId(); - sourceAbilities = attacker.getAbilities(); - } - } else { - sourceAbilities = ((Permanent) attacker).getAbilities(game); - sourceControllerId = ((Permanent) attacker).getControllerId(); - } - if (sourceAbilities != null && sourceAbilities.containsKey(InfectAbility.getInstance().getId())) { - addCounters(CounterType.POISON.createInstance(actualDamage), sourceControllerId, source, game); - } else { - GameEvent damageToLifeLossEvent = new GameEvent(GameEvent.EventType.DAMAGE_CAUSES_LIFE_LOSS, - playerId, source, playerId, actualDamage, combatDamage); - if (!game.replaceEvent(damageToLifeLossEvent)) { - this.loseLife(damageToLifeLossEvent.getAmount(), game, source, combatDamage, attackerId); - } - } - if (sourceAbilities != null && sourceAbilities.containsKey(LifelinkAbility.getInstance().getId())) { - if (combatDamage) { - game.getPermanent(attackerId).markLifelink(actualDamage); - } else { - Player player = game.getPlayer(sourceControllerId); - player.gainLife(actualDamage, game, source); - } - } - // Unstable ability - Earl of Squirrel - if (sourceAbilities != null && sourceAbilities.containsKey(SquirrellinkAbility.getInstance().getId())) { - Player player = game.getPlayer(sourceControllerId); - new SquirrelToken().putOntoBattlefield(actualDamage, game, source, player.getId()); - } - DamagedEvent damagedEvent = new DamagedPlayerEvent(playerId, attackerId, playerId, actualDamage, combatDamage); - game.fireEvent(damagedEvent); - game.getState().addSimultaneousDamage(damagedEvent, game); - return actualDamage; - } - } + if (damage < 1) { + return 0; + } + if (!canDamage(game.getObject(attackerId), game)) { + MageObject sourceObject = game.getObject(attackerId); + game.informPlayers(damage + " damage " + + (sourceObject == null ? "" : "from " + sourceObject.getLogName()) + + " to " + getLogName() + + (damage > 1 ? " were" : "was") + " prevented because of protection"); + return 0; + } + DamageEvent event = new DamagePlayerEvent(playerId, attackerId, playerId, damage, preventable, combatDamage); + event.setAppliedEffects(appliedEffects); + if (game.replaceEvent(event)) { + return 0; + } + int actualDamage = event.getAmount(); + if (actualDamage < 1) { + return 0; + } + UUID sourceControllerId = null; + Abilities sourceAbilities = null; + MageObject attacker = game.getPermanentOrLKIBattlefield(attackerId); + if (attacker == null) { + StackObject stackObject = game.getStack().getStackObject(attackerId); + if (stackObject != null) { + attacker = stackObject.getStackAbility().getSourceObject(game); } else { - MageObject sourceObject = game.getObject(attackerId); - game.informPlayers(damage + " damage " - + (sourceObject == null ? "" : "from " + sourceObject.getLogName()) - + " to " + getLogName() - + (damage > 1 ? " were" : "was") + " prevented because of protection"); + attacker = game.getObject(attackerId); + } + if (attacker instanceof Spell) { + sourceAbilities = ((Spell) attacker).getAbilities(game); + sourceControllerId = ((Spell) attacker).getControllerId(); + } else if (attacker instanceof Card) { + sourceAbilities = ((Card) attacker).getAbilities(game); + sourceControllerId = ((Card) attacker).getOwnerId(); + } else if (attacker instanceof CommandObject) { + sourceControllerId = ((CommandObject) attacker).getControllerId(); + sourceAbilities = attacker.getAbilities(); } + } else { + sourceAbilities = ((Permanent) attacker).getAbilities(game); + sourceControllerId = ((Permanent) attacker).getControllerId(); } - return 0; + if (event.isAsThoughInfect() || (sourceAbilities != null && sourceAbilities.containsKey(InfectAbility.getInstance().getId()))) { + addCounters(CounterType.POISON.createInstance(actualDamage), sourceControllerId, source, game); + } else { + GameEvent damageToLifeLossEvent = new GameEvent(GameEvent.EventType.DAMAGE_CAUSES_LIFE_LOSS, + playerId, source, playerId, actualDamage, combatDamage); + if (!game.replaceEvent(damageToLifeLossEvent)) { + this.loseLife(damageToLifeLossEvent.getAmount(), game, source, combatDamage, attackerId); + } + } + if (sourceAbilities != null && sourceAbilities.containsKey(LifelinkAbility.getInstance().getId())) { + if (combatDamage) { + game.getPermanent(attackerId).markLifelink(actualDamage); + } else { + Player player = game.getPlayer(sourceControllerId); + player.gainLife(actualDamage, game, source); + } + } + // Unstable ability - Earl of Squirrel + if (sourceAbilities != null && sourceAbilities.containsKey(SquirrellinkAbility.getInstance().getId())) { + Player player = game.getPlayer(sourceControllerId); + new SquirrelToken().putOntoBattlefield(actualDamage, game, source, player.getId()); + } + DamagedEvent damagedEvent = new DamagedPlayerEvent(playerId, attackerId, playerId, actualDamage, combatDamage); + game.fireEvent(damagedEvent); + game.getState().addSimultaneousDamage(damagedEvent, game); + return actualDamage; } @Override @@ -2685,7 +2730,7 @@ private boolean handleCastableCardsWhileLibrarySearching(Library library, Game g } // only humans can use it - if (!targetPlayer.isHuman() && !targetPlayer.isTestMode()) { + if (targetPlayer.isComputer()) { return false; } @@ -2713,7 +2758,9 @@ private boolean handleCastableCardsWhileLibrarySearching(Library library, Game g // casting selected card // TODO: fix costs (why is Panglacial Wurm automatically accepting payment?) + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); targetPlayer.cast(targetPlayer.chooseAbilityForCast(card, game, false), game, false, null); + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); castableCards.remove(card.getId()); casted = true; } @@ -3834,7 +3881,7 @@ private void addCostTargetOptions(List options, Ability option, int tar } @Override - public boolean isTestMode() { + public boolean isTestsMode() { return isTestMode; } @@ -4073,14 +4120,14 @@ public boolean moveCards(Cards cards, Zone toZone, Ability source, Game game) { } @Override - public boolean moveCards(Set cards, Zone toZone, + public boolean moveCards(Set cards, Zone toZone, Ability source, Game game ) { return moveCards(cards, toZone, source, game, false, false, false, null); } @Override - public boolean moveCards(Set cards, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects) { + public boolean moveCards(Set cards, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects) { if (cards.isEmpty()) { return true; } @@ -4180,7 +4227,9 @@ public boolean moveCards(Set cards, Zone toZone, Ability source, Game game @Override public boolean moveCardsToExile(Card card, Ability source, Game game, boolean withName, UUID exileId, String exileZoneName) { Set cards = new HashSet<>(); - cards.add(card); + if (card != null) { + cards.add(card); + } return moveCardsToExile(cards, source, game, withName, exileId, exileZoneName); } @@ -4222,13 +4271,13 @@ public boolean moveCardToHandWithInfo(Card card, Ability source, Game game, bool } @Override - public Set moveCardsToGraveyardWithInfo(Set allCards, Ability source, Game game, Zone fromZone) { + public Set moveCardsToGraveyardWithInfo(Set allCards, Ability source, Game game, Zone fromZone) { Set movedCards = new LinkedHashSet<>(); while (!allCards.isEmpty()) { // identify cards from one owner Cards cards = new CardsImpl(); UUID ownerId = null; - for (Iterator it = allCards.iterator(); it.hasNext(); ) { + for (Iterator it = allCards.iterator(); it.hasNext(); ) { Card card = it.next(); if (cards.isEmpty()) { ownerId = card.getOwnerId(); @@ -4251,7 +4300,7 @@ public Set moveCardsToGraveyardWithInfo(Set allCards, Ability source if (userData.askMoveToGraveOrder()) { if (cards.size() > 1) { chooseOrder = choosingPlayer.chooseUse(Outcome.Neutral, - "Would you like to choose the order the cards go to graveyard?", source, game); + "Choose the order in which the cards go to the graveyard?", source, game); } } if (chooseOrder) { @@ -4366,7 +4415,7 @@ public boolean moveCardToCommandWithInfo(Card card, Ability source, Game game, Z sb.append("from ").append(fromZone.toString().toLowerCase(Locale.ENGLISH)).append(' '); } if (card.isOwnedBy(getId())) { - sb.append(" to his or her command zone"); + sb.append(" to their command zone"); } else { Player player = game.getPlayer(card.getOwnerId()); if (player != null) { @@ -4672,7 +4721,7 @@ public SpellAbility chooseAbilityForCast(Card card, Game game, boolean noMana) { @Override public String toString() { - return getName() + " " + super.toString(); + return getName() + " (" + super.getClass().getSimpleName() + ")"; } } diff --git a/Mage/src/main/java/mage/players/StubPlayer.java b/Mage/src/main/java/mage/players/StubPlayer.java index 1b86856123c1..ad47e96d2a1f 100644 --- a/Mage/src/main/java/mage/players/StubPlayer.java +++ b/Mage/src/main/java/mage/players/StubPlayer.java @@ -11,6 +11,7 @@ import mage.cards.Cards; import mage.cards.decks.Deck; import mage.choices.Choice; +import mage.constants.MultiAmountType; import mage.constants.Outcome; import mage.constants.RangeOfInfluence; import mage.filter.FilterMana; @@ -205,6 +206,11 @@ public int getAmount(int min, int max, String message, Game game) { return 0; } + @Override + public List getMultiAmount(Outcome outcome, List messages, int min, int max, MultiAmountType type, Game game) { + return null; + } + @Override public void sideboard(Match match, Deck deck) { diff --git a/Mage/src/main/java/mage/target/Target.java b/Mage/src/main/java/mage/target/Target.java index ae0db1ccb5f9..72f5c25a8ae7 100644 --- a/Mage/src/main/java/mage/target/Target.java +++ b/Mage/src/main/java/mage/target/Target.java @@ -147,4 +147,8 @@ public interface Target extends Serializable { String getChooseHint(); void setEventReporting(boolean shouldReport); + + int getSize(); + + boolean contains(UUID targetId); } diff --git a/Mage/src/main/java/mage/target/TargetAmount.java b/Mage/src/main/java/mage/target/TargetAmount.java index 72012fe9d679..692cc0cb7416 100644 --- a/Mage/src/main/java/mage/target/TargetAmount.java +++ b/Mage/src/main/java/mage/target/TargetAmount.java @@ -5,6 +5,7 @@ import mage.abilities.dynamicvalue.common.StaticValue; import mage.constants.Outcome; import mage.game.Game; +import mage.players.Player; import java.util.*; import java.util.stream.Collectors; @@ -59,7 +60,7 @@ public boolean doneChosing() { public void clearChosen() { super.clearChosen(); amountWasSet = false; - // remainingAmount = amount; + // remainingAmount = amount; // auto-calced on target remove } public void setAmountDefinition(DynamicValue amount) { @@ -71,6 +72,9 @@ public void setAmount(Ability source, Game game) { amountWasSet = true; } + public int getAmountTotal(Game game, Ability source) { + return amount.calculate(game, source, null); + } @Override public void addTarget(UUID id, int amount, Ability source, Game game, boolean skipEvent) { @@ -92,17 +96,25 @@ public void remove(UUID id) { @Override public boolean chooseTarget(Outcome outcome, UUID playerId, Ability source, Game game) { + Player player = game.getPlayer(playerId); + if (player == null) { + return false; + } + if (!amountWasSet) { setAmount(source, game); } chosen = isChosen(); while (remainingAmount > 0) { + if (!player.canRespond()) { + return chosen; + } if (!getTargetController(game, playerId).chooseTargetAmount(outcome, this, source, game)) { return chosen; } chosen = isChosen(); } - return chosen = true; + return chosen; } @Override @@ -163,4 +175,12 @@ protected void addTargets(TargetAmount target, Set possibleTargets, List= getNumberOfTargets(); + chosen = targets.size() >= getNumberOfTargets(); + do { + if (!targetController.canRespond()) { + return chosen; } - chosen = targets.size() >= getNumberOfTargets(); - if (!player.choose(outcome, this, sourceId, game)) { + if (!targetController.choose(outcome, this, sourceId, game)) { return chosen; } chosen = targets.size() >= getNumberOfTargets(); - } - return chosen = true; + } while (!isChosen() && !doneChosing()); + return chosen; } @Override public boolean chooseTarget(Outcome outcome, UUID playerId, Ability source, Game game) { - Player player = game.getPlayer(playerId); - if (player == null) { + Player targetController = getTargetController(game, playerId); + if (targetController == null) { return false; } List possibleTargets = new ArrayList<>(possibleTargets(source.getSourceId(), playerId, game)); - while (!isChosen() && !doneChosing()) { - if (!player.canRespond()) { - return chosen = targets.size() >= getNumberOfTargets(); + + chosen = targets.size() >= getNumberOfTargets(); + do { + if (!targetController.canRespond()) { + return chosen; } - chosen = targets.size() >= getNumberOfTargets(); if (isRandom()) { - if (!possibleTargets.isEmpty()) { - int index = RandomUtil.nextInt(possibleTargets.size()); - this.addTarget(possibleTargets.get(index), source, game); - possibleTargets.remove(index); - } else { + if (possibleTargets.isEmpty()) { return chosen; } - } else if (!getTargetController(game, playerId).chooseTarget(outcome, this, source, game)) { + // find valid target + while (!possibleTargets.isEmpty()) { + int index = RandomUtil.nextInt(possibleTargets.size()); + if (this.canTarget(playerId, possibleTargets.get(index), source, game)) { + this.addTarget(possibleTargets.get(index), source, game); + possibleTargets.remove(index); + break; + } else { + possibleTargets.remove(index); + } + } + } else if (!targetController.chooseTarget(outcome, this, source, game)) { return chosen; } chosen = targets.size() >= getNumberOfTargets(); - } - return chosen = true; + } while (!isChosen() && !doneChosing()); + + return chosen; } @Override @@ -574,4 +574,14 @@ public String getChooseHint() { public void setEventReporting(boolean shouldReport) { this.shouldReportEvents = shouldReport; } + + @Override + public int getSize() { + return targets.size(); + } + + @Override + public boolean contains(UUID targetId) { + return targets.containsKey(targetId); + } } diff --git a/Mage/src/main/java/mage/target/TargetPermanent.java b/Mage/src/main/java/mage/target/TargetPermanent.java index 65a8e26e33c6..fbeef9d1f775 100644 --- a/Mage/src/main/java/mage/target/TargetPermanent.java +++ b/Mage/src/main/java/mage/target/TargetPermanent.java @@ -28,7 +28,11 @@ public TargetPermanent(FilterPermanent filter) { } public TargetPermanent(int numTargets, FilterPermanent filter) { - this(numTargets, numTargets, filter, false); + this(numTargets, numTargets, filter); + } + + public TargetPermanent(int minNumTargets, int maxNumTargets, FilterPermanent filter) { + this(minNumTargets, maxNumTargets, filter, false); } public TargetPermanent(int minNumTargets, int maxNumTargets, FilterPermanent filter, boolean notTarget) { @@ -75,7 +79,7 @@ public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) public boolean canTarget(UUID controllerId, UUID id, UUID sourceId, Game game, boolean flag) { Permanent permanent = game.getPermanent(id); - return permanent != null && filter.match(permanent, sourceId, controllerId, game); + return filter.match(permanent, sourceId, controllerId, game); } @Override diff --git a/Mage/src/main/java/mage/target/TargetPlayer.java b/Mage/src/main/java/mage/target/TargetPlayer.java index 2b7e1265512f..e49673aac15a 100644 --- a/Mage/src/main/java/mage/target/TargetPlayer.java +++ b/Mage/src/main/java/mage/target/TargetPlayer.java @@ -21,6 +21,10 @@ public TargetPlayer() { this(1, 1, false); } + public TargetPlayer(FilterPlayer filter) { + this(1, 1, false, filter); + } + public TargetPlayer(int numTargets) { this(numTargets, numTargets, false); } @@ -137,7 +141,7 @@ public boolean isLegal(Ability source, Game game) { @Override public boolean canTarget(UUID id, Game game) { Player player = game.getPlayer(id); - return player != null && filter.match(player, game); + return filter.match(player, game); } @Override diff --git a/Mage/src/main/java/mage/target/TargetSpell.java b/Mage/src/main/java/mage/target/TargetSpell.java index 0fe66adb0a63..b959d8697ea7 100644 --- a/Mage/src/main/java/mage/target/TargetSpell.java +++ b/Mage/src/main/java/mage/target/TargetSpell.java @@ -61,7 +61,7 @@ public boolean canTarget(UUID id, Ability source, Game game) { return false; } Spell spell = game.getStack().getSpell(id); - return spell != null && filter.match(spell, source.getSourceId(), source.getControllerId(), game); + return filter.match(spell, source.getSourceId(), source.getControllerId(), game); } @Override diff --git a/Mage/src/main/java/mage/target/TargetStackObject.java b/Mage/src/main/java/mage/target/TargetStackObject.java index c0269fca611b..11c084fba937 100644 --- a/Mage/src/main/java/mage/target/TargetStackObject.java +++ b/Mage/src/main/java/mage/target/TargetStackObject.java @@ -51,7 +51,7 @@ public FilterStackObject getFilter() { @Override public boolean canTarget(UUID id, Ability source, Game game) { StackObject stackObject = game.getStack().getStackObject(id); - return stackObject != null && filter.match(stackObject, source.getSourceId(), source.getControllerId(), game); + return filter.match(stackObject, source.getSourceId(), source.getControllerId(), game); } @Override diff --git a/Mage/src/main/java/mage/target/Targets.java b/Mage/src/main/java/mage/target/Targets.java index 09d0b080aa3d..2c2a0a4796c0 100644 --- a/Mage/src/main/java/mage/target/Targets.java +++ b/Mage/src/main/java/mage/target/Targets.java @@ -4,6 +4,7 @@ import mage.constants.Outcome; import mage.game.Game; import mage.game.events.GameEvent; +import mage.players.Player; import mage.target.targetpointer.*; import org.apache.log4j.Logger; @@ -65,6 +66,7 @@ public boolean chooseTargets(Outcome outcome, UUID playerId, Ability source, boo if (!canChoose(source.getSourceId(), playerId, game)) { return false; } + //int state = game.bookmarkState(); while (!isChosen()) { Target target = this.getUnchosen().get(0); diff --git a/Mage/src/main/java/mage/target/common/TargetAnyTarget.java b/Mage/src/main/java/mage/target/common/TargetAnyTarget.java index f5f9f9393d5b..1fd701564af5 100644 --- a/Mage/src/main/java/mage/target/common/TargetAnyTarget.java +++ b/Mage/src/main/java/mage/target/common/TargetAnyTarget.java @@ -62,7 +62,7 @@ public boolean canTarget(UUID id, Game game) { return filter.match(permanent, game); } Player player = game.getPlayer(id); - return player != null && filter.match(player, game); + return filter.match(player, game); } @Override @@ -88,7 +88,7 @@ public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) if (permanent != null) { return filter.match(permanent, game); } - return player != null && filter.match(player, game); + return filter.match(player, game); } /** @@ -143,7 +143,7 @@ public boolean canChoose(UUID sourceControllerId, Game game) { for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) { Player player = game.getPlayer(playerId); - if (player != null && filter.match(player, game)) { + if (filter.match(player, game)) { count++; if (count >= this.minNumberOfTargets) { return true; @@ -193,7 +193,7 @@ public Set possibleTargets(UUID sourceControllerId, Game game) { for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) { Player player = game.getPlayer(playerId); - if (player != null && filter.match(player, game)) { + if (filter.match(player, game)) { possibleTargets.add(playerId); } } diff --git a/Mage/src/main/java/mage/target/common/TargetCardInHand.java b/Mage/src/main/java/mage/target/common/TargetCardInHand.java index 11520478fc7e..d60d8a5ad08e 100644 --- a/Mage/src/main/java/mage/target/common/TargetCardInHand.java +++ b/Mage/src/main/java/mage/target/common/TargetCardInHand.java @@ -48,7 +48,7 @@ public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) { Card card = game.getCard(id); return game.getState().getZone(id) == Zone.HAND && game.getState().getPlayersInRange(getTargetController() == null ? playerId : getTargetController(), game).contains(game.getOwnerId(id)) - && card != null && filter.match(card, game); + && filter.match(card, game); } @Override diff --git a/Mage/src/main/java/mage/target/common/TargetCardInLibrary.java b/Mage/src/main/java/mage/target/common/TargetCardInLibrary.java index bbbd6d63a501..5fa73b53a113 100644 --- a/Mage/src/main/java/mage/target/common/TargetCardInLibrary.java +++ b/Mage/src/main/java/mage/target/common/TargetCardInLibrary.java @@ -72,27 +72,25 @@ public boolean choose(Outcome outcome, UUID playerId, UUID targetPlayerId, Game } cards.sort(Comparator.comparing(MageObject::getName)); Cards cardsId = new CardsImpl(); - cards.forEach((card) -> { - cardsId.add(card); - }); + cards.forEach(cardsId::add); - while (!isChosen() && !doneChosing()) { + chosen = targets.size() >= getMinNumberOfTargets(); + do { if (!player.canRespond()) { - return chosen = targets.size() >= minNumberOfTargets; + return chosen; } - chosen = targets.size() >= minNumberOfTargets; if (!player.chooseTarget(outcome, cardsId, this, null, game)) { return chosen; } - chosen = targets.size() >= minNumberOfTargets; - } - return chosen = true; + chosen = targets.size() >= getMinNumberOfTargets(); + } while (!isChosen() && !doneChosing()); + return chosen; } @Override public boolean canTarget(UUID id, Ability source, Game game) { Card card = game.getPlayer(source.getControllerId()).getLibrary().getCard(id, game); - return card != null && filter.match(card, source.getSourceId(), source.getControllerId(), game); + return filter.match(card, source.getSourceId(), source.getControllerId(), game); } @Override diff --git a/Mage/src/main/java/mage/target/common/TargetCardInTargetPlayersGraveyard.java b/Mage/src/main/java/mage/target/common/TargetCardInTargetPlayersGraveyard.java new file mode 100644 index 000000000000..419fcd44722f --- /dev/null +++ b/Mage/src/main/java/mage/target/common/TargetCardInTargetPlayersGraveyard.java @@ -0,0 +1,36 @@ +package mage.target.common; + +import mage.abilities.Ability; +import mage.cards.Card; +import mage.filter.StaticFilters; +import mage.game.Game; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public class TargetCardInTargetPlayersGraveyard extends TargetCardInGraveyard { + + public TargetCardInTargetPlayersGraveyard(int targets) { + super(0, targets, StaticFilters.FILTER_CARD); + } + + private TargetCardInTargetPlayersGraveyard(final TargetCardInTargetPlayersGraveyard target) { + super(target); + } + + @Override + public boolean canTarget(UUID id, Ability source, Game game) { + if (!super.canTarget(id, source, game)) { + return false; + } + Card card = game.getCard(id); + return card != null && card.isOwnedBy(source.getFirstTarget()); + } + + @Override + public TargetCardInTargetPlayersGraveyard copy() { + return new TargetCardInTargetPlayersGraveyard(this); + } +} diff --git a/Mage/src/main/java/mage/target/common/TargetControlledCreatureEachColor.java b/Mage/src/main/java/mage/target/common/TargetControlledCreatureEachColor.java new file mode 100644 index 000000000000..e47df1e4de92 --- /dev/null +++ b/Mage/src/main/java/mage/target/common/TargetControlledCreatureEachColor.java @@ -0,0 +1,76 @@ +package mage.target.common; + +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.common.ColorAssignment; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.util.CardUtil; + +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public class TargetControlledCreatureEachColor extends TargetControlledPermanent { + + private final ColorAssignment colorAssigner; + + private static final FilterControlledPermanent makeFilter(String colors) { + List objectColors + = Arrays.stream(colors.split("")) + .map(ObjectColor::new) + .collect(Collectors.toList()); + FilterControlledPermanent filter + = new FilterControlledCreaturePermanent(CardUtil.concatWithAnd( + objectColors + .stream() + .map(ObjectColor::getDescription) + .map(s -> CardUtil.addArticle(s) + " creature") + .collect(Collectors.toList()) + )); + filter.add(Predicates.or(objectColors.stream().map(ColorPredicate::new).collect(Collectors.toList()))); + return filter; + } + + public TargetControlledCreatureEachColor(String colors) { + super(colors.length(), makeFilter(colors)); + colorAssigner = new ColorAssignment(colors.split("")); + } + + private TargetControlledCreatureEachColor(final TargetControlledCreatureEachColor target) { + super(target); + this.colorAssigner = target.colorAssigner; + } + + @Override + public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) { + if (!super.canTarget(playerId, id, source, game)) { + return false; + } + Permanent permanent = game.getPermanent(id); + if (permanent == null) { + return false; + } + if (this.getTargets().isEmpty()) { + return true; + } + Cards cards = new CardsImpl(this.getTargets()); + cards.add(permanent); + return colorAssigner.getRoleCount(cards, game) >= cards.size(); + } + + @Override + public TargetControlledCreatureEachColor copy() { + return new TargetControlledCreatureEachColor(this); + } +} diff --git a/Mage/src/main/java/mage/target/common/TargetCreatureOrPlayer.java b/Mage/src/main/java/mage/target/common/TargetCreatureOrPlayer.java index a8ef0efe331e..8dc7dd5c1038 100644 --- a/Mage/src/main/java/mage/target/common/TargetCreatureOrPlayer.java +++ b/Mage/src/main/java/mage/target/common/TargetCreatureOrPlayer.java @@ -62,7 +62,7 @@ public boolean canTarget(UUID id, Game game) { return filter.match(permanent, game); } Player player = game.getPlayer(id); - return player != null && filter.match(player, game); + return filter.match(player, game); } @Override @@ -88,7 +88,7 @@ public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) if (permanent != null) { return filter.match(permanent, game); } - return player != null && filter.match(player, game); + return filter.match(player, game); } /** @@ -139,7 +139,7 @@ public boolean canChoose(UUID sourceControllerId, Game game) { int count = 0; for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) { Player player = game.getPlayer(playerId); - if (player != null && filter.match(player, game)) { + if (filter.match(player, game)) { count++; if (count >= this.minNumberOfTargets) { return true; diff --git a/Mage/src/main/java/mage/target/common/TargetDiscard.java b/Mage/src/main/java/mage/target/common/TargetDiscard.java index a88be8445f9d..63b9eb7a673f 100644 --- a/Mage/src/main/java/mage/target/common/TargetDiscard.java +++ b/Mage/src/main/java/mage/target/common/TargetDiscard.java @@ -10,7 +10,7 @@ import java.util.UUID; import mage.filter.StaticFilters; -import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.card.OwnerIdPredicate; /** * @@ -47,7 +47,7 @@ public TargetDiscard(final TargetDiscard target) { @Override public boolean canTarget(UUID id, Ability source, Game game) { Card card = game.getPlayer(playerId).getHand().get(id, game); - return card != null && filter.match(card, source.getControllerId(), game); + return filter.match(card, source.getControllerId(), game); } @Override diff --git a/Mage/src/main/java/mage/target/common/TargetOpponentOrPlaneswalker.java b/Mage/src/main/java/mage/target/common/TargetOpponentOrPlaneswalker.java index 20f4fa974e25..2672f52d796d 100644 --- a/Mage/src/main/java/mage/target/common/TargetOpponentOrPlaneswalker.java +++ b/Mage/src/main/java/mage/target/common/TargetOpponentOrPlaneswalker.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.target.common; import mage.filter.common.FilterOpponentOrPlaneswalker; diff --git a/Mage/src/main/java/mage/target/common/TargetOpponentsCreaturePermanent.java b/Mage/src/main/java/mage/target/common/TargetOpponentsCreaturePermanent.java index 16dafb435869..c8579d9c1864 100644 --- a/Mage/src/main/java/mage/target/common/TargetOpponentsCreaturePermanent.java +++ b/Mage/src/main/java/mage/target/common/TargetOpponentsCreaturePermanent.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.target.common; import mage.filter.common.FilterOpponentsCreaturePermanent; diff --git a/Mage/src/main/java/mage/target/common/TargetPermanentAmount.java b/Mage/src/main/java/mage/target/common/TargetPermanentAmount.java index 2556df419da6..50b6d967a8d0 100644 --- a/Mage/src/main/java/mage/target/common/TargetPermanentAmount.java +++ b/Mage/src/main/java/mage/target/common/TargetPermanentAmount.java @@ -51,7 +51,7 @@ public Filter getFilter() { @Override public boolean canTarget(UUID objectId, Game game) { Permanent permanent = game.getPermanent(objectId); - return permanent != null && filter.match(permanent, game); + return filter.match(permanent, game); } @Override diff --git a/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayer.java b/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayer.java index 6a99354f104c..40433a2c9ac7 100644 --- a/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayer.java +++ b/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayer.java @@ -64,7 +64,7 @@ public boolean canTarget(UUID id, Game game) { return filter.match(permanent, game); } Player player = game.getPlayer(id); - return player != null && filter.match(player, game); + return filter.match(player, game); } @Override @@ -102,7 +102,7 @@ public boolean canTarget(UUID id, Ability source, Game game) { if (permanent != null) { return filter.match(permanent, game); } - return player != null && filter.match(player, game); + return filter.match(player, game); } /** @@ -196,7 +196,7 @@ public Set possibleTargets(UUID sourceControllerId, Game game) { Set possibleTargets = new HashSet<>(); for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) { Player player = game.getPlayer(playerId); - if (player != null && filter.match(player, game)) { + if (filter.match(player, game)) { possibleTargets.add(playerId); } } diff --git a/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayerAmount.java b/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayerAmount.java index 3971c1aa5c56..f6852c40132f 100644 --- a/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayerAmount.java +++ b/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayerAmount.java @@ -54,7 +54,7 @@ public boolean canTarget(UUID objectId, Game game) { return filter.match(permanent, game); } Player player = game.getPlayer(objectId); - return player != null && filter.match(player, game); + return filter.match(player, game); } @Override @@ -83,7 +83,7 @@ public boolean canTarget(UUID objectId, Ability source, Game game) { if (permanent != null) { return filter.match(permanent, game); } - return player != null && filter.match(player, game); + return filter.match(player, game); } @Override diff --git a/Mage/src/main/java/mage/target/common/TargetPermanentOrSuspendedCard.java b/Mage/src/main/java/mage/target/common/TargetPermanentOrSuspendedCard.java index 50e37b730063..186d5bc6b345 100644 --- a/Mage/src/main/java/mage/target/common/TargetPermanentOrSuspendedCard.java +++ b/Mage/src/main/java/mage/target/common/TargetPermanentOrSuspendedCard.java @@ -118,7 +118,7 @@ public boolean canTarget(UUID id, Game game) { return filter.match(permanent, game); } Card card = game.getExile().getCard(id, game); - return card != null && filter.match(card, game); + return filter.match(card, game); } @Override @@ -134,7 +134,7 @@ public boolean canTarget(UUID id, Ability source, Game game) { } } Card card = game.getExile().getCard(id, game); - return card != null && filter.match(card, game); + return filter.match(card, game); } @Override diff --git a/Mage/src/main/java/mage/target/common/TargetPlayerOrPlaneswalker.java b/Mage/src/main/java/mage/target/common/TargetPlayerOrPlaneswalker.java index 27101e8ad397..3eb6446aa3ac 100644 --- a/Mage/src/main/java/mage/target/common/TargetPlayerOrPlaneswalker.java +++ b/Mage/src/main/java/mage/target/common/TargetPlayerOrPlaneswalker.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.target.common; import mage.filter.common.FilterPlayerOrPlaneswalker; diff --git a/Mage/src/main/java/mage/target/common/TargetSpellOrPermanent.java b/Mage/src/main/java/mage/target/common/TargetSpellOrPermanent.java index c8b7749875ee..3578c44ffc29 100644 --- a/Mage/src/main/java/mage/target/common/TargetSpellOrPermanent.java +++ b/Mage/src/main/java/mage/target/common/TargetSpellOrPermanent.java @@ -1,32 +1,3 @@ -/* - * - * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of BetaSteward_at_googlemail.com. - * - */ package mage.target.common; import mage.MageObject; @@ -54,7 +25,11 @@ public class TargetSpellOrPermanent extends TargetImpl { protected FilterSpellOrPermanent filter; public TargetSpellOrPermanent() { - this(1, 1); + this(1); + } + + public TargetSpellOrPermanent(FilterSpellOrPermanent filter) { + this(1, 1, filter, false); } public TargetSpellOrPermanent(int numTargets) { @@ -105,7 +80,7 @@ public boolean canTarget(UUID id, Game game) { return filter.match(permanent, game); } Spell spell = game.getStack().getSpell(id); - return spell != null && filter.match(spell, game); + return filter.match(spell, game); } @Override diff --git a/Mage/src/main/java/mage/target/common/TargetTriggeredAbility.java b/Mage/src/main/java/mage/target/common/TargetTriggeredAbility.java index 1c99bc642e88..54a7e3513d90 100644 --- a/Mage/src/main/java/mage/target/common/TargetTriggeredAbility.java +++ b/Mage/src/main/java/mage/target/common/TargetTriggeredAbility.java @@ -37,8 +37,8 @@ public boolean canTarget(UUID id, Ability source, Game game) { } StackObject stackObject = game.getStack().getStackObject(id); - return stackObject.getStackAbility() != null - && (stackObject.getStackAbility() instanceof TriggeredAbility) + return stackObject != null + && stackObject.getStackAbility() instanceof TriggeredAbility && source != null && stackObject.getStackAbility().isControlledBy(source.getControllerId()); } @@ -51,8 +51,7 @@ public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { @Override public boolean canChoose(UUID sourceControllerId, Game game) { for (StackObject stackObject : game.getStack()) { - if (stackObject.getStackAbility() != null - && stackObject.getStackAbility() instanceof TriggeredAbility + if (stackObject.getStackAbility() instanceof TriggeredAbility && stackObject.getStackAbility().isControlledBy(sourceControllerId)) { return true; } @@ -69,8 +68,7 @@ public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game ga public Set possibleTargets(UUID sourceControllerId, Game game) { Set possibleTargets = new HashSet<>(); for (StackObject stackObject : game.getStack()) { - if (stackObject.getStackAbility() != null - && stackObject.getStackAbility() instanceof TriggeredAbility + if (stackObject.getStackAbility() instanceof TriggeredAbility && stackObject.getStackAbility().isControlledBy(sourceControllerId)) { possibleTargets.add(stackObject.getStackAbility().getId()); } diff --git a/Mage/src/main/java/mage/target/targetadjustment/TargetAdjuster.java b/Mage/src/main/java/mage/target/targetadjustment/TargetAdjuster.java index d3e84a8e9f05..af173fa18f0e 100644 --- a/Mage/src/main/java/mage/target/targetadjustment/TargetAdjuster.java +++ b/Mage/src/main/java/mage/target/targetadjustment/TargetAdjuster.java @@ -4,10 +4,10 @@ import mage.game.Game; /** - * * @author TheElk801 */ public interface TargetAdjuster { void adjustTargets(Ability ability, Game game); + } diff --git a/Mage/src/main/java/mage/target/targetadjustment/XCMCGraveyardAdjuster.java b/Mage/src/main/java/mage/target/targetadjustment/XCMCGraveyardAdjuster.java index 617da884a1cd..57f86272ee9a 100644 --- a/Mage/src/main/java/mage/target/targetadjustment/XCMCGraveyardAdjuster.java +++ b/Mage/src/main/java/mage/target/targetadjustment/XCMCGraveyardAdjuster.java @@ -3,7 +3,7 @@ import mage.abilities.Ability; import mage.constants.ComparisonType; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.target.TargetCard; import mage.target.common.TargetCardInGraveyard; @@ -19,7 +19,7 @@ public enum XCMCGraveyardAdjuster implements TargetAdjuster { public void adjustTargets(Ability ability, Game game) { int xValue = ability.getManaCostsToPay().getX(); FilterCard filterCard = ((TargetCard) ability.getTargets().get(0)).getFilter().copy(); - filterCard.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, xValue)); + filterCard.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, xValue)); filterCard.setMessage(filterCard.getMessage().replace('X', (char) xValue)); ability.getTargets().clear(); ability.getTargets().add(new TargetCardInGraveyard(filterCard)); diff --git a/Mage/src/main/java/mage/target/targetadjustment/XCMCPermanentAdjuster.java b/Mage/src/main/java/mage/target/targetadjustment/XCMCPermanentAdjuster.java index bc6b2b577175..a23d9e121331 100644 --- a/Mage/src/main/java/mage/target/targetadjustment/XCMCPermanentAdjuster.java +++ b/Mage/src/main/java/mage/target/targetadjustment/XCMCPermanentAdjuster.java @@ -3,7 +3,7 @@ import mage.abilities.Ability; import mage.constants.ComparisonType; import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.target.TargetPermanent; @@ -21,7 +21,7 @@ public void adjustTargets(Ability ability, Game game) { int minTargets = oldTargetPermanent.getMinNumberOfTargets(); int maxTargets = oldTargetPermanent.getMaxNumberOfTargets(); FilterPermanent permanentFilter = oldTargetPermanent.getFilter().copy(); - permanentFilter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, xValue)); + permanentFilter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, xValue)); ability.getTargets().clear(); ability.getTargets().add(new TargetPermanent(minTargets, maxTargets, permanentFilter, false)); } diff --git a/Mage/src/main/java/mage/target/targetpointer/EachTargetPointer.java b/Mage/src/main/java/mage/target/targetpointer/EachTargetPointer.java new file mode 100644 index 000000000000..2ae5cdc6ab1e --- /dev/null +++ b/Mage/src/main/java/mage/target/targetpointer/EachTargetPointer.java @@ -0,0 +1,132 @@ +package mage.target.targetpointer; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.cards.Card; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.Target; + +import java.util.*; +import java.util.stream.Collectors; + +public class EachTargetPointer extends TargetPointerImpl { + + private Map zoneChangeCounter = new HashMap<>(); + + public static EachTargetPointer getInstance() { + return new EachTargetPointer(); + } + + public EachTargetPointer() { + super(); + } + + public EachTargetPointer(final EachTargetPointer targetPointer) { + super(targetPointer); + + this.zoneChangeCounter = new HashMap<>(); + for (Map.Entry entry : targetPointer.zoneChangeCounter.entrySet()) { + this.zoneChangeCounter.put(entry.getKey(), entry.getValue()); + } + } + + @Override + public void init(Game game, Ability source) { + if (!source.getTargets().isEmpty()) { + for (UUID target : source + .getTargets() + .stream() + .map(Target::getTargets) + .flatMap(Collection::stream) + .collect(Collectors.toList())) { + Card card = game.getCard(target); + if (card != null) { + this.zoneChangeCounter.put(target, card.getZoneChangeCounter(game)); + } + } + } + } + + @Override + public List getTargets(Game game, Ability source) { + List target = new ArrayList<>(); + if (!source.getTargets().isEmpty()) { + for (UUID targetId : source + .getTargets() + .stream() + .map(Target::getTargets) + .flatMap(Collection::stream) + .collect(Collectors.toList())) { + Card card = game.getCard(targetId); + if (card != null && zoneChangeCounter.containsKey(targetId) + && card.getZoneChangeCounter(game) != zoneChangeCounter.get(targetId)) { + // but no longer if new permanent is already on the battlefield + Permanent permanent = game.getPermanentOrLKIBattlefield(targetId); + if (permanent == null || permanent.getZoneChangeCounter(game) != zoneChangeCounter.get(targetId)) { + continue; + } + } + target.add(targetId); + } + } + return target; + } + + @Override + public UUID getFirst(Game game, Ability source) { + UUID targetId = source.getFirstTarget(); + if (zoneChangeCounter.containsKey(targetId)) { + Card card = game.getCard(targetId); + if (card != null && zoneChangeCounter.containsKey(targetId) + && card.getZoneChangeCounter(game) != zoneChangeCounter.get(targetId)) { + // because if dies trigger has to trigger as permanent has already moved zone, we have to check if target was on the battlefield immed. before + // but no longer if new permanent is already on the battlefield + Permanent permanent = game.getPermanentOrLKIBattlefield(targetId); + if (permanent == null || permanent.getZoneChangeCounter(game) != zoneChangeCounter.get(targetId)) { + return null; + } + } + } + return targetId; + } + + @Override + public EachTargetPointer copy() { + return new EachTargetPointer(this); + } + + @Override + public FixedTarget getFixedTarget(Game game, Ability source) { + this.init(game, source); + UUID firstId = getFirst(game, source); + if (firstId != null) { + return new FixedTarget(firstId, game.getState().getZoneChangeCounter(firstId)); + } + return null; + + } + + @Override + public Permanent getFirstTargetPermanentOrLKI(Game game, Ability source) { + UUID targetId = source.getFirstTarget(); + Permanent permanent; + if (zoneChangeCounter.containsKey(targetId)) { + permanent = game.getPermanent(targetId); + if (permanent != null && permanent.getZoneChangeCounter(game) == zoneChangeCounter.get(targetId)) { + return permanent; + } + MageObject mageObject = game.getLastKnownInformation(targetId, Zone.BATTLEFIELD, zoneChangeCounter.get(targetId)); + if (mageObject instanceof Permanent) { + return (Permanent) mageObject; + } + } else { + permanent = game.getPermanent(targetId); + if (permanent == null) { + permanent = (Permanent) game.getLastKnownInformation(targetId, Zone.BATTLEFIELD); + } + } + return permanent; + } +} diff --git a/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java b/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java index c1075c4cb598..d0d9abdd0b9f 100644 --- a/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java +++ b/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.target.targetpointer; import mage.MageObject; @@ -14,10 +9,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.UUID; +import java.util.*; /** * @author LevelX2 @@ -66,6 +58,13 @@ public FixedTargets(Set cards, Game game) { this.initialized = true; } + public FixedTargets(Collection morSet, Game game) { + super(); + + targets.addAll(morSet); + this.initialized = true; + } + private FixedTargets(final FixedTargets targetPointer) { super(targetPointer); diff --git a/Mage/src/main/java/mage/target/targetpointer/ThirdTargetPointer.java b/Mage/src/main/java/mage/target/targetpointer/ThirdTargetPointer.java index db986897b0fc..4741534539fa 100644 --- a/Mage/src/main/java/mage/target/targetpointer/ThirdTargetPointer.java +++ b/Mage/src/main/java/mage/target/targetpointer/ThirdTargetPointer.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.target.targetpointer; import mage.MageObject; diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index 77067d962c03..9d138428d079 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -21,6 +21,7 @@ import mage.filter.predicate.mageobject.NamePredicate; import mage.game.CardState; import mage.game.Game; +import mage.game.GameState; import mage.game.command.Commander; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -133,29 +134,37 @@ private static void adjustAbilityCost(Ability ability, int reduceCount) { } private static ManaCosts adjustCost(ManaCosts manaCosts, int reduceCount) { - ManaCosts adjustedCost = new ManaCostsImpl<>(); + ManaCosts newCost = new ManaCostsImpl<>(); // nothing to change if (reduceCount == 0) { for (ManaCost manaCost : manaCosts) { - adjustedCost.add(manaCost.copy()); + newCost.add(manaCost.copy()); } - return adjustedCost; + return newCost; } + // keep same order for costs + Map changedCost = new LinkedHashMap<>(); // must be ordered + List addedCost = new ArrayList<>(); + manaCosts.forEach(manaCost -> { + changedCost.put(manaCost, manaCost); + }); + // remove or save cost if (reduceCount > 0) { int restToReduce = reduceCount; // first run - priority for single option costs (generic) for (ManaCost manaCost : manaCosts) { + + // ignore snow mana if (manaCost instanceof SnowManaCost) { - adjustedCost.add(manaCost); continue; } + // ignore unknown mana if (manaCost.getOptions().size() == 0) { - adjustedCost.add(manaCost); continue; } @@ -171,15 +180,15 @@ private static ManaCosts adjustCost(ManaCosts manaCosts, int if ((colorless - restToReduce) > 0) { // partly reduce int newColorless = colorless - restToReduce; - adjustedCost.add(new GenericManaCost(newColorless)); + changedCost.put(manaCost, new GenericManaCost(newColorless)); restToReduce = 0; } else { // full reduce - ignore cost + changedCost.put(manaCost, null); restToReduce -= colorless; } } else { // nothing to reduce - adjustedCost.add(manaCost.copy()); } } @@ -203,22 +212,21 @@ private static ManaCosts adjustCost(ManaCosts manaCosts, int if ((colorless - restToReduce) > 0) { // partly reduce int newColorless = colorless - restToReduce; - adjustedCost.add(new MonoHybridManaCost(mono.getManaColor(), newColorless)); + changedCost.put(manaCost, new MonoHybridManaCost(mono.getManaColor(), newColorless)); restToReduce = 0; } else { // full reduce - adjustedCost.add(new MonoHybridManaCost(mono.getManaColor(), 0)); + changedCost.put(manaCost, new MonoHybridManaCost(mono.getManaColor(), 0)); restToReduce -= colorless; } } else { // nothing to reduce - adjustedCost.add(mono.copy()); } continue; } // unsupported multi-option mana types for reduce (like HybridManaCost) - adjustedCost.add(manaCost.copy()); + // nothing to do } } @@ -226,23 +234,39 @@ private static ManaCosts adjustCost(ManaCosts manaCosts, int if (reduceCount < 0) { boolean added = false; for (ManaCost manaCost : manaCosts) { + // ignore already reduced cost (add new cost to the start) + if (changedCost.get(manaCost) == null) { + continue; + } + + // add to existing cost if (reduceCount != 0 && manaCost instanceof GenericManaCost) { - // add increase cost to existing generic GenericManaCost gen = (GenericManaCost) manaCost; - adjustedCost.add(new GenericManaCost(gen.getOptions().get(0).getGeneric() + -reduceCount)); + changedCost.put(manaCost, new GenericManaCost(gen.getOptions().get(0).getGeneric() + -reduceCount)); reduceCount = 0; added = true; } else { // non-generic mana - adjustedCost.add(manaCost.copy()); } } + + // add as new cost if (!added) { - // add increase cost as new - adjustedCost.add(new GenericManaCost(-reduceCount)); + addedCost.add(new GenericManaCost(-reduceCount)); } } + // collect final result + addedCost.forEach(cost -> { + newCost.add(cost.copy()); + }); + changedCost.forEach((key, value) -> { + // ignore fully reduced and add changed + if (value != null) { + newCost.add(value.copy()); + } + }); + // cost modifying effects requiring snow mana unnecessarily (fixes #6000) Filter filter = manaCosts.stream() .filter(manaCost -> !(manaCost instanceof SnowManaCost)) @@ -251,9 +275,10 @@ private static ManaCosts adjustCost(ManaCosts manaCosts, int .findFirst() .orElse(null); if (filter != null) { - adjustedCost.setSourceFilter(filter); + newCost.setSourceFilter(filter); } - return adjustedCost; + + return newCost; } public static void reduceCost(SpellAbility spellAbility, ManaCosts manaCostsToReduce) { @@ -562,6 +587,10 @@ public static UUID getCardExileZoneId(Game game, UUID sourceId, boolean previous return getExileZoneId(getCardZoneString(SOURCE_EXILE_ZONE_TEXT, sourceId, game, previous), game); } + public static UUID getExileZoneId(Game game, Ability source) { + return getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + } + public static UUID getExileZoneId(Game game, UUID objectId, int zoneChangeCounter) { return getExileZoneId(getObjectZoneString(SOURCE_EXILE_ZONE_TEXT, objectId, game, zoneChangeCounter, false), game); } @@ -791,6 +820,15 @@ public static List concatManaSymbols(String delimeter, List mana return res; } + public static String concatManaSymbols(String delimeter, String mana1, String mana2) { + String res = mana1; + if (!res.isEmpty() && !mana2.isEmpty() && delimeter != null && !delimeter.isEmpty()) { + res += delimeter; + } + res += mana2; + return res; + } + public static ColoredManaSymbol manaTypeToColoredManaSymbol(ManaType manaType) { switch (manaType) { case BLACK: @@ -873,6 +911,18 @@ public static String getTextWithFirstCharUpperCase(String text) { } } + private static final String vowels = "aeiouAEIOU"; + + public static String addArticle(String text) { + if (text.startsWith("a ") + || text.startsWith("an ") + || text.startsWith("another ") + || text.startsWith("any ")) { + return text; + } + return vowels.contains(text.substring(0, 1)) ? "an " + text : "a " + text; + } + public static Set getAllSelectedTargets(Ability ability, Game game) { return ability.getModes().getSelectedModes() .stream() @@ -1065,8 +1115,8 @@ public static void takeControlUnderPlayerEnd(Game game, Player controller, Playe } } - public static void makeCardPlayableAndSpendManaAsAnyColor(Game game, Ability source, Card card, Duration duration) { - makeCardPlayableAndSpendManaAsAnyColor(game, source, card, duration, null); + public static void makeCardPlayable(Game game, Ability source, Card card, Duration duration, boolean anyColor) { + makeCardPlayable(game, source, card, duration, anyColor, null); } /** @@ -1078,9 +1128,10 @@ public static void makeCardPlayableAndSpendManaAsAnyColor(Game game, Ability sou * @param game * @param card * @param duration + * @param anyColor * @param condition can be null */ - public static void makeCardPlayableAndSpendManaAsAnyColor(Game game, Ability source, Card card, Duration duration, Condition condition) { + public static void makeCardPlayable(Game game, Ability source, Card card, Duration duration, boolean anyColor, Condition condition) { // Effect can be used for cards in zones and permanents on battlefield // PermanentCard's ZCC is static, but we need updated ZCC from the card (after moved to another zone) // So there is a workaround to get actual card's ZCC @@ -1088,7 +1139,9 @@ public static void makeCardPlayableAndSpendManaAsAnyColor(Game game, Ability sou UUID objectId = card.getMainCard().getId(); int zcc = game.getState().getZoneChangeCounter(objectId); game.addEffect(new CanPlayCardControllerEffect(game, objectId, zcc, duration, condition), source); - game.addEffect(new YouMaySpendManaAsAnyColorToCastTargetEffect(duration, condition).setTargetPointer(new FixedTarget(objectId, zcc)), source); + if (anyColor) { + game.addEffect(new YouMaySpendManaAsAnyColorToCastTargetEffect(duration, condition).setTargetPointer(new FixedTarget(objectId, zcc)), source); + } } /** @@ -1188,39 +1241,124 @@ public static boolean checkCostWords(String text) { * @return */ public static Set getObjectParts(MageObject object) { - Set res = new HashSet<>(); + Set res = new LinkedHashSet<>(); // set must be ordered + List allParts = getObjectPartsAsObjects(object); + allParts.forEach(part -> { + res.add(part.getId()); + }); + return res; + } + + public static List getObjectPartsAsObjects(MageObject object) { + List res = new ArrayList<>(); if (object == null) { return res; } if (object instanceof SplitCard || object instanceof SplitCardHalf) { SplitCard mainCard = (SplitCard) ((Card) object).getMainCard(); - res.add(object.getId()); - res.add(mainCard.getId()); - res.add(mainCard.getLeftHalfCard().getId()); - res.add(mainCard.getRightHalfCard().getId()); + res.add(mainCard); + res.add(mainCard.getLeftHalfCard()); + res.add(mainCard.getRightHalfCard()); } else if (object instanceof ModalDoubleFacesCard || object instanceof ModalDoubleFacesCardHalf) { ModalDoubleFacesCard mainCard = (ModalDoubleFacesCard) ((Card) object).getMainCard(); - res.add(object.getId()); - res.add(mainCard.getId()); - res.add(mainCard.getLeftHalfCard().getId()); - res.add(mainCard.getRightHalfCard().getId()); + res.add(mainCard); + res.add(mainCard.getLeftHalfCard()); + res.add(mainCard.getRightHalfCard()); } else if (object instanceof AdventureCard || object instanceof AdventureCardSpell) { AdventureCard mainCard = (AdventureCard) ((Card) object).getMainCard(); - res.add(object.getId()); - res.add(mainCard.getId()); - res.add(mainCard.getSpellCard().getId()); + res.add(mainCard); + res.add(mainCard.getSpellCard()); } else if (object instanceof Spell) { // example: activate Lightning Storm's ability from the spell on the stack - res.add(object.getId()); - res.addAll(getObjectParts(((Spell) object).getCard())); + res.add(object); + res.addAll(getObjectPartsAsObjects(((Spell) object).getCard())); } else if (object instanceof Commander) { // commander can contains double sides - res.add(object.getId()); - res.addAll(getObjectParts(((Commander) object).getSourceObject())); + res.add(object); + res.addAll(getObjectPartsAsObjects(((Commander) object).getSourceObject())); } else { - res.add(object.getId()); + res.add(object); + } + return res; + } + + public static int parseIntWithDefault(String value, int defaultValue) { + int res; + try { + res = Integer.parseInt(value); + } catch (NumberFormatException ex) { + res = defaultValue; } return res; } + + /** + * Find mapping from original to copied card (e.g. map original left side with copied left side) + * + * @param originalCard + * @param copiedCard + * @return + */ + public static Map getOriginalToCopiedPartsMap(Card originalCard, Card copiedCard) { + List oldIds = new ArrayList<>(CardUtil.getObjectParts(originalCard)); + List newObjects = new ArrayList<>(CardUtil.getObjectPartsAsObjects(copiedCard)); + if (oldIds.size() != newObjects.size()) { + throw new IllegalStateException("Found wrong card parts after copy: " + originalCard.getName() + " -> " + copiedCard.getName()); + } + + Map mapOldToNew = new HashMap<>(); + for (int i = 0; i < oldIds.size(); i++) { + mapOldToNew.put(oldIds.get(i), newObjects.get(i)); + } + return mapOldToNew; + } + + /** + * Return turn info for game. Uses in game logs and debug. + * + * @param game + * @return + */ + public static String getTurnInfo(Game game) { + return getTurnInfo(game == null ? null : game.getState()); + } + + public static String getTurnInfo(GameState gameState) { + // no turn info + if (gameState == null) { + return null; + } + + // not started game + if (gameState.getTurn().getStep() == null) { + return "T0"; + } + + // normal game + return "T" + gameState.getTurnNum() + "." + gameState.getTurn().getStep().getType().getStepShortText(); + } + + public static String concatWithAnd(List strings) { + switch (strings.size()) { + case 0: + return ""; + case 1: + return strings.get(0); + case 2: + return strings.get(0) + " and " + strings.get(1); + } + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < strings.size(); i++) { + sb.append(strings.get(i)); + if (i == strings.size() - 1) { + break; + } + sb.append(", "); + if (i == strings.size() - 2) { + sb.append("and "); + } + } + return sb.toString(); + } } diff --git a/Mage/src/main/java/mage/util/ManaUtil.java b/Mage/src/main/java/mage/util/ManaUtil.java index db6e85b77f8d..14998a5eb006 100644 --- a/Mage/src/main/java/mage/util/ManaUtil.java +++ b/Mage/src/main/java/mage/util/ManaUtil.java @@ -18,6 +18,7 @@ import mage.cards.SplitCard; import mage.choices.Choice; import mage.constants.ColoredManaSymbol; +import mage.constants.ManaType; import mage.filter.FilterMana; import mage.game.Game; import mage.players.Player; @@ -523,7 +524,7 @@ public static void collectColorIdentity(FilterMana destColors, FilterMana newCol * @param secondSideCard second side of double faces card * @return */ - public static FilterMana getColorIdentity(ObjectColor cardColor, List cardManaSymbols, List cardRules, Card secondSideCard) { + public static FilterMana getColorIdentity(ObjectColor cardColor, String cardManaSymbols, List cardRules, Card secondSideCard) { // 20210121 // 903.4 // The Commander variant uses color identity to determine what cards can be in a deck with a certain @@ -576,7 +577,7 @@ public static FilterMana getColorIdentity(ObjectColor cardColor, List ca res.setWhite(res.isWhite() || secondColor.isWhite()); // from mana - List secondManaSymbols = secondSideCard.getManaCost().getSymbols(); + List secondManaSymbols = secondSideCard.getManaCostSymbols(); res.setWhite(res.isWhite() || containsManaSymbol(secondManaSymbols, "W")); res.setBlue(res.isBlue() || containsManaSymbol(secondManaSymbols, "U")); res.setBlack(res.isBlack() || containsManaSymbol(secondManaSymbols, "B")); @@ -612,6 +613,11 @@ private static boolean containsManaSymbol(List cardManaSymbols, String n return cardManaSymbols.stream().anyMatch(s -> s.contains(needSymbol)); } + private static boolean containsManaSymbol(String cardManaSymbols, String needSymbol) { + // search R in {R/B} + return cardManaSymbols.contains(needSymbol); + } + public static FilterMana getColorIdentity(Card card) { Card secondSide; if (card instanceof SplitCard) { @@ -623,7 +629,7 @@ public static FilterMana getColorIdentity(Card card) { } else { secondSide = card.getSecondCardFace(); } - return getColorIdentity(card.getColor(), card.getManaCost().getSymbols(), card.getRules(), secondSide); + return getColorIdentity(card.getColor(), String.join("", card.getManaCostSymbols()), card.getRules(), secondSide); } public static int getColorIdentityHash(FilterMana colorIdentity) { @@ -694,6 +700,23 @@ public static int playerPaysXGenericMana(boolean payAsX, String restoreContextNa } else { return 0; } + } + /** + * Find all used mana types in mana cost (wubrg + colorless) + * + * @return + */ + public static List getManaTypesInCost(ManaCost cost) { + List res = new ArrayList<>(); + for (Mana mana : cost.getManaOptions()) { + if (mana.getWhite() > 0) res.add(ManaType.WHITE); + if (mana.getBlue() > 0) res.add(ManaType.BLUE); + if (mana.getBlack() > 0) res.add(ManaType.BLACK); + if (mana.getRed() > 0) res.add(ManaType.RED); + if (mana.getGreen() > 0) res.add(ManaType.GREEN); + if (mana.getColorless() > 0 || mana.getGeneric() > 0 || mana.getAny() > 0) res.add(ManaType.COLORLESS); + } + return res; } } diff --git a/Mage/src/main/java/mage/util/MessageToClient.java b/Mage/src/main/java/mage/util/MessageToClient.java index 657f79c394bb..2de064eac5c0 100644 --- a/Mage/src/main/java/mage/util/MessageToClient.java +++ b/Mage/src/main/java/mage/util/MessageToClient.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.util; /** diff --git a/Mage/src/main/java/mage/util/TournamentUtil.java b/Mage/src/main/java/mage/util/TournamentUtil.java index d47a683aaa65..3df492aad2c5 100644 --- a/Mage/src/main/java/mage/util/TournamentUtil.java +++ b/Mage/src/main/java/mage/util/TournamentUtil.java @@ -1,9 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - package mage.util; import mage.cards.Card; diff --git a/Mage/src/main/java/mage/util/functions/CopyApplier.java b/Mage/src/main/java/mage/util/functions/CopyApplier.java index 4b1892ed31bb..37288ee4903a 100644 --- a/Mage/src/main/java/mage/util/functions/CopyApplier.java +++ b/Mage/src/main/java/mage/util/functions/CopyApplier.java @@ -5,7 +5,6 @@ import mage.game.Game; import java.io.Serializable; -import java.util.Objects; import java.util.UUID; /** @@ -20,10 +19,14 @@ public abstract class CopyApplier implements Serializable { // 2. It applies to the blueprint, not the real object (the real object is targetObjectId and can be card or token, even from outside the game like EmptyToken); // 3. "source" is the current copy ability and can be different from the original copy ability (copy of copy); // 4. Don't use "source" param at all; - // 5. Use isCopyOfCopy() to detect it (some effects can apply to copy of copy, but others can't -- see Spark Double as an example). + // 5. For exception/non-copyable effects use isCopyOfCopy() to detect that situation (example: 706.9e, Spark Double, Altered Ego). public abstract boolean apply(Game game, MageObject blueprint, Ability source, UUID targetObjectId); - public boolean isCopyOfCopy(Ability source, UUID targetObjectId) { - return !Objects.equals(targetObjectId, source.getSourceId()); + public boolean isCopyOfCopy(Ability source, MageObject blueprint, UUID targetObjectId) { + return blueprint.isCopy(); + } + + public String getText() { + return ""; } } diff --git a/Mage/src/main/java/mage/util/functions/StackObjectCopyApplier.java b/Mage/src/main/java/mage/util/functions/StackObjectCopyApplier.java new file mode 100644 index 000000000000..1bd74d6212a3 --- /dev/null +++ b/Mage/src/main/java/mage/util/functions/StackObjectCopyApplier.java @@ -0,0 +1,17 @@ +package mage.util.functions; + +import mage.filter.predicate.mageobject.MageObjectReferencePredicate; +import mage.game.Game; +import mage.game.stack.StackObject; + +import java.io.Serializable; + +/** + * @author TheElk801 + */ +public interface StackObjectCopyApplier extends Serializable { + + void modifySpell(StackObject stackObject, Game game); + + MageObjectReferencePredicate getNextPredicate(); +} diff --git a/Mage/src/main/java/mage/watchers/common/AttackedOrBlockedThisCombatWatcher.java b/Mage/src/main/java/mage/watchers/common/AttackedOrBlockedThisCombatWatcher.java index 7a9bf240a906..5f5410fca363 100644 --- a/Mage/src/main/java/mage/watchers/common/AttackedOrBlockedThisCombatWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/AttackedOrBlockedThisCombatWatcher.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.watchers.common; import java.util.HashSet; diff --git a/Mage/src/main/java/mage/watchers/common/CardsPutIntoGraveyardWatcher.java b/Mage/src/main/java/mage/watchers/common/CardsPutIntoGraveyardWatcher.java index 0a73067aabcd..75ed33cdb590 100644 --- a/Mage/src/main/java/mage/watchers/common/CardsPutIntoGraveyardWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/CardsPutIntoGraveyardWatcher.java @@ -1,13 +1,7 @@ - package mage.watchers.common; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.UUID; - import mage.MageObjectReference; +import mage.cards.Card; import mage.constants.WatcherScope; import mage.constants.Zone; import mage.game.Game; @@ -15,6 +9,9 @@ import mage.game.events.ZoneChangeEvent; import mage.watchers.Watcher; +import java.util.*; +import java.util.stream.Collectors; + /** * Counts amount of cards put into graveyards of players during the current * turn. Also the UUIDs of cards that went to graveyard from Battlefield this @@ -33,19 +30,17 @@ public CardsPutIntoGraveyardWatcher() { @Override public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.UNTAP_STEP_PRE) { - reset(); + if (event.getType() != GameEvent.EventType.ZONE_CHANGE + || ((ZoneChangeEvent) event).getToZone() != Zone.GRAVEYARD) { + return; } - if (event.getType() == GameEvent.EventType.ZONE_CHANGE && ((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD) { - UUID playerId = event.getPlayerId(); - if (playerId != null && game.getCard(event.getTargetId()) != null) { - amountOfCardsThisTurn.putIfAbsent(playerId, 0); - amountOfCardsThisTurn.compute(playerId, (k, amount) -> amount += 1); - - if (((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD) { - cardsPutToGraveyardFromBattlefield.add(new MageObjectReference(event.getTargetId(), game)); - } - } + UUID playerId = event.getPlayerId(); + if (playerId == null || game.getCard(event.getTargetId()) == null) { + return; + } + amountOfCardsThisTurn.compute(playerId, (k, amount) -> amount == null ? 1 : Integer.sum(amount, 1)); + if (((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD) { + cardsPutToGraveyardFromBattlefield.add(new MageObjectReference(((ZoneChangeEvent) event).getTarget(), game, 1)); } } @@ -53,8 +48,12 @@ public int getAmountCardsPutToGraveyard(UUID playerId) { return amountOfCardsThisTurn.getOrDefault(playerId, 0); } - public Set getCardsPutToGraveyardFromBattlefield() { - return cardsPutToGraveyardFromBattlefield; + public Set getCardsPutToGraveyardFromBattlefield(Game game) { + return cardsPutToGraveyardFromBattlefield.stream().map(mor -> mor.getCard(game)).filter(Objects::nonNull).collect(Collectors.toSet()); + } + + public boolean checkCardFromBattlefield(Card card, Game game) { + return cardsPutToGraveyardFromBattlefield.stream().anyMatch(mor -> mor.refersTo(card, game)); } @Override diff --git a/Mage/src/main/java/mage/watchers/common/DamageDoneWatcher.java b/Mage/src/main/java/mage/watchers/common/DamageDoneWatcher.java index 6e4008ab16ab..348f88f29080 100644 --- a/Mage/src/main/java/mage/watchers/common/DamageDoneWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/DamageDoneWatcher.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.watchers.common; import mage.MageObjectReference; @@ -43,8 +38,7 @@ public DamageDoneWatcher() { @Override public void watch(GameEvent event, Game game) { switch (event.getType()) { - case DAMAGED_CREATURE: - case DAMAGED_PLANESWALKER: + case DAMAGED_PERMANENT: case DAMAGED_PLAYER: { MageObjectReference damageSourceRef = new MageObjectReference(event.getSourceId(), game); damagingObjects.putIfAbsent(damageSourceRef, 0); diff --git a/Mage/src/main/java/mage/watchers/common/DamagedByWatcher.java b/Mage/src/main/java/mage/watchers/common/DamagedByWatcher.java index 1956b98e1d93..5c5882e49140 100644 --- a/Mage/src/main/java/mage/watchers/common/DamagedByWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/DamagedByWatcher.java @@ -1,25 +1,24 @@ package mage.watchers.common; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; - import mage.MageObject; import mage.MageObjectReference; import mage.constants.WatcherScope; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.watchers.Watcher; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** * @author BetaSteward_at_googlemail.com */ public class DamagedByWatcher extends Watcher { - public final Set damagedBySource = new HashSet<>(); + private final Set damagedBySource = new HashSet<>(); private final boolean watchPlaneswalkers; @@ -30,12 +29,15 @@ public DamagedByWatcher(boolean watchPlaneswalkers) { @Override public void watch(GameEvent event, Game game) { - boolean eventHasAppropriateType = (event.getType() == GameEvent.EventType.DAMAGED_CREATURE) || - (watchPlaneswalkers && event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER); - if (eventHasAppropriateType && sourceId.equals(event.getSourceId())) { - MageObjectReference mor = new MageObjectReference(event.getTargetId(), game); - damagedBySource.add(mor); - + if (event.getType() != GameEvent.EventType.DAMAGED_PERMANENT) { + return; + } + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null && !watchPlaneswalkers && !permanent.isCreature()) { + return; + } + if (sourceId.equals(event.getSourceId())) { + damagedBySource.add(new MageObjectReference(event.getTargetId(), game)); } } diff --git a/Mage/src/main/java/mage/watchers/common/DragonOnTheBattlefieldWhileSpellWasCastWatcher.java b/Mage/src/main/java/mage/watchers/common/DragonOnTheBattlefieldWhileSpellWasCastWatcher.java index 0d5ee938bdae..d33cdc0f1a8d 100644 --- a/Mage/src/main/java/mage/watchers/common/DragonOnTheBattlefieldWhileSpellWasCastWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/DragonOnTheBattlefieldWhileSpellWasCastWatcher.java @@ -1,10 +1,9 @@ package mage.watchers.common; -import mage.abilities.costs.Cost; -import mage.abilities.costs.common.RevealTargetFromHandCost; -import mage.constants.SubType; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.costs.common.RevealDragonFromHandCost; import mage.constants.WatcherScope; -import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; @@ -12,16 +11,13 @@ import java.util.HashSet; import java.util.Set; -import java.util.UUID; /** * @author LevelX2 */ public class DragonOnTheBattlefieldWhileSpellWasCastWatcher extends Watcher { - private static final FilterPermanent filter = new FilterPermanent(SubType.DRAGON, "Dragons"); - - private final Set castWithDragonOnTheBattlefield = new HashSet<>(); + private final Set castWithDragonOnTheBattlefield = new HashSet<>(); public DragonOnTheBattlefieldWhileSpellWasCastWatcher() { super(WatcherScope.GAME); @@ -29,28 +25,22 @@ public DragonOnTheBattlefieldWhileSpellWasCastWatcher() { @Override public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.SPELL_CAST) { - // targetId is the unique ID of the spell - Spell spell = game.getState().getStack().getSpell(event.getTargetId()); - // revealed a Dragon card or controlled a Dragon as you cast the spell - if (spell != null) { - boolean revealedOrOnBattlefield = false; - if (spell.getSpellAbility() != null) { - for (Cost cost : spell.getSpellAbility().getCosts()) { - if (cost instanceof RevealTargetFromHandCost) { - revealedOrOnBattlefield = ((RevealTargetFromHandCost) cost).getNumberRevealedCards() > 0; - break; - } - } - } - if (!revealedOrOnBattlefield) { - revealedOrOnBattlefield = game.getBattlefield().countAll(filter, spell.getControllerId(), game) > 0; - } - if (revealedOrOnBattlefield) { - castWithDragonOnTheBattlefield.add(spell.getId()); - } - - } + if (event.getType() != GameEvent.EventType.SPELL_CAST) { + return; + } + // targetId is the unique ID of the spell + Spell spell = game.getSpell(event.getTargetId()); + // revealed a Dragon card or controlled a Dragon as you cast the spell + if (spell != null + && spell + .getSpellAbility() + .getCosts() + .stream() + .filter(RevealDragonFromHandCost.class::isInstance) + .map(RevealDragonFromHandCost.class::cast) + .anyMatch(RevealDragonFromHandCost::isRevealedOrControlled)) { + castWithDragonOnTheBattlefield.add(new MageObjectReference(spell.getCard(), game, 0)); + castWithDragonOnTheBattlefield.add(new MageObjectReference(spell.getCard(), game, 1)); } } @@ -60,7 +50,9 @@ public void reset() { castWithDragonOnTheBattlefield.clear(); } - public boolean castWithConditionTrue(UUID spellId) { - return castWithDragonOnTheBattlefield.contains(spellId); + public boolean checkCondition(Ability source, Game game) { + return castWithDragonOnTheBattlefield + .stream() + .anyMatch(mor -> mor.refersTo(source.getSourceObject(game), game)); } } diff --git a/Mage/src/main/java/mage/watchers/common/ForetoldWatcher.java b/Mage/src/main/java/mage/watchers/common/ForetoldWatcher.java index 0031a47fcf94..68aad21d073b 100644 --- a/Mage/src/main/java/mage/watchers/common/ForetoldWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/ForetoldWatcher.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.watchers.common; import java.util.HashSet; diff --git a/Mage/src/main/java/mage/watchers/common/ManaSpentToCastWatcher.java b/Mage/src/main/java/mage/watchers/common/ManaSpentToCastWatcher.java index b9da35f12355..79b9ffdd0367 100644 --- a/Mage/src/main/java/mage/watchers/common/ManaSpentToCastWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/ManaSpentToCastWatcher.java @@ -17,6 +17,7 @@ public class ManaSpentToCastWatcher extends Watcher { private Mana payment = null; + private Integer xValue = 0; public ManaSpentToCastWatcher() { super(WatcherScope.CARD); @@ -29,12 +30,14 @@ public void watch(GameEvent event, Game game) { Spell spell = (Spell) game.getObject(event.getTargetId()); if (spell != null && this.getSourceId().equals(spell.getSourceId())) { payment = spell.getSpellAbility().getManaCostsToPay().getUsedManaToPay(); + xValue = spell.getSpellAbility().getManaCostsToPay().getX(); } } if (event.getType() == GameEvent.EventType.ZONE_CHANGE && this.getSourceId().equals(event.getSourceId())) { if (((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD) { payment = null; + xValue = 0; } } } @@ -45,13 +48,16 @@ public Mana getAndResetLastPayment() { returnPayment = payment.copy(); } return returnPayment; + } + public int getAndResetLastXValue() { + return xValue; } @Override public void reset() { super.reset(); payment = null; + xValue = 0; } - } diff --git a/Mage/src/main/java/mage/watchers/common/PermanentsEnteredBattlefieldWatcher.java b/Mage/src/main/java/mage/watchers/common/PermanentsEnteredBattlefieldWatcher.java index 9e74b0bae7b4..0466fef7c8d9 100644 --- a/Mage/src/main/java/mage/watchers/common/PermanentsEnteredBattlefieldWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/PermanentsEnteredBattlefieldWatcher.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.watchers.common; import java.util.*; diff --git a/Mage/src/main/java/mage/watchers/common/PermanentsEnteredBattlefieldYourLastTurnWatcher.java b/Mage/src/main/java/mage/watchers/common/PermanentsEnteredBattlefieldYourLastTurnWatcher.java index f0a2c4f20ab6..78a9f83a4733 100644 --- a/Mage/src/main/java/mage/watchers/common/PermanentsEnteredBattlefieldYourLastTurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/PermanentsEnteredBattlefieldYourLastTurnWatcher.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.watchers.common; import mage.constants.WatcherScope; diff --git a/Mage/src/main/java/mage/watchers/common/PermanentsSacrificedWatcher.java b/Mage/src/main/java/mage/watchers/common/PermanentsSacrificedWatcher.java index c157be5f23c3..913802815cc6 100644 --- a/Mage/src/main/java/mage/watchers/common/PermanentsSacrificedWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/PermanentsSacrificedWatcher.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.watchers.common; import java.util.*; diff --git a/Mage/src/main/java/mage/watchers/common/SourceDidDamageWatcher.java b/Mage/src/main/java/mage/watchers/common/SourceDidDamageWatcher.java index 97112fd4b67c..b6399529b382 100644 --- a/Mage/src/main/java/mage/watchers/common/SourceDidDamageWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/SourceDidDamageWatcher.java @@ -1,13 +1,14 @@ - package mage.watchers.common; +import mage.MageObject; +import mage.MageObjectReference; import mage.constants.WatcherScope; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.watchers.Watcher; -import java.util.*; +import java.util.HashSet; +import java.util.Set; /** * Watcher stores which sources did damage to anything. @@ -16,7 +17,7 @@ */ public class SourceDidDamageWatcher extends Watcher { - public final Set damageSources = new HashSet<>(); + private final Set damageSources = new HashSet<>(); public SourceDidDamageWatcher() { super(WatcherScope.GAME); @@ -24,14 +25,16 @@ public SourceDidDamageWatcher() { @Override public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.DAMAGED_CREATURE - || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER + if (event.getType() == GameEvent.EventType.DAMAGED_PERMANENT || event.getType() == GameEvent.EventType.DAMAGED_PLAYER) { - damageSources.add(event.getSourceId()); - + damageSources.add(new MageObjectReference(event.getSourceId(), game)); } } + public boolean checkSource(MageObject mageObject, Game game) { + return damageSources.stream().anyMatch(mor -> mor.refersTo(mageObject, game)); + } + @Override public void reset() { super.reset(); diff --git a/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java b/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java index 46a1a7fcc014..a074640a1fb7 100644 --- a/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.watchers.common; import mage.MageObject; @@ -15,14 +10,9 @@ import mage.game.stack.Spell; import mage.watchers.Watcher; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.util.*; /** - * * @author LevelX2 */ public class SpellsCastWatcher extends Watcher { @@ -78,11 +68,11 @@ public void reset() { } public List getSpellsCastThisTurn(UUID playerId) { - return spellsCast.get(playerId); + return spellsCast.computeIfAbsent(playerId, x -> new ArrayList<>()); } public List getSpellsCastFromGraveyardThisTurn(UUID playerId) { - return spellsCastFromGraveyard.get(playerId); + return spellsCastFromGraveyard.computeIfAbsent(playerId, x -> new ArrayList<>()); } public int getNumberOfNonCreatureSpells() { diff --git a/Mage/src/test/java/mage/cards/decks/importer/samples/testdeck.json b/Mage/src/test/java/mage/cards/decks/importer/samples/testdeck.json index 025e9a47712d..70e99295b88a 100644 --- a/Mage/src/test/java/mage/cards/decks/importer/samples/testdeck.json +++ b/Mage/src/test/java/mage/cards/decks/importer/samples/testdeck.json @@ -18,7 +18,7 @@ "R", "U" ], - "convertedManaCost": 2.0, + "manaValue": 2.0, "count": 4, "edhrecRank": 626, "flavorText": "\"Result 752: Rapid mass redistribution.\n\"Result 753: Calamitous reverse synthesis.\n\"Result 754: Acute disarrayment.\"\n—Izzet research notes", @@ -165,7 +165,7 @@ }, { "date": "2018-10-05", - "text": "To determine the total cost of a spell, start with the mana cost or alternative cost you’re paying, add any cost increases, then apply any cost reductions. The converted mana cost of the spell remains unchanged, no matter what the total cost to cast it was." + "text": "To determine the total cost of a spell, start with the mana cost or alternative cost you’re paying, add any cost increases, then apply any cost reductions. The mana value of the spell remains unchanged, no matter what the total cost to cast it was." }, { "date": "2018-10-05", @@ -203,7 +203,7 @@ "R", "U" ], - "convertedManaCost": 4.0, + "manaValue": 4.0, "count": 4, "edhrecRank": 2662, "foreignData": [ @@ -368,7 +368,7 @@ "colors": [ "U" ], - "convertedManaCost": 4.0, + "manaValue": 4.0, "count": 2, "edhrecRank": 1050, "flavorText": "Rumors float through the city like crows, alighting on citizens seemingly at random.", @@ -536,7 +536,7 @@ "colors": [ "R" ], - "convertedManaCost": 4.0, + "manaValue": 4.0, "count": 1, "edhrecRank": 10215, "flavorText": "Some storms never blow over.", @@ -708,7 +708,7 @@ "R", "U" ], - "convertedManaCost": 6.0, + "manaValue": 6.0, "count": 2, "edhrecRank": 536, "foreignData": [ @@ -890,7 +890,7 @@ "colors": [ "U" ], - "convertedManaCost": 2.0, + "manaValue": 2.0, "count": 4, "edhrecRank": 1976, "flavorText": "While other pirates prowl for treasure, Captain Parrish plunders secrets.", @@ -1047,7 +1047,7 @@ "colors": [ "R" ], - "convertedManaCost": 2.0, + "manaValue": 2.0, "count": 4, "edhrecRank": 6242, "flavorText": "\"Commander, we caught the Dimir spy and took her ashes into custody.\"\n—Kramm, Wojek security officer", @@ -1212,7 +1212,7 @@ "R", "U" ], - "convertedManaCost": 3.0, + "manaValue": 3.0, "count": 1, "edhrecRank": 4333, "foreignData": [ @@ -1387,7 +1387,7 @@ "colors": [ "U" ], - "convertedManaCost": 1.0, + "manaValue": 1.0, "count": 4, "edhrecRank": 280, "flavorText": "The crystal pulsed with the power of Teferi's planeswalker spark. Had Jhoira given him a blessing or a curse?", @@ -1559,7 +1559,7 @@ "colors": [ "U" ], - "convertedManaCost": 2.0, + "manaValue": 2.0, "count": 4, "edhrecRank": 3422, "flavorText": "Genius is finding the edge of what's possible, then jumping over it.", @@ -1738,7 +1738,7 @@ "colors": [ "R" ], - "convertedManaCost": 1.0, + "manaValue": 1.0, "count": 4, "edhrecRank": 3135, "flavorText": "The tools of invention became the weapons of revolution.", @@ -1920,7 +1920,7 @@ "colors": [ "U" ], - "convertedManaCost": 1.0, + "manaValue": 1.0, "count": 2, "edhrecRank": 5887, "flavorText": "\"Seeing the river is not the same as seeing the fish.\"", @@ -2077,7 +2077,7 @@ "colors": [ "U" ], - "convertedManaCost": 2.0, + "manaValue": 2.0, "count": 1, "edhrecRank": 1693, "foreignData": [ @@ -2229,7 +2229,7 @@ "colors": [ "U" ], - "convertedManaCost": 5.0, + "manaValue": 5.0, "count": 1, "edhrecRank": 1853, "foreignData": [ @@ -2433,7 +2433,7 @@ "U" ], "colors": [], - "convertedManaCost": 0.0, + "manaValue": 0.0, "count": 3, "edhrecRank": 119, "flavorText": "\"We have inherited the mysteries of the Thran, but few of the answers.\"\n—Jhoira", @@ -2588,7 +2588,7 @@ "U" ], "colors": [], - "convertedManaCost": 0.0, + "manaValue": 0.0, "count": 4, "edhrecRank": 312, "flavorText": "Every laboratory buzzes with new experiments, each a piece of Ral's ambitious project.", @@ -2685,7 +2685,7 @@ "U" ], "colors": [], - "convertedManaCost": 0.0, + "manaValue": 0.0, "count": 8, "foreignData": [], "frameVersion": "2015", @@ -2931,7 +2931,7 @@ "R" ], "colors": [], - "convertedManaCost": 0.0, + "manaValue": 0.0, "count": 7, "foreignData": [], "frameVersion": "2015", @@ -3187,7 +3187,7 @@ "colors": [ "U" ], - "convertedManaCost": 5.0, + "manaValue": 5.0, "count": 1, "edhrecRank": 1853, "foreignData": [ @@ -3394,7 +3394,7 @@ "R", "U" ], - "convertedManaCost": 3.0, + "manaValue": 3.0, "count": 1, "edhrecRank": 4333, "foreignData": [ @@ -3569,7 +3569,7 @@ "colors": [ "U" ], - "convertedManaCost": 2.0, + "manaValue": 2.0, "count": 3, "edhrecRank": 56, "flavorText": "\"As one, nature lifts its voice to tell you this: 'No.'\"", @@ -3757,7 +3757,7 @@ "colors": [ "U" ], - "convertedManaCost": 2.0, + "manaValue": 2.0, "count": 3, "edhrecRank": 4848, "flavorText": "The stronger the will, the more bewitching the song.", @@ -3876,7 +3876,7 @@ "manaCost": "{X}{U}{U}", "name": "Entrancing Melody", "number": "55", - "originalText": "Gain control of target creature with converted mana cost X.", + "originalText": "Gain control of target creature with mana value X.", "originalType": "Sorcery", "printings": [ "PXLN", @@ -3906,7 +3906,7 @@ "setCode": "XLN", "subtypes": [], "supertypes": [], - "text": "Gain control of target creature with converted mana cost X.", + "text": "Gain control of target creature with mana value X.", "type": "Sorcery", "types": [ "Sorcery" @@ -3927,7 +3927,7 @@ "colors": [ "R" ], - "convertedManaCost": 3.0, + "manaValue": 3.0, "count": 3, "edhrecRank": 5816, "flavorText": "Wary of the ferocious dinosaurs, the Legion of Dusk built up the walls of their fort—just in time for the pirates to burn them down.", @@ -4083,7 +4083,7 @@ "colors": [ "R" ], - "convertedManaCost": 1.0, + "manaValue": 1.0, "count": 2, "edhrecRank": 8679, "flavorText": "Sometimes Shivan dragons toy with their food. Other times they just cook it.", @@ -4241,7 +4241,7 @@ "colors": [ "U" ], - "convertedManaCost": 2.0, + "manaValue": 2.0, "count": 2, "edhrecRank": 893, "flavorText": "\"Laws apply only to those who lack the vision to see past them.\"", @@ -4363,7 +4363,7 @@ "manaCost": "{1}{U}", "name": "Disdainful Stroke", "number": "37", - "originalText": "Counter target spell with converted mana cost 4 or greater.", + "originalText": "Counter target spell with mana value 4 or greater.", "originalType": "Instant", "printings": [ "C16", @@ -4383,17 +4383,17 @@ "rulings": [ { "date": "2014-09-20", - "text": "A face-down spell has converted mana cost 0 and can’t be targeted by Disdainful Stroke." + "text": "A face-down spell has mana value 0 and can’t be targeted by Disdainful Stroke." }, { "date": "2018-10-05", - "text": "If a spell has {X} in its mana cost, include the value chosen for that X when determining the converted mana cost of that spell." + "text": "If a spell has {X} in its mana cost, include the value chosen for that X when determining the mana value of that spell." } ], "setCode": "GRN", "subtypes": [], "supertypes": [], - "text": "Counter target spell with converted mana cost 4 or greater.", + "text": "Counter target spell with mana value 4 or greater.", "type": "Instant", "types": [ "Instant" diff --git a/Utils/keywords.txt b/Utils/keywords.txt index 3b75c7c86b55..74d6b1364bd8 100644 --- a/Utils/keywords.txt +++ b/Utils/keywords.txt @@ -17,6 +17,7 @@ Cumulative upkeep|cost| Cycling|cost| Dash|card, manaString| Deathtouch|instance| +Demonstrate|new| Delve|new| Dethrone|new| Devoid|color| @@ -104,4 +105,5 @@ Undying|new| Unearth|cost| Unleash|new| Vigilance|instance| +Ward|cost| Wither|instance| diff --git a/Utils/known-sets.txt b/Utils/known-sets.txt index fea5cfc65d1a..64e0e2b70ba6 100644 --- a/Utils/known-sets.txt +++ b/Utils/known-sets.txt @@ -28,6 +28,7 @@ Commander 2017 Edition|Commander2017Edition| Commander 2018|Commander2018Edition| Commander 2019|Commander2019Edition| Commander 2020|Commander2020Edition| +Commander 2021|Commander2021Edition| Commander Legends|CommanderLegends| Commander Anthology|CommanderAnthology| Commander Anthology 2018|CommanderAnthology2018| @@ -189,6 +190,7 @@ Shards of Alara|ShardsOfAlara| Starter 1999|Starter1999| Starter 2000|Starter2000| Star Wars|StarWars| +Strixhaven: School of Mages|StrixhavenSchoolOfMages| Stronghold|Stronghold| Super Series|SuperSeries| Tempest|Tempest| diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index bc9228c38d39..aacb907aa288 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -40576,3 +40576,747 @@ Strategic Planning|Kaldheim|402|C|{1}{U}|Sorcery|||Look at the top three cards o Poison the Cup|Kaldheim|403|U|{1}{B}{B}|Instant|||Destroy target creature. If this spell was foretold, scry 2.$Foretell {1}{B}| Frost Bite|Kaldheim|404|C|{R}|Snow Instant|||Frost Bite deals 2 damage to target creature or planeswalker. If you control three or more snow permanents, it deals 3 damage instead.| Masked Vandal|Kaldheim|405|C|{1}{G}|Creature - Shapeshifter|1|3|Changeling$When Masked Vandal enters the battlefield, you may exile a creature card from your graveyard. If you do, exile target artifact or enchantment an opponent controls.| +Environmental Sciences|Strixhaven: School of Mages|1|C|{2}|Sorcery - Lesson|||Search your library for a basic land card, reveal it, put it into your hand, then shuffle. You gain 2 life.| +Expanded Anatomy|Strixhaven: School of Mages|2|C|{3}|Sorcery - Lesson|||Put two +1/+1 counters on target creature. It gains vigilance until end of turn.| +Introduction to Annihilation|Strixhaven: School of Mages|3|C|{5}|Sorcery - Lesson|||Exile target nonland permanent. Its controller draws a card.| +Introduction to Prophecy|Strixhaven: School of Mages|4|C|{3}|Sorcery - Lesson|||Scry 2, then draw a card.| +Mascot Exhibition|Strixhaven: School of Mages|5|M|{7}|Sorcery - Lesson|||Create a 2/1 white and black Inkling creature token with flying, a 3/2 red and white Spirit creature token, and a 4/4 blue and red Elemental creature token.| +Explore the Vastlands|Strixhaven: School of Mages|6|R|{3}|Sorcery|||Each player looks at the top five cards of their library and may reveal a land card and/or an instant or sorcery card from among them. Each player puts the cards they revealed this way into their hand and the rest on the bottom of their library in a random order. Each player gains 3 life.| +Wandering Archaic|Strixhaven: School of Mages|6|R|{5}|Creature - Avatar|4|4|Whenever an opponent casts an instant or sorcery spell, they may pay {2}. If they don't, you may copy that spell. You may choose new targets for the copy.| +Academic Probation|Strixhaven: School of Mages|7|R|{1}{W}|Sorcery - Lesson|||Choose one —$• Choose a nonland card name. Opponents can't cast spells with the chosen name until your next turn.$• Choose target nonland permanent. Until your next turn, it can't attack or block, and its activated abilities can't be activated.| +Ageless Guardian|Strixhaven: School of Mages|8|C|{1}{W}|Creature - Spirit Soldier|1|4|| +Beaming Defiance|Strixhaven: School of Mages|9|C|{1}{W}|Instant|||Target creature you control gets +2/+2 and gains hexproof until end of turn.| +Clever Lumimancer|Strixhaven: School of Mages|10|U|{W}|Creature - Human Wizard|0|1|Magecraft — Whenever you cast or copy an instant or sorcery spell, Clever Lumimancer gets +2/+2 until end of turn.| +Combat Professor|Strixhaven: School of Mages|11|C|{3}{W}|Creature - Bird Cleric|2|3|Flying$At the beginning of combat on your turn, target creature you control gets +1/+0 and gains vigilance until end of turn.| +Defend the Campus|Strixhaven: School of Mages|12|C|{3}{W}|Instant|||Choose one —$• Creatures you control get +1/+1 until end of turn.$• Destroy target creature with power 4 or greater.| +Detention Vortex|Strixhaven: School of Mages|13|U|{W}|Enchantment - Aura|||Enchant nonland permanent$Enchanted permanent can't attack or block, and its activated abilities can't be activated.${3}: Destroy Detention Vortex. Only your opponents may activate this ability and only as a sorcery.| +Devastating Mastery|Strixhaven: School of Mages|14|R|{2}{W}{W}{W}{W}|Sorcery|||You may pay {2}{W}{W} rather than pay this spell's mana cost.$If the {2}{W}{W} cost was paid, an opponent chooses up to two nonland permanents they control and returns them to their owner's hand.$Destroy all nonland permanents.| +Dueling Coach|Strixhaven: School of Mages|15|U|{3}{W}|Creature - Human Monk|2|2|When Dueling Coach enters the battlefield, put a +1/+1 counter on target creature.${4}{W}, {T}: Put a +1/+1 counter on each creature you control with a +1/+1 counter on it.| +Eager First-Year|Strixhaven: School of Mages|16|C|{1}{W}|Creature - Human Wizard|2|2|Magecraft — Whenever you cast or copy an instant or sorcery spell, Eager First-Year gets +1/+0 until end of turn.| +Elite Spellbinder|Strixhaven: School of Mages|17|R|{2}{W}|Creature - Human Cleric|3|1|Flying$When Elite Spellbinder enters the battlefield, look at target opponent's hand. You may exile a nonland card from it. For as long as that card remains exiled, its owner may play it. A spell cast this way costs {2} more to cast.| +Expel|Strixhaven: School of Mages|18|C|{2}{W}|Instant|||Exile target tapped creature.| +Guiding Voice|Strixhaven: School of Mages|19|C|{W}|Sorcery|||Put a +1/+1 counter on target creature.$Learn.| +Leonin Lightscribe|Strixhaven: School of Mages|20|R|{1}{W}|Creature - Cat Cleric|2|2|Magecraft — Whenever you cast or copy an instant or sorcery spell, creatures you control get +1/+1 until end of turn.| +Mavinda, Students' Advocate|Strixhaven: School of Mages|21|M|{2}{W}|Legendary Creature - Bird Advisor|2|3|Flying${0}: You may cast target instant or sorcery card from your graveyard this turn. If that spell doesn't target a creature you control, it costs {8} more to cast this way. If that spell would be put into your graveyard, exile it instead. Activate only once each turn.| +Pilgrim of the Ages|Strixhaven: School of Mages|22|C|{2}{W}|Creature - Spirit|2|1|When Pilgrim of the Ages enters the battlefield, you may search your library for a basic Plains card, reveal it, put it into your hand, then shuffle.${6}: Return Pilgrim of the Ages from your graveyard to your hand.| +Pillardrop Rescuer|Strixhaven: School of Mages|23|C|{4}{W}|Creature - Spirit Cleric|2|2|Flying$When Pillardrop Rescuer enters the battlefield, return target creature card with mana value 3 or less from your graveyard to your hand.| +Professor of Symbology|Strixhaven: School of Mages|24|U|{1}{W}|Creature - Kor Cleric|2|1|When Professor of Symbology enters the battlefield, learn.| +Reduce to Memory|Strixhaven: School of Mages|25|U|{1}{W}{W}|Sorcery - Lesson|||Exile target nonland permanent. Its controller creates a 3/2 red and white Spirit creature token.| +Secret Rendezvous|Strixhaven: School of Mages|26|U|{1}{W}{W}|Sorcery|||You and target opponent each draw three cards.| +Semester's End|Strixhaven: School of Mages|27|R|{3}{W}|Instant|||Exile any number of target creatures and/or planeswalkers you control. At the beginning of the next end step, return each of them to the battlefield under its owner's control. Each of them enters the battlefield with an additional +1/+1 counter on it if it's a creature and an additional loyalty counter on it if it's a planeswalker.| +Show of Confidence|Strixhaven: School of Mages|28|U|{1}{W}|Instant|||When you cast this spell, copy it for each other instant and sorcery spell you've cast this turn. You may choose new targets for the copies.$Put a +1/+1 counter on target creature. It gains vigilance until end of turn.| +Sparring Regimen|Strixhaven: School of Mages|29|R|{2}{W}|Enchantment|||When Sparring Regimen enters the battlefield, learn.$Whenever you attack, put a +1/+1 counter on target attacking creature and untap it.| +Star Pupil|Strixhaven: School of Mages|30|C|{W}|Creature - Human Wizard|0|0|Star Pupil enters the battlefield with a +1/+1 counter on it.$When Star Pupil dies, put its counters on target creature you control.| +Stonebinder's Familiar|Strixhaven: School of Mages|31|U|{W}|Creature - Spirit Dog|1|1|Whenever one or more cards are put into exile during your turn, put a +1/+1 counter on Stonebinder's Familiar. This ability triggers only once each turn.| +Stonerise Spirit|Strixhaven: School of Mages|32|C|{1}{W}|Creature - Spirit Bird|1|2|Flying${4}, Exile a card from your graveyard: Target creature gains flying until end of turn.| +Strict Proctor|Strixhaven: School of Mages|33|R|{1}{W}|Creature - Spirit Cleric|1|3|Flying$Whenever a permanent entering the battlefield causes a triggered ability to trigger, counter that ability unless its controller pays {2}.| +Study Break|Strixhaven: School of Mages|34|C|{1}{W}|Instant|||Tap up to two target creatures.$Learn.| +Thunderous Orator|Strixhaven: School of Mages|35|U|{1}{W}|Creature - Kor Wizard|2|2|Vigilance$Whenever Thunderous Orator attacks, it gains flying until end of turn if you control a creature with flying. The same is true for first strike, double strike, deathtouch, indestructible, lifelink, menace, and trample.| +Arcane Subtraction|Strixhaven: School of Mages|36|C|{1}{U}|Instant|||Target creature gets -4/-0 until end of turn.$Learn.| +Archmage Emeritus|Strixhaven: School of Mages|37|R|{2}{U}{U}|Creature - Human Wizard|2|2|Magecraft — Whenever you cast or copy an instant or sorcery spell, draw a card.| +Burrog Befuddler|Strixhaven: School of Mages|38|C|{1}{U}|Creature - Frog Wizard|2|1|Flash$When Burrog Befuddler enters the battlefield, target creature an opponent controls gets -1/-0 until end of turn.| +Bury in Books|Strixhaven: School of Mages|39|C|{4}{U}|Instant|||This spell costs {2} less to cast if it targets an attacking creature.$Put target creature into its owner's library second from the top.| +Curate|Strixhaven: School of Mages|40|C|{1}{U}|Instant|||Look at the top two cards of your library. Put any number of them into your graveyard and the rest back on top of your library in any order.$Draw a card.| +Divide by Zero|Strixhaven: School of Mages|41|U|{2}{U}|Instant|||Return target spell or permanent with mana value 1 or greater to its owner's hand.$Learn.| +Dream Strix|Strixhaven: School of Mages|42|R|{2}{U}|Creature - Bird Illusion|3|2|Flying$When Dream Strix becomes the target of a spell, sacrifice it.$When Dream Strix dies, learn.| +Frost Trickster|Strixhaven: School of Mages|43|C|{2}{U}|Creature - Bird Wizard|2|2|Flying$When Frost Trickster enters the battlefield, tap target creature an opponent controls. That creature doesn't untap during its controller's next untap step.| +Ingenious Mastery|Strixhaven: School of Mages|44|R|{X}{2}{U}|Sorcery|||You may pay {2}{U} rather than pay this spell's mana cost.$If the {2}{U} cost was paid, you draw three cards, then an opponent creates two Treasure tokens and they scry 2. If that cost wasn't paid, you draw X cards.| +Kelpie Guide|Strixhaven: School of Mages|45|U|{2}{U}|Creature - Beast|2|2|{T}: Untap another target permanent you control.${T}: Tap target permanent. Activate only if you control eight or more lands.| +Mentor's Guidance|Strixhaven: School of Mages|46|U|{2}{U}|Sorcery|||When you cast this spell, copy it if you control a planeswalker, Cleric, Druid, Shaman, Warlock, or Wizard.$Scry 1, then draw a card.| +Mercurial Transformation|Strixhaven: School of Mages|47|U|{1}{U}|Sorcery - Lesson|||Until end of turn, target nonland permanent loses all abilities and becomes your choice of a blue Frog creature with base power and toughness 1/1 or a blue Octopus creature with base power and toughness 4/4.| +Multiple Choice|Strixhaven: School of Mages|48|R|{X}{U}|Sorcery|||If X is 1, scry 1, then draw a card.$If X is 2, you may choose a player. They return a creature they control to its owner's hand.$If X is 3, create a 4/4 blue and red Elemental creature token.$If X is 4 or more, do all of the above.| +Pop Quiz|Strixhaven: School of Mages|49|C|{2}{U}|Instant|||Draw a card.$Learn.| +Reject|Strixhaven: School of Mages|50|C|{1}{U}|Instant|||Counter target creature or planeswalker spell unless its controller pays {3}. If that spell is countered this way, exile it instead of putting it into its owner's graveyard.| +Resculpt|Strixhaven: School of Mages|51|C|{1}{U}|Instant|||Exile target artifact or creature. Its controller creates a 4/4 blue and red Elemental creature token.| +Serpentine Curve|Strixhaven: School of Mages|52|C|{3}{U}|Sorcery|||Create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it, where X is one plus the total number of instant and sorcery cards you own in exile and in your graveyard.| +Snow Day|Strixhaven: School of Mages|53|U|{4}{U}{U}|Instant|||Tap up to two target creatures. Those creatures don't untap during their controller's next untap step.$Draw two cards, then discard a card.| +Solve the Equation|Strixhaven: School of Mages|54|U|{2}{U}|Sorcery|||Search your library for an instant or sorcery card, reveal it, put it into your hand, then shuffle.| +Soothsayer Adept|Strixhaven: School of Mages|55|C|{1}{U}|Creature - Merfolk Wizard|1|3|{1}{U}, {T}: Draw a card, then discard a card.| +Symmetry Sage|Strixhaven: School of Mages|56|U|{U}|Creature - Human Wizard|0|2|Flying$Magecraft — Whenever you cast or copy an instant or sorcery spell, target creature you control has base power 2 until end of turn.| +Teachings of the Archaics|Strixhaven: School of Mages|57|R|{2}{U}|Sorcery - Lesson|||If an opponent has more cards in hand than you, draw two cards. Draw three cards instead if an opponent has at least four more cards in hand than you.| +Tempted by the Oriq|Strixhaven: School of Mages|58|R|{1}{U}{U}{U}|Sorcery|||For each opponent, gain control of up to one target creature or planeswalker that player controls with mana value 3 or less.| +Test of Talents|Strixhaven: School of Mages|59|U|{1}{U}|Instant|||Counter target instant or sorcery spell. Search its controller's graveyard, hand, and library for any number of cards with the same name as that spell and exile them. That player shuffles, then draws a card for each card exiled from their hand this way.| +Vortex Runner|Strixhaven: School of Mages|60|C|{2}{U}|Creature - Human Wizard|2|3|As long as you control eight or more lands, Vortex Runner gets +1/+0 and can't be blocked.| +Waterfall Aerialist|Strixhaven: School of Mages|61|C|{3}{U}|Creature - Djinn Wizard|3|1|Flying$Ward {2}| +Wormhole Serpent|Strixhaven: School of Mages|62|U|{4}{U}|Creature - Serpent|3|5|{3}{U}: Target creature can't be blocked this turn.| +Arrogant Poet|Strixhaven: School of Mages|63|C|{1}{B}|Creature - Human Warlock|2|1|Whenever Arrogant Poet attacks, you may pay 2 life. If you do, it gains flying until end of turn.| +Baleful Mastery|Strixhaven: School of Mages|64|R|{3}{B}|Instant|||You may pay {1}{B} rather than pay this spell's mana cost.$If the {1}{B} cost was paid, an opponent draws a card.$Exile target creature or planeswalker.| +Brackish Trudge|Strixhaven: School of Mages|65|U|{2}{B}|Creature - Fungus Beast|4|2|Brackish Trudge enters the battlefield tapped.${1}{B}: Return Brackish Trudge from your graveyard to your hand. Activate only if you gained life this turn.| +Callous Bloodmage|Strixhaven: School of Mages|66|R|{2}{B}|Creature - Vampire Warlock|2|1|When Callous Bloodmage enters the battlefield, choose one —$• Create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life."$• You draw a card and you lose 1 life.$• Exile target player's graveyard.| +Confront the Past|Strixhaven: School of Mages|67|R|{X}{B}|Sorcery - Lesson|||Choose one —$• Return target planeswalker card with mana value X or less from your graveyard to the battlefield.$• Remove twice X loyalty counters from target planeswalker an opponent controls.| +Crushing Disappointment|Strixhaven: School of Mages|68|C|{3}{B}|Instant|||Each player loses 2 life. You draw two cards.| +Essence Infusion|Strixhaven: School of Mages|69|C|{1}{B}|Sorcery|||Put two +1/+1 counters on target creature. It gains lifelink until end of turn.| +Eyetwitch|Strixhaven: School of Mages|70|U|{B}|Creature - Eye Bat|1|1|Flying$When Eyetwitch dies, learn.| +Flunk|Strixhaven: School of Mages|71|U|{1}{B}|Instant|||Target creature gets -X/-X until end of turn, where X is 7 minus the number of cards in that creature's controller's hand.| +Go Blank|Strixhaven: School of Mages|72|U|{2}{B}|Sorcery|||Target player discards two cards. Then exile all cards from that player's graveyard.| +Hunt for Specimens|Strixhaven: School of Mages|73|C|{1}{B}|Sorcery|||Create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life."$Learn.| +Lash of Malice|Strixhaven: School of Mages|74|C|{B}|Instant|||Target creature gets +2/-2 until end of turn.| +Leech Fanatic|Strixhaven: School of Mages|75|C|{1}{B}|Creature - Human Warlock|2|2|As long as it's your turn, Leech Fanatic has lifelink.| +Mage Hunter|Strixhaven: School of Mages|76|U|{3}{B}|Creature - Horror|3|4|Whenever an opponent casts or copies an instant or sorcery spell, they lose 1 life.| +Mage Hunters' Onslaught|Strixhaven: School of Mages|77|C|{2}{B}{B}|Sorcery|||Destroy target creature or planeswalker.$Whenever a creature blocks this turn, its controller loses 1 life.| +Necrotic Fumes|Strixhaven: School of Mages|78|U|{1}{B}{B}|Sorcery - Lesson|||As an additional cost to cast this spell, exile a creature you control.$Exile target creature or planeswalker.| +Novice Dissector|Strixhaven: School of Mages|79|C|{3}{B}|Creature - Troll Warlock|3|3|{1}, Sacrifice another creature: Put a +1/+1 counter on target creature. Activate only as a sorcery.| +Oriq Loremage|Strixhaven: School of Mages|80|R|{2}{B}{B}|Creature - Human Warlock|3|3|{T}: Search your library for a card, put it into your graveyard, then shuffle. If it's an instant or sorcery card, put a +1/+1 counter on Oriq Loremage.| +Plumb the Forbidden|Strixhaven: School of Mages|81|U|{1}{B}|Instant|||As an additional cost to cast this spell, you may sacrifice one or more creatures. When you do, copy this spell for each creature sacrificed this way.$You draw a card and you lose 1 life.| +Poet's Quill|Strixhaven: School of Mages|82|R|{1}{B}|Artifact - Equipment|||When Poet's Quill enters the battlefield, learn.$Equipped creature gets +1/+1 and has lifelink.$Equip {1}{B}| +Professor Onyx|Strixhaven: School of Mages|83|M|{4}{B}{B}|Legendary Planeswalker - Liliana|5|Magecraft — Whenever you cast or copy an instant or sorcery spell, each opponent loses 2 life and you gain 2 life.$+1: You lose 1 life. Look at the top three cards of your library. Put one of them into your hand and the rest into your graveyard.$−3: Each opponent sacrifices a creature with the greatest power among creatures that player controls.$−8: Each opponent may discard a card. If they don't, they lose 3 life. Repeat this process six more times.| +Professor's Warning|Strixhaven: School of Mages|84|C|{B}|Instant|||Choose one —$• Put a +1/+1 counter on target creature.$• Target creature gains indestructible until end of turn.| +Promising Duskmage|Strixhaven: School of Mages|85|C|{2}{B}|Creature - Human Warlock|2|3|When Promising Duskmage dies, if it had a +1/+1 counter on it, draw a card.| +Sedgemoor Witch|Strixhaven: School of Mages|86|R|{2}{B}|Creature - Human Warlock|3|2|Menace$Ward—Pay 3 life.$Magecraft — Whenever you cast or copy an instant or sorcery spell, create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life."| +Specter of the Fens|Strixhaven: School of Mages|87|C|{3}{B}|Creature - Specter|2|3|Flying${5}{B}: Target opponent loses 2 life and you gain 2 life.| +Tenured Inkcaster|Strixhaven: School of Mages|88|U|{4}{B}|Creature - Vampire Warlock|2|2|When Tenured Inkcaster enters the battlefield, put a +1/+1 counter on target creature.$Whenever a creature you control with a +1/+1 counter on it attacks, each opponent loses 1 life and you gain 1 life.| +Umbral Juke|Strixhaven: School of Mages|89|U|{2}{B}|Instant|||Choose one —$• Target player sacrifices a creature or planeswalker.$• Create a 2/1 white and black Inkling creature token with flying.| +Unwilling Ingredient|Strixhaven: School of Mages|90|C|{B}|Creature - Frog|1|1|Menace${2}{B}, Exile Unwilling Ingredient from your graveyard: You draw a card and you lose 1 life.| +Academic Dispute|Strixhaven: School of Mages|91|U|{R}|Instant|||Target creature blocks this turn if able. You may have it gain reach until end of turn.$Learn.| +Ardent Dustspeaker|Strixhaven: School of Mages|92|U|{4}{R}|Creature - Minotaur Shaman|3|4|Whenever Ardent Dustspeaker attacks, you may put an instant or sorcery card from your graveyard on the bottom of your library. If you do, exile the top two cards of your library. You may play those cards this turn.| +Blood Age General|Strixhaven: School of Mages|93|C|{1}{R}|Creature - Spirit Warrior|2|2|{T}: Attacking Spirits get +1/+0 until end of turn.| +Conspiracy Theorist|Strixhaven: School of Mages|94|R|{1}{R}|Creature - Human Shaman|2|2|Whenever Conspiracy Theorist attacks, you may pay {1} and discard a card. If you do, draw a card.$Whenever you discard one or more nonland cards, you may exile one of them from your graveyard. If you do, you may cast it this turn.| +Crackle with Power|Strixhaven: School of Mages|95|M|{X}{X}{X}{R}{R}|Sorcery|||Crackle with Power deals five times X damage to each of up to X targets.| +Draconic Intervention|Strixhaven: School of Mages|96|R|{2}{R}{R}|Sorcery|||As an additional cost to cast this spell, exile an instant or sorcery card from your graveyard.$Draconic Intervention deals X damage to each non-Dragon creature, where X is the exiled card's mana value. If a creature dealt damage this way would die this turn, exile it instead.$Exile Draconic Intervention.| +Dragon's Approach|Strixhaven: School of Mages|97|C|{2}{R}|Sorcery|||Dragon's Approach deals 3 damage to each opponent. You may exile Dragon's Approach and four cards named Dragon's Approach from your graveyard. If you do, search your library for a Dragon creature card, put it onto the battlefield, then shuffle.$A deck can have any number of cards named Dragon's Approach.| +Efreet Flamepainter|Strixhaven: School of Mages|98|R|{3}{R}|Creature - Efreet Shaman|1|4|Double strike$Whenever Efreet Flamepainter deals combat damage to a player, you may cast target instant or sorcery card from your graveyard without paying its mana cost. If that spell would be put into your graveyard, exile it instead.| +Enthusiastic Study|Strixhaven: School of Mages|99|C|{2}{R}|Instant|||Target creature gets +3/+1 and gains trample until end of turn.$Learn.| +Explosive Welcome|Strixhaven: School of Mages|100|U|{7}{R}|Instant|||Explosive Welcome deals 5 damage to any target and 3 damage to any other target. Add {R}{R}{R}.| +Fervent Mastery|Strixhaven: School of Mages|101|R|{3}{R}{R}|Sorcery|||You may pay {2}{R}{R} rather than pay this spell's mana cost.$If the {2}{R}{R} cost was paid, an opponent discards any number of cards, then draws that many cards.$Search your library for up to three cards, put them into your hand, shuffle, then discard three cards at random.| +First Day of Class|Strixhaven: School of Mages|102|C|{1}{R}|Instant|||Whenever a creature enters the battlefield under your control this turn, put a +1/+1 counter on it and it gains haste until end of turn.$Learn.| +Fuming Effigy|Strixhaven: School of Mages|103|C|{3}{R}|Creature - Spirit|4|3|Whenever one or more cards leave your graveyard, Fuming Effigy deals 1 damage to each opponent.| +Grinning Ignus|Strixhaven: School of Mages|104|U|{2}{R}|Creature - Elemental|2|2|{R}, Return Grinning Ignus to its owner's hand: Add {C}{C}{R}. Activate only as a sorcery.| +Hall Monitor|Strixhaven: School of Mages|105|U|{R}|Creature - Lizard Shaman|1|1|Haste${1}{R}, {T}: Target creature can't block this turn.| +Heated Debate|Strixhaven: School of Mages|106|C|{2}{R}|Instant|||This spell can't be countered.$Heated Debate deals 4 damage to target creature or planeswalker.| +Igneous Inspiration|Strixhaven: School of Mages|107|U|{2}{R}|Sorcery|||Igneous Inspiration deals 3 damage to any target.$Learn.| +Illuminate History|Strixhaven: School of Mages|108|R|{2}{R}{R}|Sorcery - Lesson|||Discard any number of cards, then draw that many cards. Then if there are seven or more cards in your graveyard, create a 3/2 red and white Spirit creature token.| +Illustrious Historian|Strixhaven: School of Mages|109|C|{1}{R}|Creature - Human Shaman|2|1|{5}, Exile Illustrious Historian from your graveyard: Create a tapped 3/2 red and white Spirit creature token.| +Mascot Interception|Strixhaven: School of Mages|110|U|{3}{R}|Sorcery|||This spell costs {3} less to cast if it targets a creature token.$Gain control of target creature until end of turn. Untap that creature. It gets +2/+0 and gains haste until end of turn.| +Pigment Storm|Strixhaven: School of Mages|111|C|{3}{R}{R}|Sorcery|||Pigment Storm deals 5 damage to target creature. Excess damage is dealt to that creature's controller instead.| +Pillardrop Warden|Strixhaven: School of Mages|112|C|{3}{R}|Creature - Spirit Dwarf|1|5|Reach${2}, {T}, Sacrifice Pillardrop Warden: Return target instant or sorcery card from your graveyard to your hand. Activate only as a sorcery.| +Retriever Phoenix|Strixhaven: School of Mages|113|R|{3}{R}|Creature - Phoenix|2|2|Flying, haste$When Retriever Phoenix enters the battlefield, if you cast it, learn.$As long as Retriever Phoenix is in your graveyard, if you would learn, you may instead return Retriever Phoenix to the battlefield.| +Start from Scratch|Strixhaven: School of Mages|114|U|{2}{R}|Sorcery - Lesson|||Choose one —$• Start from Scratch deals 1 damage to any target.$• Destroy target artifact.| +Storm-Kiln Artist|Strixhaven: School of Mages|115|U|{3}{R}|Creature - Dwarf Shaman|2|2|Storm-Kiln Artist gets +1/+0 for each artifact you control.$Magecraft — Whenever you cast or copy an instant or sorcery spell, create a Treasure token.| +Sudden Breakthrough|Strixhaven: School of Mages|116|C|{1}{R}|Instant|||Target creature gets +2/+0 and gains first strike until end of turn.$Create a Treasure token.| +Tome Shredder|Strixhaven: School of Mages|117|C|{2}{R}|Creature - Wolf|2|2|Haste${T}, Exile an instant or sorcery card from your graveyard: Put a +1/+1 counter on Tome Shredder.| +Twinscroll Shaman|Strixhaven: School of Mages|118|C|{2}{R}|Creature - Dwarf Shaman|1|2|Double strike| +Accomplished Alchemist|Strixhaven: School of Mages|119|R|{3}{G}|Creature - Elf Druid|2|5|{T}: Add one mana of any color.${T}: Add X mana of any one color, where X is the amount of life you gained this turn.| +Basic Conjuration|Strixhaven: School of Mages|120|R|{1}{G}{G}|Sorcery - Lesson|||Look at the top six cards of your library. You may reveal a creature card from among them and put it into your hand. Put the rest on the bottom of your library in a random order. You gain 3 life.| +Bayou Groff|Strixhaven: School of Mages|121|C|{1}{G}|Creature - Plant Dog|5|4|As an additional cost to cast this spell, sacrifice a creature or pay {3}.| +Big Play|Strixhaven: School of Mages|122|C|{1}{G}|Instant|||Target creature gets +2/+2 and gains reach until end of turn. Put a +1/+1 counter on it.| +Bookwurm|Strixhaven: School of Mages|123|U|{7}{G}|Creature - Wurm|7|7|Trample$When Bookwurm enters the battlefield, you gain 3 life and draw a card.${2}{G}: Put Bookwurm from your graveyard into your library third from the top.| +Charge Through|Strixhaven: School of Mages|124|C|{G}|Instant|||Target creature gains trample until end of turn.$Draw a card.| +Containment Breach|Strixhaven: School of Mages|125|U|{2}{G}|Sorcery - Lesson|||Destroy target artifact or enchantment. If its mana value is 2 or less, create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life."| +Devouring Tendrils|Strixhaven: School of Mages|126|U|{1}{G}|Sorcery|||Target creature you control deals damage equal to its power to target creature or planeswalker you don't control. When the permanent you don't control dies this turn, you gain 2 life.| +Dragonsguard Elite|Strixhaven: School of Mages|127|R|{1}{G}|Creature - Human Druid|2|2|Magecraft — Whenever you cast or copy an instant or sorcery spell, put a +1/+1 counter on Dragonsguard Elite.${4}{G}{G}: Double the number of +1/+1 counters on Dragonsguard Elite.| +Ecological Appreciation|Strixhaven: School of Mages|128|M|{X}{2}{G}|Sorcery|||Search your library and graveyard for up to four creature cards with different names that each have mana value X or less and reveal them. An opponent chooses two of those cards. Shuffle the chosen cards into your library and put the rest onto the battlefield. Exile Ecological Appreciation.| +Emergent Sequence|Strixhaven: School of Mages|129|U|{1}{G}|Sorcery|||Search your library for a basic land card, put it onto the battlefield tapped, then shuffle. That land becomes a 0/0 green and blue Fractal creature that's still a land. Put a +1/+1 counter on it for each land you had enter the battlefield under your control this turn.| +Exponential Growth|Strixhaven: School of Mages|130|R|{X}{X}{G}{G}|Sorcery|||Until end of turn, double target creature's power X times.| +Field Trip|Strixhaven: School of Mages|131|C|{2}{G}|Sorcery|||Search your library for a basic Forest card, put that card onto the battlefield tapped, then shuffle.$Learn.| +Fortifying Draught|Strixhaven: School of Mages|132|U|{G}|Instant|||You gain 2 life. Target creature gets +X/+X until end of turn, where X is the amount of life you gained this turn.| +Gnarled Professor|Strixhaven: School of Mages|133|R|{2}{G}{G}|Creature - Treefolk Druid|5|4|Trample$When Gnarled Professor enters the battlefield, learn.| +Honor Troll|Strixhaven: School of Mages|134|U|{2}{G}|Creature - Troll Druid|2|3|Vigilance$If you would gain life, you gain that much life plus 1 instead.$Honor Troll gets +2/+1 as long as you have 25 or more life.| +Karok Wrangler|Strixhaven: School of Mages|135|U|{4}{G}|Creature - Elf Druid|3|3|Magecraft — Whenever you cast or copy an instant or sorcery spell, put a +1/+1 counter on target creature you control.| +Leyline Invocation|Strixhaven: School of Mages|136|C|{5}{G}|Sorcery|||Create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it, where X is the number of lands you control.| +Mage Duel|Strixhaven: School of Mages|137|C|{2}{G}|Sorcery|||This spell costs {2} less to cast if you've cast another instant or sorcery spell this turn.$Target creature you control gets +1/+2 until end of turn. Then it fights target creature you don't control.| +Master Symmetrist|Strixhaven: School of Mages|138|U|{2}{G}{G}|Creature - Rhino Druid|4|4|Reach$Whenever a creature you control with power equal to its toughness attacks, it gains trample until end of turn.| +Overgrown Arch|Strixhaven: School of Mages|139|U|{1}{G}|Creature - Plant Wall|0|4|Defender${T}: You gain 1 life.${2}, Sacrifice Overgrown Arch: Learn.| +Professor of Zoomancy|Strixhaven: School of Mages|140|C|{3}{G}|Creature - Bear Druid|4|3|When Professor of Zoomancy enters the battlefield, create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life."| +Reckless Amplimancer|Strixhaven: School of Mages|141|C|{1}{G}|Creature - Elf Druid|2|2|{4}{G}: Double Reckless Amplimancer's power and toughness until end of turn.| +Scurrid Colony|Strixhaven: School of Mages|142|C|{1}{G}|Creature - Squirrel|2|2|Reach$Scurrid Colony gets +2/+2 as long as you control eight or more lands.| +Spined Karok|Strixhaven: School of Mages|143|C|{2}{G}|Creature - Crocodile|2|4|| +Springmane Cervin|Strixhaven: School of Mages|144|C|{2}{G}|Creature - Elk|3|2|When Springmane Cervin enters the battlefield, you gain 2 life.| +Tangletrap|Strixhaven: School of Mages|145|C|{1}{G}|Instant|||Choose one —$• Tangletrap deals 5 damage to target creature with flying.$• Destroy target artifact.| +Verdant Mastery|Strixhaven: School of Mages|146|R|{5}{G}|Sorcery|||You may pay {3}{G} rather than pay this spell's mana cost.$Search your library for up to four basic land cards and reveal them. Put one of them onto the battlefield tapped under an opponent's control if the {3}{G} cost was paid. Put two of them onto the battlefield tapped under your control and the rest into your hand. Then shuffle.| +Augmenter Pugilist|Strixhaven: School of Mages|147|R|{1}{G}{G}|Creature - Troll Druid|3|3|Trample$As long as you control eight or more lands, Augmenter Pugilist gets +5/+5.| +Echoing Equation|Strixhaven: School of Mages|147|R|{3}{U}{U}|Sorcery|||Choose target creature you control. Each other creature you control becomes a copy of it until end of turn, except those creatures aren't legendary if the chosen creature is legendary.| +Blex, Vexing Pest|Strixhaven: School of Mages|148|M|{2}{G}|Legendary Creature - Pest|3|2|Other Pests, Bats, Insects, Snakes, and Spiders you control get +1/+1.$When Blex, Vexing Pest dies, you gain 4 life.| +Search for Blex|Strixhaven: School of Mages|148|M|{2}{B}{B}|Sorcery|||Look at the top five cards of your library. You may put any number of them into your hand and the rest into your graveyard. You lose 3 life for each card you put into your hand this way.| +Awaken the Blood Avatar|Strixhaven: School of Mages|149|M|{6}{B}{R}|Sorcery|||As an additional cost to cast this spell, you may sacrifice any number of creatures. This spell costs {2} less to cast for each creature sacrificed this way.$Each opponent sacrifices a creature. Create a 3/6 black and red Avatar creature token with haste and "Whenever this creature attacks, it deals 3 damage to each opponent."| +Extus, Oriq Overlord|Strixhaven: School of Mages|149|M|{1}{W}{B}{B}|Legendary Creature - Human Warlock|2|4|Double strike$Magecraft — Whenever you cast or copy an instant or sorcery spell, return target nonlegendary creature card from your graveyard to your hand.| +Flamescroll Celebrant|Strixhaven: School of Mages|150|R|{1}{R}|Creature - Human Shaman|2|1|Whenever an opponent activates an ability that isn't a mana ability, Flamescroll Celebrant deals 1 damage to that player.${1}{R}: Flamescroll Celebrant gets +2/+0 until end of turn.| +Revel in Silence|Strixhaven: School of Mages|150|R|{W}{W}|Instant|||Your opponents can't cast spells or activate planeswalkers' loyalty abilities this turn.$Exile Revel in Silence.| +Jadzi, Oracle of Arcavios|Strixhaven: School of Mages|151|M|{6}{U}{U}|Legendary Creature - Human Wizard|5|5|Discard a card: Return Jadzi, Oracle of Arcavios to its owner's hand.$Magecraft — Whenever you cast or copy an instant or sorcery spell, reveal the top card of your library. If it's a nonland card, you may cast it by paying {1} rather than paying its mana cost. If it's a land card, put it onto the battlefield.| +Journey to the Oracle|Strixhaven: School of Mages|151|M|{2}{G}{G}|Sorcery|||You may put any number of land cards from your hand onto the battlefield. Then if you control eight or more lands, you may discard a card. If you do, return Journey to the Oracle to its owner's hand.| +Imbraham, Dean of Theory|Strixhaven: School of Mages|152|R|{2}{U}{U}|Legendary Creature - Bird Wizard|3|3|Flying${X}{U}{U}, {T}: Exile the top X cards of your library and put a study counter on each of them. Then you may put a card you own in exile with a study counter on it into your hand.| +Kianne, Dean of Substance|Strixhaven: School of Mages|152|R|{2}{G}|Legendary Creature - Elf Druid|2|2|{T}: Exile the top card of your library. If it's a land card, put it into your hand. Otherwise, put a study counter on it.${4}{G}: Create a 0/0 green and blue Fractal creature token. Put a +1/+1 counter on it for each different mana value among nonland cards you own in exile with study counters on them.| +Lukka, Wayward Bonder|Strixhaven: School of Mages|153|M|{4}{R}{R}|Legendary Planeswalker - Lukka|5|+1: You may discard a card. If you do, draw a card. If a creature card was discarded this way, draw two cards instead.$−2: Return target creature card from your graveyard to the battlefield. It gains haste. Exile it at the beginning of your next upkeep.$−7: You get an emblem with "Whenever a creature enters the battlefield under your control, it deals damage equal to its power to any target."| +Mila, Crafty Companion|Strixhaven: School of Mages|153|M|{1}{W}{W}|Legendary Creature - Fox|2|3|Whenever an opponent attacks one or more planeswalkers you control, put a loyalty counter on each planeswalker you control.$Whenever a permanent you control becomes the target of a spell or ability an opponent controls, you may draw a card.| +Pestilent Cauldron|Strixhaven: School of Mages|154|R|{2}{B}|Artifact|||{T}, Discard a card: Create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life."${1}, {T}: Each opponent mills cards equal to the amount of life you gained this turn.${4}, {T}: Exile four target cards from a single graveyard. Draw a card.| +Restorative Burst|Strixhaven: School of Mages|154|R|{3}{G}{G}|Sorcery|||Return up to two target creature, land, and/or planeswalker cards from your graveyard to your hand. Each player gains 4 life. Exile Restorative Burst.| +Augusta, Dean of Order|Strixhaven: School of Mages|155|R|{2}{W}|Legendary Creature - Human Cleric|1|3|Other tapped creatures you control get +1/+0.$Other untapped creatures you control get +0/+1.$Whenever you attack, untap each creature you control, then tap any number of creatures you control.| +Plargg, Dean of Chaos|Strixhaven: School of Mages|155|R|{1}{R}|Legendary Creature - Orc Shaman|2|2|{T}, Discard a card: Draw a card.${4}{R}, {T}: Reveal cards from the top of your library until you reveal a nonlegendary, nonland card with mana value 3 or less. You may cast that card without paying its mana cost. Put all revealed cards not cast this way on the bottom of your library in a random order.| +Rowan, Scholar of Sparks|Strixhaven: School of Mages|156|M|{2}{R}|Legendary Planeswalker - Rowan|2|Instant and sorcery spells you cast cost {1} less to cast.$+1: Rowan, Scholar of Sparks deals 1 damage to each opponent. If you've drawn three or more cards this turn, she deals 3 damage to each opponent instead.$−4: You get an emblem with "Whenever you cast an instant or sorcery spell, you may pay {2}. If you do, copy that spell. You may choose new targets for the copy."| +Will, Scholar of Frost|Strixhaven: School of Mages|156|M|{4}{U}|Legendary Planeswalker - Will|4|Instant and sorcery spells you cast cost {1} less to cast.$+1: Up to one target creature has base power and toughness 0/2 until your next turn.$−3: Draw two cards.$−7: Exile up to five target permanents. For each permanent exiled this way, its controller creates a 4/4 blue and red Elemental creature token.| +Deadly Vanity|Strixhaven: School of Mages|157|R|{5}{B}{B}{B}|Sorcery|||Choose a creature or planeswalker, then destroy all other creatures and planeswalkers.| +Selfless Glyphweaver|Strixhaven: School of Mages|157|R|{2}{W}|Creature - Human Cleric|2|3|Exile Selfless Glyphweaver: Creatures you control gain indestructible until end of turn.| +Embrose, Dean of Shadow|Strixhaven: School of Mages|158|R|{2}{B}{B}|Legendary Creature - Human Warlock|4|4|{T}: Put a +1/+1 counter on another target creature, then Embrose, Dean of Shadow deals 2 damage to that creature.$Whenever a creature you control with a +1/+1 counter on it dies, draw a card.| +Shaile, Dean of Radiance|Strixhaven: School of Mages|158|R|{1}{W}|Legendary Creature - Bird Cleric|1|1|Flying, vigilance${T}: Put a +1/+1 counter on each creature that entered the battlefield under your control this turn.| +Flamethrower Sonata|Strixhaven: School of Mages|159|R|{1}{R}|Sorcery|||Discard a card, then draw a card. When you discard an instant or sorcery card this way, Flamethrower Sonata deals damage equal to that card's mana value to target creature or planeswalker you don't control.| +Torrent Sculptor|Strixhaven: School of Mages|159|R|{2}{U}{U}|Creature - Merfolk Wizard|2|2|Ward {2}$When Torrent Sculptor enters the battlefield, exile an instant or sorcery card from your graveyard. Put a number of +1/+1 counters on Torrent Sculptor equal to half that card's mana value, rounded up.| +Nassari, Dean of Expression|Strixhaven: School of Mages|160|R|{3}{R}{R}|Legendary Creature - Efreet Shaman|4|4|At the beginning of your upkeep, exile the top card of each opponent's library. Until end of turn, you may cast spells from among those exiled cards, and you may spend mana as though it were mana of any color to cast those spells.$Whenever you cast a spell from exile, put a +1/+1 counter on Nassari, Dean of Expression.| +Uvilda, Dean of Perfection|Strixhaven: School of Mages|160|R|{2}{U}|Legendary Creature - Djinn Wizard|2|2|{T}: You may exile an instant or sorcery card from your hand and put three hone counters on it. It gains "At the beginning of your upkeep, if this card is exiled, remove a hone counter from it" and "When the last hone counter is removed from this card, if it's exiled, you may cast it. It costs {4} less to cast this way."| +Lisette, Dean of the Root|Strixhaven: School of Mages|161|R|{2}{G}{G}|Legendary Creature - Human Druid|4|4|Whenever you gain life, you may pay {1}. If you do, put a +1/+1 counter on each creature you control and those creatures gain trample until end of turn.| +Valentin, Dean of the Vein|Strixhaven: School of Mages|161|R|{B}|Legendary Creature - Vampire Warlock|1|1|Menace, lifelink$If a nontoken creature an opponent controls would die, exile it instead. When you do, you may pay {2}. If you do, create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life."| +Aether Helix|Strixhaven: School of Mages|162|U|{3}{G}{U}|Sorcery|||Return target permanent to its owner's hand. Return target permanent card from your graveyard to your hand.| +Beledros Witherbloom|Strixhaven: School of Mages|163|M|{5}{B}{G}|Legendary Creature - Elder Dragon|4|4|Flying$At the beginning of each upkeep, create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life."$Pay 10 life: Untap all lands you control. Activate only once each turn.| +Biomathematician|Strixhaven: School of Mages|164|C|{1}{G}{U}|Creature - Human Wizard|2|2|When Biomathematician enters the battlefield, create a 0/0 green and blue Fractal creature token. Put a +1/+1 counter on each Fractal you control.| +Blade Historian|Strixhaven: School of Mages|165|R|{R/W}{R/W}{R/W}{R/W}|Creature - Human Cleric|2|3|Attacking creatures you control have double strike.| +Blood Researcher|Strixhaven: School of Mages|166|C|{1}{B}{G}|Creature - Vampire Druid|2|2|Menace$Whenever you gain life, put a +1/+1 counter on Blood Researcher.| +Blot Out the Sky|Strixhaven: School of Mages|167|M|{X}{W}{B}|Sorcery|||Create X tapped 2/1 white and black Inkling creature tokens with flying. If X is 6 or more, destroy all noncreature, nonland permanents.| +Body of Research|Strixhaven: School of Mages|168|M|{G}{G}{G}{U}{U}{U}|Sorcery|||Create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it, where X is the number of cards in your library.| +Closing Statement|Strixhaven: School of Mages|169|U|{3}{W}{B}|Instant|||This spell costs {2} less to cast during your end step.$Destroy target creature or planeswalker you don't control. Put a +1/+1 counter on up to one target creature you control.| +Cram Session|Strixhaven: School of Mages|170|C|{1}{B/G}|Sorcery|||You gain 4 life.$Learn.| +Creative Outburst|Strixhaven: School of Mages|171|U|{3}{U}{U}{R}{R}|Instant|||Creative Outburst deals 5 damage to any target. Look at the top five cards of your library. Put one of them into your hand and the rest on the bottom of your library in a random order.${U/R}{U/R}, Discard Creative Outburst: Create a Treasure token.| +Culling Ritual|Strixhaven: School of Mages|172|R|{2}{B}{G}|Sorcery|||Destroy each nonland permanent with mana value 2 or less. Add {B} or {G} for each permanent destroyed this way.| +Culmination of Studies|Strixhaven: School of Mages|173|R|{X}{U}{R}|Sorcery|||Exile the top X cards of your library. For each land card exiled this way, create a Treasure token. For each blue card exiled this way, draw a card. For each red card exiled this way, Culmination of Studies deals 1 damage to each opponent.| +Daemogoth Titan|Strixhaven: School of Mages|174|R|{B/G}{B/G}{B/G}{B/G}|Creature - Demon|11|10|Whenever Daemogoth Titan attacks or blocks, sacrifice a creature.| +Daemogoth Woe-Eater|Strixhaven: School of Mages|175|U|{1}{B}{B/G}{G}|Creature - Demon|7|6|At the beginning of your upkeep, sacrifice a creature.$When you sacrifice Daemogoth Woe-Eater, each opponent discards a card, you draw a card, and you gain 2 life.| +Deadly Brew|Strixhaven: School of Mages|176|U|{B}{G}|Sorcery|||Each player sacrifices a creature or planeswalker. If you sacrificed a permanent this way, you may return another permanent card from your graveyard to your hand.| +Decisive Denial|Strixhaven: School of Mages|177|U|{G}{U}|Instant|||Choose one —$• Target creature you control fights target creature you don't control.$• Counter target noncreature spell unless its controller pays {3}.| +Dina, Soul Steeper|Strixhaven: School of Mages|178|U|{B}{G}|Legendary Creature - Dryad Druid|1|3|Whenever you gain life, each opponent loses 1 life.${1}, Sacrifice another creature: Dina, Soul Steeper gets +X/+0 until end of turn, where X is the sacrificed creature's power.| +Double Major|Strixhaven: School of Mages|179|R|{G}{U}|Instant|||Copy target creature spell you control, except it isn't legendary if the spell is legendary.| +Dramatic Finale|Strixhaven: School of Mages|180|R|{W/B}{W/B}{W/B}{W/B}|Enchantment|||Creature tokens you control get +1/+1.$Whenever one or more nontoken creatures you control die, create a 2/1 white and black Inkling creature token with flying. This ability triggers only once each turn.| +Elemental Expressionist|Strixhaven: School of Mages|181|R|{U/R}{U/R}{U/R}{U/R}|Creature - Orc Wizard|4|4|Magecraft — Whenever you cast or copy an instant or sorcery spell, choose target creature you control. Until end of turn, it gains "If this creature would leave the battlefield, exile it instead of putting it anywhere else" and "When this creature is put into exile, create a 4/4 blue and red Elemental creature token."| +Elemental Masterpiece|Strixhaven: School of Mages|182|C|{5}{U}{R}|Sorcery|||Create two 4/4 blue and red Elemental creature tokens.${U/R}{U/R}, Discard Elemental Masterpiece: Create a Treasure token.| +Elemental Summoning|Strixhaven: School of Mages|183|C|{3}{U/R}{U/R}|Sorcery - Lesson|||Create a 4/4 blue and red Elemental creature token.| +Eureka Moment|Strixhaven: School of Mages|184|C|{2}{G}{U}|Instant|||Draw two cards. You may put a land card from your hand onto the battlefield.| +Exhilarating Elocution|Strixhaven: School of Mages|185|C|{2}{W}{B}|Sorcery|||Put two +1/+1 counters on target creature you control. Other creatures you control get +1/+1 until end of turn.| +Expressive Iteration|Strixhaven: School of Mages|186|U|{U}{R}|Sorcery|||Look at the top three cards of your library. Put one of them into your hand, put one of them on the bottom of your library, and exile one of them. You may play the exiled card this turn.| +Fractal Summoning|Strixhaven: School of Mages|187|C|{X}{G/U}{G/U}|Sorcery - Lesson|||Create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it.| +Fracture|Strixhaven: School of Mages|188|U|{W}{B}|Instant|||Destroy target artifact, enchantment, or planeswalker.| +Galazeth Prismari|Strixhaven: School of Mages|189|M|{2}{U}{R}|Legendary Creature - Elder Dragon|3|4|Flying$When Galazeth Prismari enters the battlefield, create a Treasure token.$Artifacts you control have "{T}: Add one mana of any color. Spend this mana only to cast an instant or sorcery spell."| +Golden Ratio|Strixhaven: School of Mages|190|U|{1}{G}{U}|Sorcery|||Draw a card for each different power among creatures you control.| +Harness Infinity|Strixhaven: School of Mages|191|M|{1}{B}{B}{B}{G}{G}{G}|Instant|||Exchange your hand and graveyard.$Exile Harness Infinity.| +Hofri Ghostforge|Strixhaven: School of Mages|192|M|{3}{R}{W}|Legendary Creature - Dwarf Cleric|4|5|Spirits you control get +1/+1 and have trample and haste.$Whenever another nontoken creature you control dies, exile it. If you do, create a token that's a copy of that creature, except it's a Spirit in addition to its other types and it has "When this creature leaves the battlefield, return the exiled card to your graveyard."| +Humiliate|Strixhaven: School of Mages|193|U|{W}{B}|Sorcery|||Target opponent reveals their hand. You choose a nonland card from it. That player discards that card. Put a +1/+1 counter on a creature you control.| +Infuse with Vitality|Strixhaven: School of Mages|194|C|{B}{G}|Instant|||Until end of turn, target creature gains deathtouch and "When this creature dies, return it to the battlefield tapped under its owner's control."$You gain 2 life.| +Inkling Summoning|Strixhaven: School of Mages|195|C|{1}{W/B}{W/B}|Sorcery - Lesson|||Create a 2/1 white and black Inkling creature token with flying.| +Kasmina, Enigma Sage|Strixhaven: School of Mages|196|M|{1}{G}{U}|Legendary Planeswalker - Kasmina|2|Each other planeswalker you control has the loyalty abilities of Kasmina, Enigma Sage.$+2: Scry 1.$−X: Create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it.$−8: Search your library for an instant or sorcery card that shares a color with this planeswalker, exile that card, then shuffle. You may cast that card without paying its mana cost.| +Killian, Ink Duelist|Strixhaven: School of Mages|197|U|{W}{B}|Legendary Creature - Human Warlock|2|2|Lifelink$Menace$Spells you cast that target a creature cost {2} less to cast.| +Lorehold Apprentice|Strixhaven: School of Mages|198|U|{R}{W}|Creature - Human Cleric|2|2|Magecraft — Whenever you cast or copy an instant or sorcery spell, until end of turn, Spirit creatures you control gain "{T}: This creature deals 1 damage to each opponent."| +Lorehold Command|Strixhaven: School of Mages|199|R|{3}{R}{W}|Instant|||Choose two —$• Create a 3/2 red and white Spirit creature token.$• Creatures you control get +1/+0 and gain indestructible and haste until end of turn.$• Lorehold Command deals 3 damage to any target. Target player gains 3 life.$• Sacrifice a permanent, then draw two cards.| +Lorehold Excavation|Strixhaven: School of Mages|200|U|{R}{W}|Enchantment|||At the beginning of your end step, mill a card. If a land card was milled this way, you gain 1 life. Otherwise, Lorehold Excavation deals 1 damage to each opponent.${5}, Exile a creature card from your graveyard: Create a tapped 3/2 red and white Spirit creature token.| +Lorehold Pledgemage|Strixhaven: School of Mages|201|C|{1}{R/W}{R/W}|Creature - Kor Shaman|2|2|First strike$Magecraft — Whenever you cast or copy an instant or sorcery spell, Lorehold Pledgemage gets +1/+0 until end of turn.| +Maelstrom Muse|Strixhaven: School of Mages|202|U|{1}{U}{U/R}{R}|Creature - Djinn Wizard|2|4|Flying$Whenever Maelstrom Muse attacks, the next instant or sorcery spell you cast this turn costs {X} less to cast, where X is Maelstrom Muse's power as this ability resolves.| +Magma Opus|Strixhaven: School of Mages|203|M|{6}{U}{R}|Instant|||Magma Opus deals 4 damage divided as you choose among any number of targets. Tap two target permanents. Create a 4/4 blue and red Elemental creature token. Draw two cards.${U/R}{U/R}, Discard Magma Opus: Create a Treasure token.| +Make Your Mark|Strixhaven: School of Mages|204|C|{R/W}|Instant|||Target creature gets +1/+0 until end of turn. When that creature dies this turn, create a 3/2 red and white Spirit creature token.| +Manifestation Sage|Strixhaven: School of Mages|205|R|{G/U}{G/U}{G/U}{G/U}|Creature - Human Wizard|2|2|When Manifestation Sage enters the battlefield, create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it, where X is the number of cards in your hand.| +Moldering Karok|Strixhaven: School of Mages|206|C|{2}{B}{G}|Creature - Zombie Crocodile|3|3|Trample, lifelink| +Mortality Spear|Strixhaven: School of Mages|207|U|{2}{B}{G}|Instant|||This spell costs {2} less to cast if you gained life this turn.$Destroy target nonland permanent.| +Needlethorn Drake|Strixhaven: School of Mages|208|C|{G}{U}|Creature - Drake|1|1|Flying, deathtouch| +Oggyar Battle-Seer|Strixhaven: School of Mages|209|C|{3}{U}{R}|Creature - Ogre Shaman|3|4|Haste${T}: Scry 1.| +Owlin Shieldmage|Strixhaven: School of Mages|210|C|{3}{W}{B}|Creature - Bird Warlock|3|3|Flying$Ward—Pay 3 life.| +Pest Summoning|Strixhaven: School of Mages|211|C|{1}{B/G}{B/G}|Sorcery - Lesson|||Create two 1/1 black and green Pest creature tokens with "When this creature dies, you gain 1 life."| +Practical Research|Strixhaven: School of Mages|212|U|{3}{U}{R}|Instant|||Draw four cards. Then discard two cards unless you discard an instant or sorcery card.| +Prismari Apprentice|Strixhaven: School of Mages|213|U|{U}{R}|Creature - Human Shaman|2|2|Magecraft — Whenever you cast or copy an instant or sorcery spell, Prismari Apprentice can't be blocked this turn. If that spell has mana value 5 or greater, put a +1/+1 counter on Prismari Apprentice.| +Prismari Command|Strixhaven: School of Mages|214|R|{1}{U}{R}|Instant|||Choose two —$• Prismari Command deals 2 damage to any target.$• Target player draws two cards, then discards two cards.$• Target player creates a Treasure token.$• Destroy target artifact.| +Prismari Pledgemage|Strixhaven: School of Mages|215|C|{U/R}{U/R}|Creature - Orc Wizard|3|3|Defender$Magecraft — Whenever you cast or copy an instant or sorcery spell, Prismari Pledgemage can attack this turn as though it didn't have defender.| +Quandrix Apprentice|Strixhaven: School of Mages|216|U|{G}{U}|Creature - Human Wizard|2|2|Magecraft — Whenever you cast or copy an instant or sorcery spell, look at the top three cards of your library. You may reveal a land card from among them and put that card into your hand. Put the rest on the bottom of your library in any order.| +Quandrix Command|Strixhaven: School of Mages|217|R|{1}{G}{U}|Instant|||Choose two —$• Return target creature or planeswalker to its owner's hand.$• Counter target artifact or enchantment spell.$• Put two +1/+1 counters on target creature.$• Target player shuffles up to three target cards from their graveyard into their library.| +Quandrix Cultivator|Strixhaven: School of Mages|218|U|{1}{G}{G/U}{U}|Creature - Turtle Druid|3|4|When Quandrix Cultivator enters the battlefield, you may search your library for a basic Forest or Island card, put it onto the battlefield, then shuffle.| +Quandrix Pledgemage|Strixhaven: School of Mages|219|C|{1}{G/U}{G/U}|Creature - Merfolk Druid|2|2|Magecraft — Whenever you cast or copy an instant or sorcery spell, put a +1/+1 counter on Quandrix Pledgemage.| +Quintorius, Field Historian|Strixhaven: School of Mages|220|U|{3}{R}{W}|Legendary Creature - Elephant Cleric|2|4|Spirits you control get +1/+0.$Whenever one or more cards leave your graveyard, create a 3/2 red and white Spirit creature token.| +Radiant Scrollwielder|Strixhaven: School of Mages|221|R|{2}{R}{W}|Creature - Dwarf Cleric|2|4|Instant and sorcery spells you control have lifelink.$At the beginning of your upkeep, exile an instant or sorcery card at random from your graveyard. You may cast it this turn. If a spell cast this way would be put into your graveyard, exile it instead.| +Reconstruct History|Strixhaven: School of Mages|222|U|{2}{R}{W}|Sorcery|||Return up to one target artifact card, up to one target enchantment card, up to one target instant card, up to one target sorcery card, and up to one target planeswalker card from your graveyard to your hand.$Exile Reconstruct History.| +Relic Sloth|Strixhaven: School of Mages|223|C|{3}{R}{W}|Creature - Beast|4|4|Vigilance$Menace| +Returned Pastcaller|Strixhaven: School of Mages|224|U|{3}{R}{R/W}{W}|Creature - Spirit Cleric|4|2|Flying$When Returned Pastcaller enters the battlefield, return target Spirit, instant, or sorcery card from your graveyard to your hand.| +Rip Apart|Strixhaven: School of Mages|225|U|{R}{W}|Sorcery|||Choose one —$• Rip Apart deals 3 damage to target creature or planeswalker.$• Destroy target artifact or enchantment.| +Rise of Extus|Strixhaven: School of Mages|226|C|{4}{W/B}{W/B}|Sorcery|||Exile target creature. Exile up to one target instant or sorcery card from a graveyard.$Learn.| +Rootha, Mercurial Artist|Strixhaven: School of Mages|227|U|{1}{U}{R}|Legendary Creature - Orc Shaman|1|4|{2}, Return Rootha, Mercurial Artist to its owner's hand: Copy target instant or sorcery spell you control. You may choose new targets for the copy.| +Rushed Rebirth|Strixhaven: School of Mages|228|R|{B}{G}|Instant|||Choose target creature. When that creature dies this turn, search your library for a creature card with lesser mana value, put it onto the battlefield tapped, then shuffle.| +Shadewing Laureate|Strixhaven: School of Mages|229|U|{W}{W/B}{B}|Creature - Human Warlock|2|2|Flying$Whenever another creature you control with flying dies, put a +1/+1 counter on target creature you control.| +Shadrix Silverquill|Strixhaven: School of Mages|230|M|{3}{W}{B}|Legendary Creature - Elder Dragon|2|5|Flying, double strike$At the beginning of combat on your turn, you may choose two. Each mode must target a different player.$• Target player creates a 2/1 white and black Inkling creature token with flying.$• Target player draws a card and loses 1 life.$• Target player puts a +1/+1 counter on each creature they control.| +Silverquill Apprentice|Strixhaven: School of Mages|231|U|{W}{B}|Creature - Human Warlock|2|2|Magecraft — Whenever you cast or copy an instant or sorcery spell, target creature gets +1/+0 until end of turn.| +Silverquill Command|Strixhaven: School of Mages|232|R|{2}{W}{B}|Sorcery|||Choose two —$• Target creature gets +3/+3 and gains flying until end of turn.$• Return target creature card with mana value 2 or less from your graveyard to the battlefield.$• Target player draws a card and loses 1 life.$• Target opponent sacrifices a creature.| +Silverquill Pledgemage|Strixhaven: School of Mages|233|C|{1}{W/B}{W/B}|Creature - Vampire Cleric|3|1|Magecraft — Whenever you cast or copy an instant or sorcery spell, Silverquill Pledgemage gains your choice of flying or lifelink until end of turn.| +Silverquill Silencer|Strixhaven: School of Mages|234|R|{W}{B}|Creature - Human Cleric|3|2|As Silverquill Silencer enters the battlefield, choose a nonland card name.$Whenever an opponent casts a spell with the chosen name, they lose 3 life and you draw a card.| +Spectacle Mage|Strixhaven: School of Mages|235|C|{1}{U}{R}|Creature - Bird Shaman|2|2|Flying$Instant and sorcery spells you cast with mana value 5 or greater cost {1} less to cast.| +Spirit Summoning|Strixhaven: School of Mages|236|C|{1}{R/W}{R/W}|Sorcery - Lesson|||Create a 3/2 red and white Spirit creature token.| +Spiteful Squad|Strixhaven: School of Mages|237|C|{2}{W}{B}|Creature - Human Warlock|0|0|Deathtouch$Spiteful Squad enters the battlefield with two +1/+1 counters on it.$When Spiteful Squad dies, put its counters on target creature you control.| +Square Up|Strixhaven: School of Mages|238|C|{1}{G/U}|Instant|||Target creature has base power and toughness 4/4 until end of turn.| +Stonebound Mentor|Strixhaven: School of Mages|239|C|{1}{R}{W}|Creature - Spirit Advisor|3|3|Whenever one or more cards leave your graveyard, scry 1.| +Tanazir Quandrix|Strixhaven: School of Mages|240|M|{3}{G}{U}|Legendary Creature - Elder Dragon|4|4|Flying, trample$When Tanazir Quandrix enters the battlefield, double the number of +1/+1 counters on target creature you control.$Whenever Tanazir Quandrix attacks, you may have the base power and toughness of other creatures you control become equal to Tanazir Quandrix's power and toughness until end of turn.| +Teach by Example|Strixhaven: School of Mages|241|C|{U/R}{U/R}|Instant|||When you cast your next instant or sorcery spell this turn, copy that spell. You may choose new targets for the copy.| +Tend the Pests|Strixhaven: School of Mages|242|U|{B}{G}|Instant|||As an additional cost to cast this spell, sacrifice a creature.$Create X 1/1 black and green Pest creature tokens with "When this creature dies, you gain 1 life," where X is the sacrificed creature's power.| +Thrilling Discovery|Strixhaven: School of Mages|243|C|{R}{W}|Sorcery|||You gain 2 life. Then you may discard two cards. If you do, draw three cards.| +Vanishing Verse|Strixhaven: School of Mages|244|R|{W}{B}|Instant|||Exile target monocolored permanent.| +Velomachus Lorehold|Strixhaven: School of Mages|245|M|{5}{R}{W}|Legendary Creature - Elder Dragon|5|5|Flying, vigilance, haste$Whenever Velomachus Lorehold attacks, look at the top seven cards of your library. You may cast an instant or sorcery spell with mana value less than or equal to Velomachus Lorehold's power from among them without paying its mana cost. Put the rest on the bottom of your library in a random order.| +Venerable Warsinger|Strixhaven: School of Mages|246|R|{1}{R}{W}|Creature - Spirit Cleric|3|3|Vigilance, trample$Whenever Venerable Warsinger deals combat damage to a player, you may return target creature card with mana value X or less from your graveyard to the battlefield, where X is the amount of damage Venerable Warsinger dealt to that player.| +Witherbloom Apprentice|Strixhaven: School of Mages|247|U|{B}{G}|Creature - Human Druid|2|2|Magecraft — Whenever you cast or copy an instant or sorcery spell, each opponent loses 1 life and you gain 1 life.| +Witherbloom Command|Strixhaven: School of Mages|248|R|{B}{G}|Sorcery|||Choose two —$• Target player mills three cards, then you return a land card from your graveyard to your hand.$• Destroy target noncreature, nonland permanent with mana value 2 or less.$• Target creature gets -3/-1 until end of turn.$• Target opponent loses 2 life and you gain 2 life.| +Witherbloom Pledgemage|Strixhaven: School of Mages|249|C|{3}{B/G}{B/G}|Creature - Treefolk Warlock|5|5|Magecraft — Whenever you cast or copy an instant or sorcery spell, you gain 1 life.| +Zimone, Quandrix Prodigy|Strixhaven: School of Mages|250|U|{G}{U}|Legendary Creature - Human Wizard|1|2|{1}, {T}: You may put a land card from your hand onto the battlefield tapped.${4}, {T}: Draw a card. If you control eight or more lands, draw two cards instead.| +Biblioplex Assistant|Strixhaven: School of Mages|251|C|{4}|Artifact Creature - Gargoyle|2|1|Flying$When Biblioplex Assistant enters the battlefield, put up to one target instant or sorcery card from your graveyard on top of your library.| +Campus Guide|Strixhaven: School of Mages|252|C|{2}|Artifact Creature - Golem|2|1|When Campus Guide enters the battlefield, you may search your library for a basic land card, reveal it, then shuffle and put that card on top.| +Codie, Vociferous Codex|Strixhaven: School of Mages|253|R|{3}|Legendary Artifact Creature - Construct|1|4|You can't cast permanent spells.${4}, {T}: Add {W}{U}{B}{R}{G}. When you cast your next spell this turn, exile cards from the top of your library until you exile an instant or sorcery card with lesser mana value. Until end of turn, you may cast that card without paying its mana cost. Put each other card exiled this way on the bottom of your library in a random order.| +Cogwork Archivist|Strixhaven: School of Mages|254|C|{6}|Artifact Creature - Construct|4|5|Reach${2}, {T}: Put target card from a graveyard on the bottom of its owner's library.| +Excavated Wall|Strixhaven: School of Mages|255|C|{1}|Artifact Creature - Wall|0|4|Defender${1}, {T}: Mill a card.| +Letter of Acceptance|Strixhaven: School of Mages|256|C|{3}|Artifact|||{T}: Add one mana of any color.${2}, {T}, Sacrifice Letter of Acceptance: Draw a card.| +Reflective Golem|Strixhaven: School of Mages|257|U|{3}|Artifact Creature - Golem|2|3|Whenever you cast an instant or sorcery spell that targets only Reflective Golem, you may pay {2}. If you do, copy that spell. You may choose new targets for the copy.| +Spell Satchel|Strixhaven: School of Mages|258|U|{2}|Artifact|||Magecraft — Whenever you cast or copy an instant or sorcery spell, put a book counter on Spell Satchel.${T}, Remove a book counter from Spell Satchel: Add {C}.${3}, {T}, Remove three book counters from Spell Satchel: Draw a card.| +Strixhaven Stadium|Strixhaven: School of Mages|259|R|{3}|Artifact|||{T}: Add {C}. Put a point counter on Strixhaven Stadium.$Whenever a creature deals combat damage to you, remove a point counter from Strixhaven Stadium.$Whenever a creature you control deals combat damage to an opponent, put a point counter on Strixhaven Stadium. Then if it has ten or more point counters on it, remove them all and that player loses the game.| +Team Pennant|Strixhaven: School of Mages|260|U|{1}|Artifact - Equipment|||Equipped creature gets +1/+1 and has vigilance and trample.$Equip creature token {1}$Equip {3}| +Zephyr Boots|Strixhaven: School of Mages|261|U|{1}|Artifact - Equipment|||Equipped creature has flying.$Whenever equipped creature deals combat damage to a player, draw a card, then discard a card.$Equip {2}| +Access Tunnel|Strixhaven: School of Mages|262|U||Land|||{T}: Add {C}.${3}, {T}: Target creature with power 3 or less can't be blocked this turn.| +Archway Commons|Strixhaven: School of Mages|263|C||Land|||Archway Commons enters the battlefield tapped.$When Archway Commons enters the battlefield, sacrifice it unless you pay {1}.${T}: Add one mana of any color.| +The Biblioplex|Strixhaven: School of Mages|264|R||Land|||{T}: Add {C}.${2}, {T}: Look at the top card of your library. If it's an instant or sorcery card, you may reveal it and put it into your hand. If you don't put the card into your hand, you may put it into your graveyard. Activate only if you have exactly zero or seven cards in hand.| +Frostboil Snarl|Strixhaven: School of Mages|265|R||Land|||As Frostboil Snarl enters the battlefield, you may reveal an Island or Mountain card from your hand. If you don't, Frostboil Snarl enters the battlefield tapped.${T}: Add {U} or {R}.| +Furycalm Snarl|Strixhaven: School of Mages|266|R||Land|||As Furycalm Snarl enters the battlefield, you may reveal a Mountain or Plains card from your hand. If you don't, Furycalm Snarl enters the battlefield tapped.${T}: Add {R} or {W}.| +Hall of Oracles|Strixhaven: School of Mages|267|R||Land|||{T}: Add {C}.${1}, {T}: Add one mana of any color.${T}: Put a +1/+1 counter on target creature. Activate only as a sorcery and only if you've cast an instant or sorcery spell this turn.| +Lorehold Campus|Strixhaven: School of Mages|268|C||Land|||Lorehold Campus enters the battlefield tapped.${T}: Add {R} or {W}.${4}, {T}: Scry 1.| +Necroblossom Snarl|Strixhaven: School of Mages|269|R||Land|||As Necroblossom Snarl enters the battlefield, you may reveal a Swamp or Forest card from your hand. If you don't, Necroblossom Snarl enters the battlefield tapped.${T}: Add {B} or {G}.| +Prismari Campus|Strixhaven: School of Mages|270|C||Land|||Prismari Campus enters the battlefield tapped.${T}: Add {U} or {R}.${4}, {T}: Scry 1.| +Quandrix Campus|Strixhaven: School of Mages|271|C||Land|||Quandrix Campus enters the battlefield tapped.${T}: Add {G} or {U}.${4}, {T}: Scry 1.| +Shineshadow Snarl|Strixhaven: School of Mages|272|R||Land|||As Shineshadow Snarl enters the battlefield, you may reveal a Plains or Swamp card from your hand. If you don't, Shineshadow Snarl enters the battlefield tapped.${T}: Add {W} or {B}.| +Silverquill Campus|Strixhaven: School of Mages|273|C||Land|||Silverquill Campus enters the battlefield tapped.${T}: Add {W} or {B}.${4}, {T}: Scry 1.| +Vineglimmer Snarl|Strixhaven: School of Mages|274|R||Land|||As Vineglimmer Snarl enters the battlefield, you may reveal a Forest or Island card from your hand. If you don't, Vineglimmer Snarl enters the battlefield tapped.${T}: Add {G} or {U}.| +Witherbloom Campus|Strixhaven: School of Mages|275|C||Land|||Witherbloom Campus enters the battlefield tapped.${T}: Add {B} or {G}.${4}, {T}: Scry 1.| +Professor Onyx|Strixhaven: School of Mages|276|M|{4}{B}{B}|Legendary Planeswalker - Liliana|5|Magecraft — Whenever you cast or copy an instant or sorcery spell, each opponent loses 2 life and you gain 2 life.$+1: You lose 1 life. Look at the top three cards of your library. Put one of them into your hand and the rest into your graveyard.$−3: Each opponent sacrifices a creature with the greatest power among creatures that player controls.$−8: Each opponent may discard a card. If they don't, they lose 3 life. Repeat this process six more times.| +Lukka, Wayward Bonder|Strixhaven: School of Mages|277|M|{4}{R}{R}|Legendary Planeswalker - Lukka|5|+1: You may discard a card. If you do, draw a card. If a creature card was discarded this way, draw two cards instead.$−2: Return target creature card from your graveyard to the battlefield. It gains haste. Exile it at the beginning of your next upkeep.$−7: You get an emblem with "Whenever a creature enters the battlefield under your control, it deals damage equal to its power to any target."| +Mila, Crafty Companion|Strixhaven: School of Mages|277|M|{1}{W}{W}|Legendary Creature - Fox|2|3|Whenever an opponent attacks one or more planeswalkers you control, put a loyalty counter on each planeswalker you control.$Whenever a permanent you control becomes the target of a spell or ability an opponent controls, you may draw a card.| +Rowan, Scholar of Sparks|Strixhaven: School of Mages|278|M|{2}{R}|Legendary Planeswalker - Rowan|2|Instant and sorcery spells you cast cost {1} less to cast.$+1: Rowan, Scholar of Sparks deals 1 damage to each opponent. If you've drawn three or more cards this turn, she deals 3 damage to each opponent instead.$−4: You get an emblem with "Whenever you cast an instant or sorcery spell, you may pay {2}. If you do, copy that spell. You may choose new targets for the copy."| +Will, Scholar of Frost|Strixhaven: School of Mages|278|M|{4}{U}|Legendary Planeswalker - Will|4|Instant and sorcery spells you cast cost {1} less to cast.$+1: Up to one target creature has base power and toughness 0/2 until your next turn.$−3: Draw two cards.$−7: Exile up to five target permanents. For each permanent exiled this way, its controller creates a 4/4 blue and red Elemental creature token.| +Kasmina, Enigma Sage|Strixhaven: School of Mages|279|M|{1}{G}{U}|Legendary Planeswalker - Kasmina|2|Each other planeswalker you control has the loyalty abilities of Kasmina, Enigma Sage.$+2: Scry 1.$−X: Create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it.$−8: Search your library for an instant or sorcery card that shares a color with this planeswalker, exile that card, then shuffle. You may cast that card without paying its mana cost.| +Shadrix Silverquill|Strixhaven: School of Mages|280|M|{3}{W}{B}|Legendary Creature - Elder Dragon|2|5|Flying, double strike$At the beginning of combat on your turn, you may choose two. Each mode must target a different player.$• Target player creates a 2/1 white and black Inkling creature token with flying.$• Target player draws a card and loses 1 life.$• Target player puts a +1/+1 counter on each creature they control.| +Galazeth Prismari|Strixhaven: School of Mages|281|M|{2}{U}{R}|Legendary Creature - Elder Dragon|3|4|Flying$When Galazeth Prismari enters the battlefield, create a Treasure token.$Artifacts you control have "{T}: Add one mana of any color. Spend this mana only to cast an instant or sorcery spell."| +Beledros Witherbloom|Strixhaven: School of Mages|282|M|{5}{B}{G}|Legendary Creature - Elder Dragon|4|4|Flying$At the beginning of each upkeep, create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life."$Pay 10 life: Untap all lands you control. Activate only once each turn.| +Velomachus Lorehold|Strixhaven: School of Mages|283|M|{5}{R}{W}|Legendary Creature - Elder Dragon|5|5|Flying, vigilance, haste$Whenever Velomachus Lorehold attacks, look at the top seven cards of your library. You may cast an instant or sorcery spell with mana value less than or equal to Velomachus Lorehold's power from among them without paying its mana cost. Put the rest on the bottom of your library in a random order.| +Tanazir Quandrix|Strixhaven: School of Mages|284|M|{3}{G}{U}|Legendary Creature - Elder Dragon|4|4|Flying, trample$When Tanazir Quandrix enters the battlefield, double the number of +1/+1 counters on target creature you control.$Whenever Tanazir Quandrix attacks, you may have the base power and toughness of other creatures you control become equal to Tanazir Quandrix's power and toughness until end of turn.| +Mascot Exhibition|Strixhaven: School of Mages|285|M|{7}|Sorcery - Lesson|||Create a 2/1 white and black Inkling creature token with flying, a 3/2 red and white Spirit creature token, and a 4/4 blue and red Elemental creature token.| +Explore the Vastlands|Strixhaven: School of Mages|286|R|{3}|Sorcery|||Each player looks at the top five cards of their library and may reveal a land card and/or an instant or sorcery card from among them. Each player puts the cards they revealed this way into their hand and the rest on the bottom of their library in a random order. Each player gains 3 life.| +Wandering Archaic|Strixhaven: School of Mages|286|R|{5}|Creature - Avatar|4|4|Whenever an opponent casts an instant or sorcery spell, they may pay {2}. If they don't, you may copy that spell. You may choose new targets for the copy.| +Academic Probation|Strixhaven: School of Mages|287|R|{1}{W}|Sorcery - Lesson|||Choose one —$• Choose a nonland card name. Opponents can't cast spells with the chosen name until your next turn.$• Choose target nonland permanent. Until your next turn, it can't attack or block, and its activated abilities can't be activated.| +Devastating Mastery|Strixhaven: School of Mages|288|R|{2}{W}{W}{W}{W}|Sorcery|||You may pay {2}{W}{W} rather than pay this spell's mana cost.$If the {2}{W}{W} cost was paid, an opponent chooses up to two nonland permanents they control and returns them to their owner's hand.$Destroy all nonland permanents.| +Elite Spellbinder|Strixhaven: School of Mages|289|R|{2}{W}|Creature - Human Cleric|3|1|Flying$When Elite Spellbinder enters the battlefield, look at target opponent's hand. You may exile a nonland card from it. For as long as that card remains exiled, its owner may play it. A spell cast this way costs {2} more to cast.| +Leonin Lightscribe|Strixhaven: School of Mages|290|R|{1}{W}|Creature - Cat Cleric|2|2|Magecraft — Whenever you cast or copy an instant or sorcery spell, creatures you control get +1/+1 until end of turn.| +Mavinda, Students' Advocate|Strixhaven: School of Mages|291|M|{2}{W}|Legendary Creature - Bird Advisor|2|3|Flying${0}: You may cast target instant or sorcery card from your graveyard this turn. If that spell doesn't target a creature you control, it costs {8} more to cast this way. If that spell would be put into your graveyard, exile it instead. Activate only once each turn.| +Semester's End|Strixhaven: School of Mages|292|R|{3}{W}|Instant|||Exile any number of target creatures and/or planeswalkers you control. At the beginning of the next end step, return each of them to the battlefield under its owner's control. Each of them enters the battlefield with an additional +1/+1 counter on it if it's a creature and an additional loyalty counter on it if it's a planeswalker.| +Sparring Regimen|Strixhaven: School of Mages|293|R|{2}{W}|Enchantment|||When Sparring Regimen enters the battlefield, learn.$Whenever you attack, put a +1/+1 counter on target attacking creature and untap it.| +Strict Proctor|Strixhaven: School of Mages|294|R|{1}{W}|Creature - Spirit Cleric|1|3|Flying$Whenever a permanent entering the battlefield causes a triggered ability to trigger, counter that ability unless its controller pays {2}.| +Archmage Emeritus|Strixhaven: School of Mages|295|R|{2}{U}{U}|Creature - Human Wizard|2|2|Magecraft — Whenever you cast or copy an instant or sorcery spell, draw a card.| +Dream Strix|Strixhaven: School of Mages|296|R|{2}{U}|Creature - Bird Illusion|3|2|Flying$When Dream Strix becomes the target of a spell, sacrifice it.$When Dream Strix dies, learn.| +Ingenious Mastery|Strixhaven: School of Mages|297|R|{X}{2}{U}|Sorcery|||You may pay {2}{U} rather than pay this spell's mana cost.$If the {2}{U} cost was paid, you draw three cards, then an opponent creates two Treasure tokens and they scry 2. If that cost wasn't paid, you draw X cards.| +Multiple Choice|Strixhaven: School of Mages|298|R|{X}{U}|Sorcery|||If X is 1, scry 1, then draw a card.$If X is 2, you may choose a player. They return a creature they control to its owner's hand.$If X is 3, create a 4/4 blue and red Elemental creature token.$If X is 4 or more, do all of the above.| +Teachings of the Archaics|Strixhaven: School of Mages|299|R|{2}{U}|Sorcery - Lesson|||If an opponent has more cards in hand than you, draw two cards. Draw three cards instead if an opponent has at least four more cards in hand than you.| +Tempted by the Oriq|Strixhaven: School of Mages|300|R|{1}{U}{U}{U}|Sorcery|||For each opponent, gain control of up to one target creature or planeswalker that player controls with mana value 3 or less.| +Baleful Mastery|Strixhaven: School of Mages|301|R|{3}{B}|Instant|||You may pay {1}{B} rather than pay this spell's mana cost.$If the {1}{B} cost was paid, an opponent draws a card.$Exile target creature or planeswalker.| +Callous Bloodmage|Strixhaven: School of Mages|302|R|{2}{B}|Creature - Vampire Warlock|2|1|When Callous Bloodmage enters the battlefield, choose one —$• Create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life."$• You draw a card and you lose 1 life.$• Exile target player's graveyard.| +Confront the Past|Strixhaven: School of Mages|303|R|{X}{B}|Sorcery - Lesson|||Choose one —$• Return target planeswalker card with mana value X or less from your graveyard to the battlefield.$• Remove twice X loyalty counters from target planeswalker an opponent controls.| +Oriq Loremage|Strixhaven: School of Mages|304|R|{2}{B}{B}|Creature - Human Warlock|3|3|{T}: Search your library for a card, put it into your graveyard, then shuffle. If it's an instant or sorcery card, put a +1/+1 counter on Oriq Loremage.| +Poet's Quill|Strixhaven: School of Mages|305|R|{1}{B}|Artifact - Equipment|||When Poet's Quill enters the battlefield, learn.$Equipped creature gets +1/+1 and has lifelink.$Equip {1}{B}| +Sedgemoor Witch|Strixhaven: School of Mages|306|R|{2}{B}|Creature - Human Warlock|3|2|Menace$Ward—Pay 3 life.$Magecraft — Whenever you cast or copy an instant or sorcery spell, create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life."| +Conspiracy Theorist|Strixhaven: School of Mages|307|R|{1}{R}|Creature - Human Shaman|2|2|Whenever Conspiracy Theorist attacks, you may pay {1} and discard a card. If you do, draw a card.$Whenever you discard one or more nonland cards, you may exile one of them from your graveyard. If you do, you may cast it this turn.| +Crackle with Power|Strixhaven: School of Mages|308|M|{X}{X}{X}{R}{R}|Sorcery|||Crackle with Power deals five times X damage to each of up to X targets.| +Draconic Intervention|Strixhaven: School of Mages|309|R|{2}{R}{R}|Sorcery|||As an additional cost to cast this spell, exile an instant or sorcery card from your graveyard.$Draconic Intervention deals X damage to each non-Dragon creature, where X is the exiled card's mana value. If a creature dealt damage this way would die this turn, exile it instead.$Exile Draconic Intervention.| +Efreet Flamepainter|Strixhaven: School of Mages|310|R|{3}{R}|Creature - Efreet Shaman|1|4|Double strike$Whenever Efreet Flamepainter deals combat damage to a player, you may cast target instant or sorcery card from your graveyard without paying its mana cost. If that spell would be put into your graveyard, exile it instead.| +Fervent Mastery|Strixhaven: School of Mages|311|R|{3}{R}{R}|Sorcery|||You may pay {2}{R}{R} rather than pay this spell's mana cost.$If the {2}{R}{R} cost was paid, an opponent discards any number of cards, then draws that many cards.$Search your library for up to three cards, put them into your hand, shuffle, then discard three cards at random.| +Illuminate History|Strixhaven: School of Mages|312|R|{2}{R}{R}|Sorcery - Lesson|||Discard any number of cards, then draw that many cards. Then if there are seven or more cards in your graveyard, create a 3/2 red and white Spirit creature token.| +Retriever Phoenix|Strixhaven: School of Mages|313|R|{3}{R}|Creature - Phoenix|2|2|Flying, haste$When Retriever Phoenix enters the battlefield, if you cast it, learn.$As long as Retriever Phoenix is in your graveyard, if you would learn, you may instead return Retriever Phoenix to the battlefield.| +Accomplished Alchemist|Strixhaven: School of Mages|314|R|{3}{G}|Creature - Elf Druid|2|5|{T}: Add one mana of any color.${T}: Add X mana of any one color, where X is the amount of life you gained this turn.| +Basic Conjuration|Strixhaven: School of Mages|315|R|{1}{G}{G}|Sorcery - Lesson|||Look at the top six cards of your library. You may reveal a creature card from among them and put it into your hand. Put the rest on the bottom of your library in a random order. You gain 3 life.| +Dragonsguard Elite|Strixhaven: School of Mages|316|R|{1}{G}|Creature - Human Druid|2|2|Magecraft — Whenever you cast or copy an instant or sorcery spell, put a +1/+1 counter on Dragonsguard Elite.${4}{G}{G}: Double the number of +1/+1 counters on Dragonsguard Elite.| +Ecological Appreciation|Strixhaven: School of Mages|317|M|{X}{2}{G}|Sorcery|||Search your library and graveyard for up to four creature cards with different names that each have mana value X or less and reveal them. An opponent chooses two of those cards. Shuffle the chosen cards into your library and put the rest onto the battlefield. Exile Ecological Appreciation.| +Exponential Growth|Strixhaven: School of Mages|318|R|{X}{X}{G}{G}|Sorcery|||Until end of turn, double target creature's power X times.| +Gnarled Professor|Strixhaven: School of Mages|319|R|{2}{G}{G}|Creature - Treefolk Druid|5|4|Trample$When Gnarled Professor enters the battlefield, learn.| +Verdant Mastery|Strixhaven: School of Mages|320|R|{5}{G}|Sorcery|||You may pay {3}{G} rather than pay this spell's mana cost.$Search your library for up to four basic land cards and reveal them. Put one of them onto the battlefield tapped under an opponent's control if the {3}{G} cost was paid. Put two of them onto the battlefield tapped under your control and the rest into your hand. Then shuffle.| +Augmenter Pugilist|Strixhaven: School of Mages|321|R|{1}{G}{G}|Creature - Troll Druid|3|3|Trample$As long as you control eight or more lands, Augmenter Pugilist gets +5/+5.| +Echoing Equation|Strixhaven: School of Mages|321|R|{3}{U}{U}|Sorcery|||Choose target creature you control. Each other creature you control becomes a copy of it until end of turn, except those creatures aren't legendary if the chosen creature is legendary.| +Blex, Vexing Pest|Strixhaven: School of Mages|322|M|{2}{G}|Legendary Creature - Pest|3|2|Other Pests, Bats, Insects, Snakes, and Spiders you control get +1/+1.$When Blex, Vexing Pest dies, you gain 4 life.| +Search for Blex|Strixhaven: School of Mages|322|M|{2}{B}{B}|Sorcery|||Look at the top five cards of your library. You may put any number of them into your hand and the rest into your graveyard. You lose 3 life for each card you put into your hand this way.| +Awaken the Blood Avatar|Strixhaven: School of Mages|323|M|{6}{B}{R}|Sorcery|||As an additional cost to cast this spell, you may sacrifice any number of creatures. This spell costs {2} less to cast for each creature sacrificed this way.$Each opponent sacrifices a creature. Create a 3/6 black and red Avatar creature token with haste and "Whenever this creature attacks, it deals 3 damage to each opponent."| +Extus, Oriq Overlord|Strixhaven: School of Mages|323|M|{1}{W}{B}{B}|Legendary Creature - Human Warlock|2|4|Double strike$Magecraft — Whenever you cast or copy an instant or sorcery spell, return target nonlegendary creature card from your graveyard to your hand.| +Flamescroll Celebrant|Strixhaven: School of Mages|324|R|{1}{R}|Creature - Human Shaman|2|1|Whenever an opponent activates an ability that isn't a mana ability, Flamescroll Celebrant deals 1 damage to that player.${1}{R}: Flamescroll Celebrant gets +2/+0 until end of turn.| +Revel in Silence|Strixhaven: School of Mages|324|R|{W}{W}|Instant|||Your opponents can't cast spells or activate planeswalkers' loyalty abilities this turn.$Exile Revel in Silence.| +Jadzi, Oracle of Arcavios|Strixhaven: School of Mages|325|M|{6}{U}{U}|Legendary Creature - Human Wizard|5|5|Discard a card: Return Jadzi, Oracle of Arcavios to its owner's hand.$Magecraft — Whenever you cast or copy an instant or sorcery spell, reveal the top card of your library. If it's a nonland card, you may cast it by paying {1} rather than paying its mana cost. If it's a land card, put it onto the battlefield.| +Journey to the Oracle|Strixhaven: School of Mages|325|M|{2}{G}{G}|Sorcery|||You may put any number of land cards from your hand onto the battlefield. Then if you control eight or more lands, you may discard a card. If you do, return Journey to the Oracle to its owner's hand.| +Imbraham, Dean of Theory|Strixhaven: School of Mages|326|R|{2}{U}{U}|Legendary Creature - Bird Wizard|3|3|Flying${X}{U}{U}, {T}: Exile the top X cards of your library and put a study counter on each of them. Then you may put a card you own in exile with a study counter on it into your hand.| +Kianne, Dean of Substance|Strixhaven: School of Mages|326|R|{2}{G}|Legendary Creature - Elf Druid|2|2|{T}: Exile the top card of your library. If it's a land card, put it into your hand. Otherwise, put a study counter on it.${4}{G}: Create a 0/0 green and blue Fractal creature token. Put a +1/+1 counter on it for each different mana value among nonland cards you own in exile with study counters on them.| +Pestilent Cauldron|Strixhaven: School of Mages|327|R|{2}{B}|Artifact|||{T}, Discard a card: Create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life."${1}, {T}: Each opponent mills cards equal to the amount of life you gained this turn.${4}, {T}: Exile four target cards from a single graveyard. Draw a card.| +Restorative Burst|Strixhaven: School of Mages|327|R|{3}{G}{G}|Sorcery|||Return up to two target creature, land, and/or planeswalker cards from your graveyard to your hand. Each player gains 4 life. Exile Restorative Burst.| +Augusta, Dean of Order|Strixhaven: School of Mages|328|R|{2}{W}|Legendary Creature - Human Cleric|1|3|Other tapped creatures you control get +1/+0.$Other untapped creatures you control get +0/+1.$Whenever you attack, untap each creature you control, then tap any number of creatures you control.| +Plargg, Dean of Chaos|Strixhaven: School of Mages|328|R|{1}{R}|Legendary Creature - Orc Shaman|2|2|{T}, Discard a card: Draw a card.${4}{R}, {T}: Reveal cards from the top of your library until you reveal a nonlegendary, nonland card with mana value 3 or less. You may cast that card without paying its mana cost. Put all revealed cards not cast this way on the bottom of your library in a random order.| +Deadly Vanity|Strixhaven: School of Mages|329|R|{5}{B}{B}{B}|Sorcery|||Choose a creature or planeswalker, then destroy all other creatures and planeswalkers.| +Selfless Glyphweaver|Strixhaven: School of Mages|329|R|{2}{W}|Creature - Human Cleric|2|3|Exile Selfless Glyphweaver: Creatures you control gain indestructible until end of turn.| +Embrose, Dean of Shadow|Strixhaven: School of Mages|330|R|{2}{B}{B}|Legendary Creature - Human Warlock|4|4|{T}: Put a +1/+1 counter on another target creature, then Embrose, Dean of Shadow deals 2 damage to that creature.$Whenever a creature you control with a +1/+1 counter on it dies, draw a card.| +Shaile, Dean of Radiance|Strixhaven: School of Mages|330|R|{1}{W}|Legendary Creature - Bird Cleric|1|1|Flying, vigilance${T}: Put a +1/+1 counter on each creature that entered the battlefield under your control this turn.| +Flamethrower Sonata|Strixhaven: School of Mages|331|R|{1}{R}|Sorcery|||Discard a card, then draw a card. When you discard an instant or sorcery card this way, Flamethrower Sonata deals damage equal to that card's mana value to target creature or planeswalker you don't control.| +Torrent Sculptor|Strixhaven: School of Mages|331|R|{2}{U}{U}|Creature - Merfolk Wizard|2|2|Ward {2}$When Torrent Sculptor enters the battlefield, exile an instant or sorcery card from your graveyard. Put a number of +1/+1 counters on Torrent Sculptor equal to half that card's mana value, rounded up.| +Nassari, Dean of Expression|Strixhaven: School of Mages|332|R|{3}{R}{R}|Legendary Creature - Efreet Shaman|4|4|At the beginning of your upkeep, exile the top card of each opponent's library. Until end of turn, you may cast spells from among those exiled cards, and you may spend mana as though it were mana of any color to cast those spells.$Whenever you cast a spell from exile, put a +1/+1 counter on Nassari, Dean of Expression.| +Uvilda, Dean of Perfection|Strixhaven: School of Mages|332|R|{2}{U}|Legendary Creature - Djinn Wizard|2|2|{T}: You may exile an instant or sorcery card from your hand and put three hone counters on it. It gains "At the beginning of your upkeep, if this card is exiled, remove a hone counter from it" and "When the last hone counter is removed from this card, if it's exiled, you may cast it. It costs {4} less to cast this way."| +Lisette, Dean of the Root|Strixhaven: School of Mages|333|R|{2}{G}{G}|Legendary Creature - Human Druid|4|4|Whenever you gain life, you may pay {1}. If you do, put a +1/+1 counter on each creature you control and those creatures gain trample until end of turn.| +Valentin, Dean of the Vein|Strixhaven: School of Mages|333|R|{B}|Legendary Creature - Vampire Warlock|1|1|Menace, lifelink$If a nontoken creature an opponent controls would die, exile it instead. When you do, you may pay {2}. If you do, create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life."| +Blade Historian|Strixhaven: School of Mages|334|R|{R/W}{R/W}{R/W}{R/W}|Creature - Human Cleric|2|3|Attacking creatures you control have double strike.| +Blot Out the Sky|Strixhaven: School of Mages|335|M|{X}{W}{B}|Sorcery|||Create X tapped 2/1 white and black Inkling creature tokens with flying. If X is 6 or more, destroy all noncreature, nonland permanents.| +Body of Research|Strixhaven: School of Mages|336|M|{G}{G}{G}{U}{U}{U}|Sorcery|||Create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it, where X is the number of cards in your library.| +Culling Ritual|Strixhaven: School of Mages|337|R|{2}{B}{G}|Sorcery|||Destroy each nonland permanent with mana value 2 or less. Add {B} or {G} for each permanent destroyed this way.| +Culmination of Studies|Strixhaven: School of Mages|338|R|{X}{U}{R}|Sorcery|||Exile the top X cards of your library. For each land card exiled this way, create a Treasure token. For each blue card exiled this way, draw a card. For each red card exiled this way, Culmination of Studies deals 1 damage to each opponent.| +Daemogoth Titan|Strixhaven: School of Mages|339|R|{B/G}{B/G}{B/G}{B/G}|Creature - Demon|11|10|Whenever Daemogoth Titan attacks or blocks, sacrifice a creature.| +Double Major|Strixhaven: School of Mages|340|R|{G}{U}|Instant|||Copy target creature spell you control, except it isn't legendary if the spell is legendary.| +Dramatic Finale|Strixhaven: School of Mages|341|R|{W/B}{W/B}{W/B}{W/B}|Enchantment|||Creature tokens you control get +1/+1.$Whenever one or more nontoken creatures you control die, create a 2/1 white and black Inkling creature token with flying. This ability triggers only once each turn.| +Elemental Expressionist|Strixhaven: School of Mages|342|R|{U/R}{U/R}{U/R}{U/R}|Creature - Orc Wizard|4|4|Magecraft — Whenever you cast or copy an instant or sorcery spell, choose target creature you control. Until end of turn, it gains "If this creature would leave the battlefield, exile it instead of putting it anywhere else" and "When this creature is put into exile, create a 4/4 blue and red Elemental creature token."| +Harness Infinity|Strixhaven: School of Mages|343|M|{1}{B}{B}{B}{G}{G}{G}|Instant|||Exchange your hand and graveyard.$Exile Harness Infinity.| +Hofri Ghostforge|Strixhaven: School of Mages|344|M|{3}{R}{W}|Legendary Creature - Dwarf Cleric|4|5|Spirits you control get +1/+1 and have trample and haste.$Whenever another nontoken creature you control dies, exile it. If you do, create a token that's a copy of that creature, except it's a Spirit in addition to its other types and it has "When this creature leaves the battlefield, return the exiled card to your graveyard."| +Lorehold Command|Strixhaven: School of Mages|345|R|{3}{R}{W}|Instant|||Choose two —$• Create a 3/2 red and white Spirit creature token.$• Creatures you control get +1/+0 and gain indestructible and haste until end of turn.$• Lorehold Command deals 3 damage to any target. Target player gains 3 life.$• Sacrifice a permanent, then draw two cards.| +Magma Opus|Strixhaven: School of Mages|346|M|{6}{U}{R}|Instant|||Magma Opus deals 4 damage divided as you choose among any number of targets. Tap two target permanents. Create a 4/4 blue and red Elemental creature token. Draw two cards.${U/R}{U/R}, Discard Magma Opus: Create a Treasure token.| +Manifestation Sage|Strixhaven: School of Mages|347|R|{G/U}{G/U}{G/U}{G/U}|Creature - Human Wizard|2|2|When Manifestation Sage enters the battlefield, create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it, where X is the number of cards in your hand.| +Prismari Command|Strixhaven: School of Mages|348|R|{1}{U}{R}|Instant|||Choose two —$• Prismari Command deals 2 damage to any target.$• Target player draws two cards, then discards two cards.$• Target player creates a Treasure token.$• Destroy target artifact.| +Quandrix Command|Strixhaven: School of Mages|349|R|{1}{G}{U}|Instant|||Choose two —$• Return target creature or planeswalker to its owner's hand.$• Counter target artifact or enchantment spell.$• Put two +1/+1 counters on target creature.$• Target player shuffles up to three target cards from their graveyard into their library.| +Radiant Scrollwielder|Strixhaven: School of Mages|350|R|{2}{R}{W}|Creature - Dwarf Cleric|2|4|Instant and sorcery spells you control have lifelink.$At the beginning of your upkeep, exile an instant or sorcery card at random from your graveyard. You may cast it this turn. If a spell cast this way would be put into your graveyard, exile it instead.| +Rushed Rebirth|Strixhaven: School of Mages|351|R|{B}{G}|Instant|||Choose target creature. When that creature dies this turn, search your library for a creature card with lesser mana value, put it onto the battlefield tapped, then shuffle.| +Silverquill Command|Strixhaven: School of Mages|352|R|{2}{W}{B}|Sorcery|||Choose two —$• Target creature gets +3/+3 and gains flying until end of turn.$• Return target creature card with mana value 2 or less from your graveyard to the battlefield.$• Target player draws a card and loses 1 life.$• Target opponent sacrifices a creature.| +Silverquill Silencer|Strixhaven: School of Mages|353|R|{W}{B}|Creature - Human Cleric|3|2|As Silverquill Silencer enters the battlefield, choose a nonland card name.$Whenever an opponent casts a spell with the chosen name, they lose 3 life and you draw a card.| +Vanishing Verse|Strixhaven: School of Mages|354|R|{W}{B}|Instant|||Exile target monocolored permanent.| +Venerable Warsinger|Strixhaven: School of Mages|355|R|{1}{R}{W}|Creature - Spirit Cleric|3|3|Vigilance, trample$Whenever Venerable Warsinger deals combat damage to a player, you may return target creature card with mana value X or less from your graveyard to the battlefield, where X is the amount of damage Venerable Warsinger dealt to that player.| +Witherbloom Command|Strixhaven: School of Mages|356|R|{B}{G}|Sorcery|||Choose two —$• Target player mills three cards, then you return a land card from your graveyard to your hand.$• Destroy target noncreature, nonland permanent with mana value 2 or less.$• Target creature gets -3/-1 until end of turn.$• Target opponent loses 2 life and you gain 2 life.| +Codie, Vociferous Codex|Strixhaven: School of Mages|357|R|{3}|Legendary Artifact Creature - Construct|1|4|You can't cast permanent spells.${4}, {T}: Add {W}{U}{B}{R}{G}. When you cast your next spell this turn, exile cards from the top of your library until you exile an instant or sorcery card with lesser mana value. Until end of turn, you may cast that card without paying its mana cost. Put each other card exiled this way on the bottom of your library in a random order.| +Strixhaven Stadium|Strixhaven: School of Mages|358|R|{3}|Artifact|||{T}: Add {C}. Put a point counter on Strixhaven Stadium.$Whenever a creature deals combat damage to you, remove a point counter from Strixhaven Stadium.$Whenever a creature you control deals combat damage to an opponent, put a point counter on Strixhaven Stadium. Then if it has ten or more point counters on it, remove them all and that player loses the game.| +The Biblioplex|Strixhaven: School of Mages|359|R||Land|||{T}: Add {C}.${2}, {T}: Look at the top card of your library. If it's an instant or sorcery card, you may reveal it and put it into your hand. If you don't put the card into your hand, you may put it into your graveyard. Activate only if you have exactly zero or seven cards in hand.| +Frostboil Snarl|Strixhaven: School of Mages|360|R||Land|||As Frostboil Snarl enters the battlefield, you may reveal an Island or Mountain card from your hand. If you don't, Frostboil Snarl enters the battlefield tapped.${T}: Add {U} or {R}.| +Furycalm Snarl|Strixhaven: School of Mages|361|R||Land|||As Furycalm Snarl enters the battlefield, you may reveal a Mountain or Plains card from your hand. If you don't, Furycalm Snarl enters the battlefield tapped.${T}: Add {R} or {W}.| +Hall of Oracles|Strixhaven: School of Mages|362|R||Land|||{T}: Add {C}.${1}, {T}: Add one mana of any color.${T}: Put a +1/+1 counter on target creature. Activate only as a sorcery and only if you've cast an instant or sorcery spell this turn.| +Necroblossom Snarl|Strixhaven: School of Mages|363|R||Land|||As Necroblossom Snarl enters the battlefield, you may reveal a Swamp or Forest card from your hand. If you don't, Necroblossom Snarl enters the battlefield tapped.${T}: Add {B} or {G}.| +Shineshadow Snarl|Strixhaven: School of Mages|364|R||Land|||As Shineshadow Snarl enters the battlefield, you may reveal a Plains or Swamp card from your hand. If you don't, Shineshadow Snarl enters the battlefield tapped.${T}: Add {W} or {B}.| +Vineglimmer Snarl|Strixhaven: School of Mages|365|R||Land|||As Vineglimmer Snarl enters the battlefield, you may reveal a Forest or Island card from your hand. If you don't, Vineglimmer Snarl enters the battlefield tapped.${T}: Add {G} or {U}.| +Plains|Strixhaven: School of Mages|366|C||Basic Land - Plains|||({T}: Add {W}.)| +Plains|Strixhaven: School of Mages|367|C||Basic Land - Plains|||({T}: Add {W}.)| +Island|Strixhaven: School of Mages|368|C||Basic Land - Island|||({T}: Add {U}.)| +Island|Strixhaven: School of Mages|369|C||Basic Land - Island|||({T}: Add {U}.)| +Swamp|Strixhaven: School of Mages|370|C||Basic Land - Swamp|||({T}: Add {B}.)| +Swamp|Strixhaven: School of Mages|371|C||Basic Land - Swamp|||({T}: Add {B}.)| +Mountain|Strixhaven: School of Mages|372|C||Basic Land - Mountain|||({T}: Add {R}.)| +Mountain|Strixhaven: School of Mages|373|C||Basic Land - Mountain|||({T}: Add {R}.)| +Forest|Strixhaven: School of Mages|374|C||Basic Land - Forest|||({T}: Add {G}.)| +Forest|Strixhaven: School of Mages|375|C||Basic Land - Forest|||({T}: Add {G}.)| +Dragonsguard Elite|Strixhaven: School of Mages|376|R|{1}{G}|Creature - Human Druid|2|2|Magecraft — Whenever you cast or copy an instant or sorcery spell, put a +1/+1 counter on Dragonsguard Elite.${4}{G}{G}: Double the number of +1/+1 counters on Dragonsguard Elite.| +Archmage Emeritus|Strixhaven: School of Mages|377|R|{2}{U}{U}|Creature - Human Wizard|2|2|Magecraft — Whenever you cast or copy an instant or sorcery spell, draw a card.| +Fracture|Strixhaven: School of Mages|378|U|{W}{B}|Instant|||Destroy target artifact, enchantment, or planeswalker.| +Expressive Iteration|Strixhaven: School of Mages|379|U|{U}{R}|Sorcery|||Look at the top three cards of your library. Put one of them into your hand, put one of them on the bottom of your library, and exile one of them. You may play the exiled card this turn.| +Mortality Spear|Strixhaven: School of Mages|380|U|{2}{B}{G}|Instant|||This spell costs {2} less to cast if you gained life this turn.$Destroy target nonland permanent.| +Rip Apart|Strixhaven: School of Mages|381|U|{R}{W}|Sorcery|||Choose one —$• Rip Apart deals 3 damage to target creature or planeswalker.$• Destroy target artifact or enchantment.| +Decisive Denial|Strixhaven: School of Mages|382|U|{G}{U}|Instant|||Choose one —$• Target creature you control fights target creature you don't control.$• Counter target noncreature spell unless its controller pays {3}.| +Breena, the Demagogue|Commander 2021|1|M|{1}{W}{B}|Legendary Creature - Bird Warlock|1|3|Flying$Whenever a player attacks one of your opponents, if that opponent has more life than another of your opponents, that attacking player draws a card and you put two +1/+1 counters on a creature you control.| +Felisa, Fang of Silverquill|Commander 2021|2|M|{2}{W}{B}|Legendary Creature - Vampire Wizard|3|2|Flying$Mentor$Whenever a nontoken creature you control dies, if it had counters on it, create X tapped 2/1 white and black Inkling creature tokens with flying, where X is the number of counters it had on it.| +Veyran, Voice of Duality|Commander 2021|3|M|{1}{U}{R}|Legendary Creature - Efreet Wizard|2|2|Magecraft — Whenever you cast or copy an instant or sorcery spell, Veyran, Voice of Duality gets +1/+1 until end of turn.$If you casting or copying an instant or sorcery spell causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.| +Zaffai, Thunder Conductor|Commander 2021|4|M|{2}{U}{R}|Legendary Creature - Human Shaman|1|4|Magecraft — Whenever you cast or copy an instant or sorcery spell, scry 1. If that spell's mana value is 5 or greater, create a 4/4 blue and red Elemental creature token. If that spell's mana value is 10 or greater, Zaffai, Thunder Conductor deals 10 damage to an opponent chosen at random.| +Gyome, Master Chef|Commander 2021|5|M|{2}{B}{G}|Legendary Creature - Troll Warlock|5|3|Trample$At the beginning of your end step, create a number of Food tokens equal to the number of nontoken creatures you had enter the battlefield under your control this turn.${1}, Sacrifice a Food: Target creature gains indestructible until end of turn. Tap it.| +Willowdusk, Essence Seer|Commander 2021|6|M|{1}{B}{G}|Legendary Creature - Dryad Druid|3|3|{1}, {T}: Choose another target creature. Put a number of +1/+1 counters on it equal to the amount of life you gained this turn or the amount of life you lost this turn, whichever is greater. Activate only as a sorcery.| +Alibou, Ancient Witness|Commander 2021|7|M|{3}{R}{W}|Legendary Artifact Creature - Golem|4|5|Other artifact creatures you control have haste.$Whenever one or more artifact creatures you control attack, Alibou, Ancient Witness deals X damage to any target and you scry X, where X is the number of tapped artifacts you control.| +Osgir, the Reconstructor|Commander 2021|8|M|{2}{R}{W}|Legendary Creature - Giant Artificer|4|4|Vigilance${1}, Sacrifice an artifact: Target creature you control gets +2/+0 until end of turn.${X}, {T}, Exile an artifact with mana value X from your graveyard: Create two tokens that are copies of the exiled card. Activate only as a sorcery.| +Adrix and Nev, Twincasters|Commander 2021|9|M|{2}{G}{U}|Legendary Creature - Merfolk Wizard|2|2|Ward {2}$If one or more tokens would be created under your control, twice that many of those tokens are created instead.| +Esix, Fractal Bloom|Commander 2021|10|M|{4}{G}{U}|Legendary Creature - Fractal|4|4|Flying$The first time you would create one or more tokens during each of your turns, you may instead choose a creature other than Esix, Fractal Bloom and create that many tokens that are copies of that creature.| +Angel of the Ruins|Commander 2021|11|R|{5}{W}{W}|Artifact Creature - Angel|5|7|Flying$When Angel of the Ruins enters the battlefield, exile up to two target artifacts and/or enchantments.$Plainscycling {2}| +Archaeomancer's Map|Commander 2021|12|R|{2}{W}|Artifact|||When Archaeomancer's Map enters the battlefield, search your library for up to two basic Plains cards, reveal them, put them into your hand, then shuffle.$Whenever a land enters the battlefield under an opponent's control, if that player controls more lands than you, you may put a land card from your hand onto the battlefield.| +Bronze Guardian|Commander 2021|13|R|{4}{W}|Artifact Creature - Golem|*|5|Double strike$Ward {2}$Other artifacts you control have ward {2}.$Bronze Guardian's power is equal to the number of artifacts you control.| +Combat Calligrapher|Commander 2021|14|R|{3}{W}|Creature - Bird Cleric|3|3|Flying$Inklings can't attack you or planeswalkers you control.$Whenever a player attacks one of your opponents, that attacking player creates a tapped 2/1 white and black Inkling creature token with flying that's attacking that opponent.| +Digsite Engineer|Commander 2021|15|R|{2}{W}|Creature - Dwarf Artificer|3|3|Whenever you cast an artifact spell, you may pay {2}. If you do, create a 0/0 colorless Construct artifact creature token with "This creature gets +1/+1 for each artifact you control."| +Excavation Technique|Commander 2021|16|R|{3}{W}|Sorcery|||Demonstrate$Destroy target nonland permanent. Its controller creates two Treasure tokens.| +Guardian Archon|Commander 2021|17|R|{4}{W}{W}|Creature - Archon|5|5|Flying$As Guardian Archon enters the battlefield, secretly choose an opponent.$Reveal the player you chose: You and target permanent you control each gain protection from the chosen player until end of turn. Activate only once.| +Losheel, Clockwork Scholar|Commander 2021|18|R|{2}{W}|Legendary Creature - Elephant Artificer|2|4|Prevent all combat damage that would be dealt to attacking artifact creatures you control.$Whenever one or more artifact creatures enter the battlefield under your control, draw a card. This ability triggers only once each turn.| +Monologue Tax|Commander 2021|19|R|{2}{W}|Enchantment|||Whenever an opponent casts their second spell each turn, you create a Treasure token.| +Nils, Discipline Enforcer|Commander 2021|20|R|{2}{W}|Legendary Creature - Human Cleric|2|2|At the beginning of your end step, for each player, put a +1/+1 counter on up to one target creature that player controls.$Each creature with one or more counters on it can't attack you or planeswalkers you control unless its controller pays {X}, where X is the number of counters on that creature.| +Promise of Loyalty|Commander 2021|21|R|{4}{W}|Sorcery|||Each player puts a vow counter on a creature they control and sacrifices the rest. Each of those creatures can't attack you or planeswalkers you control for as long as it has a vow counter on it.| +Scholarship Sponsor|Commander 2021|22|R|{3}{W}|Creature - Human Advisor|3|3|When Scholarship Sponsor enters the battlefield, each player who controls fewer lands than the player who controls the most lands searches their library for a number of basic land cards less than or equal to the difference, puts those cards onto the battlefield tapped, then shuffles.| +Commander's Insight|Commander 2021|23|R|{X}{U}{U}{U}|Instant|||Target player draws X cards plus an additional card for each time they've cast a commander from the command zone this game.| +Curiosity Crafter|Commander 2021|24|R|{3}{U}|Creature - Bird Wizard|3|3|Flying$You have no maximum hand size.$Whenever a creature token you control deals combat damage to a player, draw a card.| +Dazzling Sphinx|Commander 2021|25|R|{3}{U}{U}|Creature - Sphinx|4|5|Flying$Whenever Dazzling Sphinx deals combat damage to a player, that player exiles cards from the top of their library until they exile an instant or sorcery card. You may cast that card without paying its mana cost. Then that player puts the exiled cards that weren't cast this way on the bottom of their library in a random order.| +Deekah, Fractal Theorist|Commander 2021|26|R|{4}{U}|Legendary Creature - Human Wizard|3|3|Magecraft — Whenever you cast or copy an instant or sorcery spell, create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it, where X is that spell's mana value.${3}{U}: Target creature token can't be blocked this turn.| +Inspiring Refrain|Commander 2021|27|R|{4}{U}{U}|Sorcery|||Draw two cards. Exile Inspiring Refrain with three time counters on it.$Suspend 3—{2}{U}| +Muse Vortex|Commander 2021|28|R|{X}{U}{U}|Sorcery|||Exile the top X cards of your library. You may cast an instant or sorcery spell with mana value X or less from among them without paying its mana cost. Then put the exiled instant and sorcery cards that weren't cast this way into your hand and the rest on the bottom of your library in a random order.| +Octavia, Living Thesis|Commander 2021|29|R|{8}{U}{U}|Legendary Creature - Elemental Octopus|8|8|This spell costs {8} less to cast if you have eight or more instant and/or sorcery cards in your graveyard.$Ward {8}$Magecraft — Whenever you cast or copy an instant or sorcery spell, target creature has base power and toughness 8/8 until end of turn.| +Perplexing Test|Commander 2021|30|R|{3}{U}{U}|Instant|||Choose one —$• Return all creature tokens to their owners' hands.$• Return all nontoken creatures to their owners' hands.| +Replication Technique|Commander 2021|31|R|{4}{U}|Sorcery|||Demonstrate$Create a token that's a copy of target permanent you control.| +Sly Instigator|Commander 2021|32|R|{3}{U}|Creature - Human Wizard|2|4|{U}, {T}: Until your next turn, target creature an opponent controls can't be blocked. Goad that creature.| +Spawning Kraken|Commander 2021|33|R|{5}{U}|Creature - Kraken|6|6|Whenever a Kraken, Leviathan, Octopus, or Serpent you control deals combat damage to a player, create a 9/9 blue Kraken creature token.| +Theoretical Duplication|Commander 2021|34|R|{2}{U}|Instant|||Whenever a nontoken creature enters the battlefield under an opponent's control this turn, create a token that's a copy of that creature.| +Author of Shadows|Commander 2021|35|R|{4}{B}|Creature - Shade Warlock|3|3|When Author of Shadows enters the battlefield, exile all cards from all opponents' graveyards. Choose a nonland card exiled this way. You may cast that card for as long as it remains exiled, and you may spend mana as though it were mana of any color to cast that spell.| +Blight Mound|Commander 2021|36|R|{2}{B}|Enchantment|||Attacking Pests you control get +1/+0 and have menace.$Whenever a nontoken creature you control dies, create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life."| +Bold Plagiarist|Commander 2021|37|R|{3}{B}|Creature - Vampire Rogue|2|2|Flash$Whenever an opponent puts one or more counters on a creature they control, they put the same number and kind of counters on Bold Plagiarist.| +Cunning Rhetoric|Commander 2021|38|R|{2}{B}|Enchantment|||Whenever an opponent attacks you and/or one or more planeswalkers you control, exile the top card of that player's library. You may play that card for as long as it remains exiled, and you may spend mana as though it were mana of any color to cast it.| +Essence Pulse|Commander 2021|39|R|{3}{B}|Sorcery|||You gain 2 life. Each creature gets -X/-X until end of turn, where X is the amount of life you gained this turn.| +Fain, the Broker|Commander 2021|40|R|{2}{B}|Legendary Creature - Human Warlock|3|3|{T}, Sacrifice a creature: Put two +1/+1 counters on target creature.${T}, Remove a counter from a creature you control: Create a Treasure token.${T}, Sacrifice an artifact: Create a 2/1 white and black Inkling creature token with flying.${3}{B}: Untap Fain, the Broker.| +Incarnation Technique|Commander 2021|41|R|{4}{B}|Sorcery|||Demonstrate$Mill five cards, then return a creature card from your graveyard to the battlefield.| +Keen Duelist|Commander 2021|42|R|{1}{B}|Creature - Human Wizard|2|2|At the beginning of your upkeep, you and target opponent each reveal the top card of your library. You each lose life equal to the mana value of the card revealed by the other player. You each put the card you revealed into your hand.| +Marshland Bloodcaster|Commander 2021|43|R|{4}{B}|Creature - Vampire Warlock|3|5|Flying${1}{B}, {T}: Rather than pay the mana cost of the next spell you cast this turn, you may pay life equal to that spell's mana value.| +Stinging Study|Commander 2021|44|R|{4}{B}|Instant|||You draw X cards and you lose X life, where X is the mana value of a commander you own on the battlefield or in the command zone.| +Tivash, Gloom Summoner|Commander 2021|45|R|{4}{B}|Legendary Creature - Human Warlock|4|4|Lifelink$At the beginning of your end step, if you gained life this turn, you may pay X life, where X is the amount of life you gained this turn. If you do, create an X/X black Demon creature token with flying.| +Veinwitch Coven|Commander 2021|46|R|{2}{B}|Creature - Vampire Warlock|3|3|Menace$Whenever you gain life, you pay {B}. If you do, return target creature card from your graveyard to your hand.| +Audacious Reshapers|Commander 2021|47|R|{2}{R}|Creature - Human Artificer|3|3|{T}, Sacrifice an artifact: Reveal cards from the top of your library until you reveal an artifact card. Put that card onto the battlefield and the rest on the bottom of your library in a random order. Audacious Reshapers deals damage to you equal to the number of cards revealed this way.| +Battlemage's Bracers|Commander 2021|48|R|{2}{R}|Artifact - Equipment|||Equipped creature has haste.$Whenever an ability of equipped creature is activated, if it isn't a mana ability, you may pay {1}. If you do, copy that ability. You may choose new targets for the copy.$Equip {2}| +Creative Technique|Commander 2021|49|R|{4}{R}|Sorcery|||Demonstrate$Shuffle your library, then reveal cards from the top of it until you reveal a nonland card. Exile that card and put the rest on the bottom of your library in a random order. You may cast the exiled card without paying its mana cost.| +Cursed Mirror|Commander 2021|50|R|{2}{R}|Artifact|||{T}: Add {R}.$As Cursed Mirror enters the battlefield, you may have it become a copy of any creature on the battlefield until end of turn, except it has haste.| +Fiery Encore|Commander 2021|51|R|{4}{R}|Sorcery|||Discard a card, then draw a card. When you discard a nonland card this way, Fiery Encore deals damage equal to that card's mana value to target creature or planeswalker.$Storm| +Inferno Project|Commander 2021|52|R|{6}{R}|Creature - Elemental|0|0|Trample$Inferno Project enters the battlefield with X +1/+1 counters on it, where X is the total mana value of instant and sorcery cards in your graveyard.| +Laelia, the Blade Reforged|Commander 2021|53|R|{2}{R}|Legendary Creature - Spirit Warrior|2|2|Haste$Whenever Laelia, the Blade Reforged attacks, exile the top card of your library. You may play that card this turn.$Whenever a spell or ability you control exiles one or more cards from your library and/or your graveyard, put a +1/+1 counter on Laelia.| +Radiant Performer|Commander 2021|54|R|{3}{R}{R}|Creature - Human Wizard|2|2|Flash$When Radiant Performer enters the battlefield, if you cast it from your hand, choose target spell or ability that targets only a single permanent or player. Copy that spell or ability for each other permanent or player the spell or ability could target. Each copy targets a different one of those permanents and players.| +Rionya, Fire Dancer|Commander 2021|55|R|{3}{R}{R}|Legendary Creature - Human Wizard|3|4|At the beginning of combat on your turn, create X tokens that are copies of another target creature you control, where X is one plus the number of instant and sorcery spells you've cast this turn. They gain haste. Exile them at the beginning of the next end step.| +Rousing Refrain|Commander 2021|56|R|{3}{R}{R}|Sorcery|||Add {R} for each card in target opponent's hand. Until end of turn, you don't lose this mana as steps and phases end. Exile Rousing Refrain with three time counters on it.$Suspend 3—{1}{R}| +Ruin Grinder|Commander 2021|57|R|{5}{R}|Artifact Creature - Construct|7|4|Menace$When Ruin Grinder dies, each player may discard their hand and draw seven cards.$Mountaincycling {2}| +Surge to Victory|Commander 2021|58|R|{4}{R}{R}|Sorcery|||Exile target instant or sorcery card from your graveyard. Creatures you control get +X/+0 until end of turn, where X is that card's mana value. Whenever a creature you control deals combat damage to a player this turn, copy the exiled card. You may cast the copy without paying its mana cost.| +Blossoming Bogbeast|Commander 2021|59|R|{4}{G}|Creature - Beast|3|3|Whenever Blossoming Bogbeast attacks, you gain 2 life. Then creatures you control gain trample and get +X/+X until end of turn, where X is the amount of life you gained this turn.| +Ezzaroot Channeler|Commander 2021|60|R|{5}{G}|Creature - Treefolk Druid|4|6|Reach$Creature spells you cast cost {X} less to cast, where X is the amount of life you gained this turn.${T}: You gain 2 life.| +Fractal Harness|Commander 2021|61|R|{X}{2}{G}|Artifact - Equipment|||When Fractal Harness enters the battlefield, create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it and attach Fractal Harness to it.$Whenever equipped creature attacks, double the number of +1/+1 counters on it.$Equip {2}| +Guardian Augmenter|Commander 2021|62|R|{2}{G}|Creature - Troll Wizard|2|2|Flash$Commander creatures you control get +2/+2.$Commanders you control have hexproof.| +Healing Technique|Commander 2021|63|R|{3}{G}|Sorcery|||Demonstrate$Return target card from your graveyard to your hand. You gain life equal to that card's mana value. Exile Healing Technique.| +Paradox Zone|Commander 2021|64|R|{4}{G}|Enchantment|||Paradox Zone enters the battlefield with a growth counter on it.$At the beginning of your end step, double the number of growth counters on Paradox Zone. Then create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it, where X is the number of growth counters on Paradox Zone.| +Pest Infestation|Commander 2021|65|R|{X}{X}{G}|Sorcery|||Destroy up to X target artifacts and/or enchantments. Create twice X 1/1 black and green Pest creature tokens with "When this creature dies, you gain 1 life."| +Ruxa, Patient Professor|Commander 2021|66|R|{2}{G}{G}|Legendary Creature - Bear Druid|4|4|Whenever Ruxa, Patient Professor enters the battlefield or attacks, return target creature card with no abilities from your graveyard to your hand.$Creatures you control with no abilities get +1/+1.$You may have creatures you control with no abilities assign their combat damage as though they weren't blocked.| +Sequence Engine|Commander 2021|67|R|{2}{G}|Artifact|||{X}, {T}: Exile target creature card with mana value X from a graveyard. Create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it. Activate only as a sorcery.| +Sproutback Trudge|Commander 2021|68|R|{7}{G}{G}|Creature - Fungus Beast|9|7|This spell costs {X} less to cast, where X is the amount of life you gained this turn.$Trample$At the beginning of your end step, if you gained life this turn, you may cast Sproutback Trudge from your graveyard.| +Trudge Garden|Commander 2021|69|R|{2}{G}|Enchantment|||Whenever you gain life, you may pay {2}. If you do, create a 4/4 green Fungus Beast creature token with trample.| +Yedora, Grave Gardener|Commander 2021|70|R|{4}{G}|Legendary Creature - Treefolk Druid|5|5|Whenever another nontoken creature you control dies, you may return it to the battlefield face down under its owner's control. It's a Forest land.| +Inkshield|Commander 2021|71|R|{3}{W}{B}|Instant|||Prevent all combat damage that would be dealt to you this turn. For each 1 damage prevented this way, create a 2/1 white and black Inkling creature token with flying.| +Oversimplify|Commander 2021|72|R|{3}{G}{U}|Sorcery|||Exile all creatures. Each player creates a 0/0 green and blue Fractal creature token and puts a number of +1/+1 counters on it equal to the total power of creatures they controlled that were exiled this way.| +Reinterpret|Commander 2021|73|R|{2}{U}{R}|Instant|||Counter target spell. You may cast a spell with an equal or lesser mana value from your hand without paying its mana cost.| +Revival Experiment|Commander 2021|74|R|{4}{B}{G}|Sorcery|||For each permanent type, return up to one card of that type from your graveyard to the battlefield. You lose 3 life for each card returned this way. Exile Revival Experiment.| +Wake the Past|Commander 2021|75|R|{5}{R}{W}|Sorcery|||Return all artifact cards from your graveyard to the battlefield. They gain haste until end of turn.| +Elementalist's Palette|Commander 2021|76|R|{3}|Artifact|||Whenever you cast a spell with {X} in its mana cost, put two charge counters on Elementalist's Palette.${T}: Add one mana of any color.${T}: Add {C} for each charge counter on Elementalist's Palette. Spend this mana only on costs that contain {X}.| +Geometric Nexus|Commander 2021|77|R|{2}|Artifact|||Whenever a player casts an instant or sorcery spell, put a number of charge counters on Geometric Nexus equal to that spell's mana value.${6}, {T}, Remove all charge counters from Geometric Nexus: Create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it, where X is the number of charge counters removed this way.| +Tempting Contract|Commander 2021|78|R|{4}|Artifact|||At the beginning of your upkeep, each opponent may create a Treasure token. For each opponent who does, you create a Treasure token.| +Triplicate Titan|Commander 2021|79|R|{9}|Artifact Creature - Golem|9|9|Flying, vigilance, trample$When Triplicate Titan dies, create a 3/3 colorless Golem artifact creature token with flying, a 3/3 colorless Golem artifact creature token with vigilance, and a 3/3 colorless Golem artifact creature token with trample.| +Study Hall|Commander 2021|80|C||Land|||{T}: Add {C}.${1}, {T}: Add one mana of any color. When you spend this mana to cast your commander, scry X, where X is the number of times it's been cast from the command zone this game.| +Witch's Clinic|Commander 2021|81|R||Land|||{T}: Add {C}.${2}, {T}: Target commander gains lifelink until end of turn.| +Desolation Twin|Commander 2021|82|R|{10}|Creature - Eldrazi|10|10|When you cast this spell, create a 10/10 colorless Eldrazi creature token.| +Angel of Serenity|Commander 2021|83|M|{4}{W}{W}{W}|Creature - Angel|5|6|Flying$When Angel of Serenity enters the battlefield, you may exile up to three other target creatures from the battlefield and/or creature cards from graveyards.$When Angel of Serenity leaves the battlefield, return the exiled cards to their owners' hands.| +Boreas Charger|Commander 2021|84|R|{2}{W}|Creature - Pegasus|2|1|Flying$When Boreas Charger leaves the battlefield, choose an opponent who controls more lands than you. Search your library for a number of Plains cards equal to the difference, reveal those cards, put one of them onto the battlefield tapped and the rest into your hand, then shuffle.| +Citadel Siege|Commander 2021|85|R|{2}{W}{W}|Enchantment|||As Citadel Siege enters the battlefield, choose Khans or Dragons.$• Khans — At the beginning of combat on your turn, put two +1/+1 counters on target creature you control.$• Dragons — At the beginning of combat on each opponent's turn, tap target creature that player controls.| +Cleansing Nova|Commander 2021|86|R|{3}{W}{W}|Sorcery|||Choose one —$• Destroy all creatures.$• Destroy all artifacts and enchantments.| +Darksteel Mutation|Commander 2021|87|U|{1}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature is an Insect artifact creature with base power and toughness 0/1 and has indestructible, and it loses all other abilities, card types, and creature types.| +Dispatch|Commander 2021|88|U|{W}|Instant|||Tap target creature.$Metalcraft — If you control three or more artifacts, exile that creature.| +Dispeller's Capsule|Commander 2021|89|C|{W}|Artifact|||{2}{W}, {T}, Sacrifice Dispeller's Capsule: Destroy target artifact or enchantment.| +Duelist's Heritage|Commander 2021|90|R|{2}{W}|Enchantment|||Whenever one or more creatures attack, you may have target attacking creature gain double strike until end of turn.| +Elite Scaleguard|Commander 2021|91|U|{4}{W}|Creature - Human Soldier|2|3|When Elite Scaleguard enters the battlefield, bolster 2.$Whenever a creature you control with a +1/+1 counter on it attacks, tap target creature defending player controls.| +Ghostly Prison|Commander 2021|92|U|{2}{W}|Enchantment|||Creatures can't attack you unless their controller pays {2} for each creature they control that's attacking you.| +Gideon, Champion of Justice|Commander 2021|93|M|{2}{W}{W}|Legendary Planeswalker - Gideon|4|+1: Put a loyalty counter on Gideon, Champion of Justice for each creature target opponent controls.$0: Until end of turn, Gideon, Champion of Justice becomes a Human Soldier creature with power and toughness each equal to the number of loyalty counters on him and gains indestructible. He's still a planeswalker. Prevent all damage that would be dealt to him this turn.$−15: Exile all other permanents.| +Hunted Lammasu|Commander 2021|94|R|{2}{W}{W}|Creature - Lammasu|5|5|Flying$When Hunted Lammasu enters the battlefield, target opponent creates a 4/4 black Horror creature token.| +Knight of the White Orchid|Commander 2021|95|R|{W}{W}|Creature - Human Knight|2|2|First strike$When Knight of the White Orchid enters the battlefield, if an opponent controls more lands than you, you may search your library for a Plains card, put it onto the battlefield, then shuffle your library.| +Martial Impetus|Commander 2021|96|U|{2}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +1/+1 and is goaded.$Whenever enchanted creature attacks, each other creature that's attacking one of your opponents gets +1/+1 until end of turn.| +Oblation|Commander 2021|97|R|{2}{W}|Instant|||The owner of target nonland permanent shuffles it into their library, then draws two cards.| +Oreskos Explorer|Commander 2021|98|U|{1}{W}|Creature - Cat Scout|2|2|When Oreskos Explorer enters the battlefield, search your library for up to X Plains cards, where X is the number of players who control more lands than you. Reveal those cards, put them into your hand, then shuffle.| +Orzhov Advokist|Commander 2021|99|U|{2}{W}|Creature - Human Advisor|1|4|At the beginning of your upkeep, each player may put two +1/+1 counters on a creature they control. If a player does, creatures that player controls can't attack you or planeswalkers you control until your next turn.| +Return to Dust|Commander 2021|100|U|{2}{W}{W}|Instant|||Exile target artifact or enchantment. If you cast this spell during your main phase, you may exile up to one other target artifact or enchantment.| +Rout|Commander 2021|101|R|{3}{W}{W}|Sorcery|||You may cast Rout as though it had flash if you pay {2} more to cast it.$Destroy all creatures. They can't be regenerated.| +Sanctum Gargoyle|Commander 2021|102|C|{3}{W}|Artifact Creature - Gargoyle|2|3|Flying$When Sanctum Gargoyle enters the battlefield, you may return target artifact card from your graveyard to your hand.| +Selfless Squire|Commander 2021|103|R|{3}{W}|Creature - Human Soldier|1|1|Flash$When Selfless Squire enters the battlefield, prevent all damage that would be dealt to you this turn.$Whenever damage that would be dealt to you is prevented, put that many +1/+1 counters on Selfless Squire.| +Soul Snare|Commander 2021|104|U|{W}|Enchantment|||{W}, Sacrifice Soul Snare: Exile target creature that's attacking you or a planeswalker you control.| +Stalking Leonin|Commander 2021|105|R|{2}{W}|Creature - Cat Archer|3|3|When Stalking Leonin enters the battlefield, secretly choose an opponent.$Reveal the player you chose: Exile target creature that's attacking you if it's controlled by the chosen player. Activate only once.| +Sun Titan|Commander 2021|106|M|{4}{W}{W}|Creature - Giant|6|6|Vigilance$Whenever Sun Titan enters the battlefield or attacks, you may return target permanent card with converted mana cost 3 or less from your graveyard to the battlefield.| +Sunscorch Regent|Commander 2021|107|R|{3}{W}{W}|Creature - Dragon|4|3|Flying$Whenever an opponent casts a spell, put a +1/+1 counter on Sunscorch Regent and you gain 1 life.| +Together Forever|Commander 2021|108|R|{W}{W}|Enchantment|||When Together Forever enters the battlefield, support 2.${1}: Choose target creature with a counter on it. When that creature dies this turn, return that card to its owner's hand.| +Tragic Arrogance|Commander 2021|109|R|{3}{W}{W}|Sorcery|||For each player, you choose from among the permanents that player controls an artifact, a creature, an enchantment, and a planeswalker. Then each player sacrifices all other nonland permanents they control.| +Vow of Duty|Commander 2021|110|U|{2}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2, has vigilance, and can't attack you or planeswalkers you control.| +Windborn Muse|Commander 2021|111|R|{3}{W}|Creature - Spirit|2|3|Flying$Creatures can't attack you unless their controller pays {2} for each creature they control that's attacking you.| +Zetalpa, Primal Dawn|Commander 2021|112|R|{6}{W}{W}|Legendary Creature - Elder Dinosaur|4|8|Flying, double strike, vigilance, trample, indestructible| +Aether Gale|Commander 2021|113|R|{3}{U}{U}|Sorcery|||Return six target nonland permanents to their owners' hands.| +Aetherspouts|Commander 2021|114|R|{3}{U}{U}|Instant|||For each attacking creature, its owner puts it on the top or bottom of their library.| +Brainstorm|Commander 2021|115|C|{U}|Instant|||Draw three cards, then put two cards from your hand on top of your library in any order.| +Champion of Wits|Commander 2021|116|R|{2}{U}|Creature - Naga Wizard|2|1|When Champion of Wits enters the battlefield, you may draw cards equal to its power. If you do, discard two cards.$Eternalize {5}{U}{U}| +Crafty Cutpurse|Commander 2021|117|R|{3}{U}|Creature - Human Pirate|2|2|Flash$When Crafty Cutpurse enters the battlefield, each token that would be created under an opponent's control this turn is created under your control instead.| +Curse of the Swine|Commander 2021|118|R|{X}{U}{U}|Sorcery|||Exile X target creatures. For each creature exiled this way, its controller creates a 2/2 green Boar creature token.| +Dig Through Time|Commander 2021|119|R|{6}{U}{U}|Instant|||Delve$Look at the top seven cards of your library. Put two of them into your hand and the rest on the bottom of your library in any order.| +Diluvian Primordial|Commander 2021|120|R|{5}{U}{U}|Creature - Avatar|5|5|Flying$When Diluvian Primordial enters the battlefield, for each opponent, you may cast up to one target instant or sorcery card from that player's graveyard without paying its mana cost. If a spell cast this way would be put into a graveyard this turn, exile it instead.| +Living Lore|Commander 2021|121|U|{3}{U}|Creature - Avatar|*|*|As Living Lore enters the battlefield, exile an instant or sorcery card from your graveyard.$Living Lore's power and toughness are each equal to the exiled card's converted mana cost.$Whenever Living Lore deals combat damage, you may sacrifice it. If you do, you may cast the exiled card without paying its mana cost.| +Metallurgic Summonings|Commander 2021|122|M|{3}{U}{U}|Enchantment|||Whenever you cast an instant or sorcery spell, create an X/X colorless Construct artifact creature token, where X is that spell's mana value.${3}{U}{U}, Exile Metallurgic Summonings: Return all instant and sorcery cards from your graveyard to your hand. Activate only if you control six or more artifacts.| +Mind's Desire|Commander 2021|123|R|{4}{U}{U}|Sorcery|||Shuffle your library. Then exile the top card of your library. Until end of turn, you may play that card without paying its mana cost.$Storm| +Naru Meha, Master Wizard|Commander 2021|124|M|{2}{U}{U}|Legendary Creature - Human Wizard|3|3|Flash$When Naru Meha, Master Wizard enters the battlefield, copy target instant or sorcery spell you control. You may choose new targets for the copy.$Other Wizards you control get +1/+1.| +Ponder|Commander 2021|125|C|{U}|Sorcery|||Look at the top three cards of your library, then put them back in any order. You may shuffle your library.$Draw a card.| +Rapid Hybridization|Commander 2021|126|U|{U}|Instant|||Destroy target creature. It can't be regenerated. That creature's controller creates a 3/3 green Frog Lizard creature token.| +Reef Worm|Commander 2021|127|R|{3}{U}|Creature - Worm|0|1|When Reef Worm dies, create a 3/3 blue Fish creature token with "When this creature dies, create a 6/6 blue Whale creature token with 'When this creature dies, create a 9/9 blue Kraken creature token.'"| +Rite of Replication|Commander 2021|128|R|{2}{U}{U}|Sorcery|||Kicker {5}$Create a token that's a copy of target creature. If this spell was kicked, create five of those tokens instead.| +Serum Visions|Commander 2021|129|U|{U}|Sorcery|||Draw a card. Scry 2.| +Swarm Intelligence|Commander 2021|130|R|{6}{U}|Enchantment|||Whenever you cast an instant or sorcery spell, you may copy that spell. You may choose new targets for the copy.| +Talrand, Sky Summoner|Commander 2021|131|R|{2}{U}{U}|Legendary Creature - Merfolk Wizard|2|2|Whenever you cast an instant or sorcery spell, create a 2/2 blue Drake creature token with flying.| +Traumatic Visions|Commander 2021|132|C|{3}{U}{U}|Instant|||Counter target spell.$Basic landcycling {1}{U}| +Treasure Cruise|Commander 2021|133|C|{7}{U}|Sorcery|||Delve$Draw three cards.| +Ambition's Cost|Commander 2021|134|U|{3}{B}|Sorcery|||You draw three cards and you lose 3 life.| +Ancient Craving|Commander 2021|135|U|{3}{B}|Sorcery|||You draw three cards and you lose 3 life.| +Bloodthirsty Aerialist|Commander 2021|136|U|{1}{B}{B}|Creature - Vampire Rogue|2|3|Flying$Whenever you gain life, put a +1/+1 counter on Bloodthirsty Aerialist.| +Bloodtracker|Commander 2021|137|R|{3}{B}|Creature - Vampire Wizard|2|2|Flying${B}, Pay 2 life: Put a +1/+1 counter on Bloodtracker.$When Bloodtracker leaves the battlefield, draw a card for each +1/+1 counter on it.| +Curse of Disturbance|Commander 2021|138|U|{2}{B}|Enchantment - Aura Curse|||Enchant player$Whenever enchanted player is attacked, create a 2/2 black Zombie creature token. Each opponent attacking that player does the same.| +Damnable Pact|Commander 2021|139|R|{X}{B}{B}|Sorcery|||Target player draws X cards and loses X life.| +Deadly Tempest|Commander 2021|140|R|{4}{B}{B}|Sorcery|||Destroy all creatures. Each player loses life equal to the number of creatures they controlled that were destroyed this way.| +Deathbringer Regent|Commander 2021|141|R|{5}{B}{B}|Creature - Dragon|5|6|Flying$When Deathbringer Regent enters the battlefield, if you cast it from your hand and there are five or more other creatures on the battlefield, destroy all other creatures.| +Defiant Bloodlord|Commander 2021|142|R|{5}{B}{B}|Creature - Vampire|4|5|Flying$Whenever you gain life, target opponent loses that much life.| +Epicure of Blood|Commander 2021|143|C|{4}{B}|Creature - Vampire|4|4|Whenever you gain life, each opponent loses 1 life.| +Feed the Swarm|Commander 2021|144|C|{1}{B}|Sorcery|||Destroy target creature or enchantment an opponent controls. You lose life equal to that permanent's mana value.| +Greed|Commander 2021|145|R|{3}{B}|Enchantment|||{B}, Pay 2 life: Draw a card.| +Infernal Offering|Commander 2021|146|R|{4}{B}|Sorcery|||Choose an opponent. You and that player each sacrifice a creature. Each player who sacrificed a creature this way draws two cards.$Choose an opponent. Return a creature card from your graveyard to the battlefield, then that player returns a creature card from their graveyard to the battlefield.| +Necropolis Regent|Commander 2021|147|M|{3}{B}{B}{B}|Creature - Vampire|6|5|Flying$Whenever a creature you control deals combat damage to a player, put that many +1/+1 counters on it.| +Noxious Gearhulk|Commander 2021|148|M|{4}{B}{B}|Artifact Creature - Construct|5|4|Menace$When Noxious Gearhulk enters the battlefield, you may destroy another target creature. If a creature is destroyed this way, you gain life equal to its toughness.| +Ob Nixilis Reignited|Commander 2021|149|M|{3}{B}{B}|Legendary Planeswalker - Nixilis|5|+1: You draw a card and you lose 1 life.$−3: Destroy target creature.$−8: Target opponent gets an emblem with "Whenever a player draws a card, you lose 2 life."| +Parasitic Impetus|Commander 2021|150|U|{2}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2 and is goaded.$Whenever enchanted creature attacks, its controller loses 2 life and you gain 2 life.| +Reckless Spite|Commander 2021|151|U|{1}{B}{B}|Instant|||Destroy two target nonblack creatures. You lose 5 life.| +Sangromancer|Commander 2021|152|R|{2}{B}{B}|Creature - Vampire Shaman|3|3|Flying$Whenever a creature an opponent controls dies, you may gain 3 life.$Whenever an opponent discards a card, you may gain 3 life.| +Sanguine Bond|Commander 2021|153|R|{3}{B}{B}|Enchantment|||Whenever you gain life, target opponent loses that much life.| +Silversmote Ghoul|Commander 2021|154|U|{2}{B}|Creature - Zombie Vampire|3|1|At the beginning of your end step, if you gained 3 or more life this turn, return Silversmote Ghoul from your graveyard to the battlefield tapped.${1}{B}, Sacrifice Silversmote Ghoul: Draw a card.| +Suffer the Past|Commander 2021|155|U|{X}{B}|Instant|||Exile X target cards from target player's graveyard. For each card exiled this way, that player loses 1 life and you gain 1 life.| +Taste of Death|Commander 2021|156|R|{4}{B}{B}|Sorcery|||Each player sacrifices three creatures. You create three Food tokens.| +Vampire Nighthawk|Commander 2021|157|U|{1}{B}{B}|Creature - Vampire Shaman|2|3|Flying, deathtouch, lifelink| +Apex of Power|Commander 2021|158|M|{7}{R}{R}{R}|Sorcery|||Exile the top seven cards of your library. Until end of turn, you may cast spells from among them.$If this spell was cast from your hand, add ten mana of any one color.| +Blasphemous Act|Commander 2021|159|R|{8}{R}|Sorcery|||This spell costs {1} less to cast for each creature on the battlefield.$Blasphemous Act deals 13 damage to each creature.| +Brass's Bounty|Commander 2021|160|R|{6}{R}|Sorcery|||For each land you control, create a Treasure token.| +Chain Reaction|Commander 2021|161|R|{2}{R}{R}|Sorcery|||Chain Reaction deals X damage to each creature, where X is the number of creatures on the battlefield.| +Charmbreaker Devils|Commander 2021|162|R|{5}{R}|Creature - Devil|4|4|At the beginning of your upkeep, return an instant or sorcery card at random from your graveyard to your hand.$Whenever you cast an instant or sorcery spell, Charmbreaker Devils gets +4/+0 until end of turn.| +Combustible Gearhulk|Commander 2021|163|M|{4}{R}{R}|Artifact Creature - Construct|6|6|First strike$When Combustible Gearhulk enters the battlefield, target opponent may have you draw three cards. If the player doesn't, you mill three cards, then Combustible Gearhulk deals damage to that player equal to the total mana value of those cards.| +Daretti, Scrap Savant|Commander 2021|164|M|{3}{R}|Legendary Planeswalker - Daretti|3|+2: Discard up to two cards, then draw that many cards.$−2: Sacrifice an artifact. If you do, return target artifact card from your graveyard to the battlefield.$−10: You get an emblem with "Whenever an artifact is put into your graveyard from the battlefield, return that card to the battlefield at the beginning of the next end step."$Daretti, Scrap Savant can be your commander.| +Dualcaster Mage|Commander 2021|165|R|{1}{R}{R}|Creature - Human Wizard|2|2|Flash$When Dualcaster Mage enters the battlefield, copy target instant or sorcery spell. You may choose new targets for the copy.| +Erratic Cyclops|Commander 2021|166|R|{3}{R}|Creature - Cyclops Shaman|0|8|Trample$Whenever you cast an instant or sorcery spell, Erratic Cyclops gets +X/+0 until end of turn, where X is that spell's mana value.| +Etali, Primal Storm|Commander 2021|167|R|{4}{R}{R}|Legendary Creature - Elder Dinosaur|6|6|Whenever Etali, Primal Storm attacks, exile the top card of each player's library, then you may cast any number of spells from among those cards without paying their mana costs.| +Faithless Looting|Commander 2021|168|C|{R}|Sorcery|||Draw two cards, then discard two cards.$Flashback {2}{R}| +Feldon of the Third Path|Commander 2021|169|M|{1}{R}{R}|Legendary Creature - Human Artificer|2|3|{2}{R}, {T}: Create a token that's a copy of target creature card in your graveyard, except it's an artifact in addition to its other types. It gains haste. Sacrifice it at the beginning of the next end step.| +Fiery Fall|Commander 2021|170|C|{5}{R}|Instant|||Fiery Fall deals 5 damage to target creature.$Basic landcycling {1}{R}| +Hellkite Igniter|Commander 2021|171|R|{5}{R}{R}|Creature - Dragon|5|5|Flying, haste${1}{R}: Hellkite Igniter gets +X/+0 until end of turn, where X is the number of artifacts you control.| +Hellkite Tyrant|Commander 2021|172|M|{4}{R}{R}|Creature - Dragon|6|5|Flying, trample$Whenever Hellkite Tyrant deals combat damage to a player, gain control of all artifacts that player controls.$At the beginning of your upkeep, if you control twenty or more artifacts, you win the game.| +Hoard-Smelter Dragon|Commander 2021|173|R|{4}{R}{R}|Creature - Dragon|5|5|Flying${3}{R}: Destroy target artifact. Hoard-Smelter Dragon gets +X/+0 until end of turn, where X is that artifact's mana value.| +Humble Defector|Commander 2021|174|U|{1}{R}|Creature - Human Rogue|2|1|{T}: Draw two cards. Target opponent gains control of Humble Defector. Activate only during your turn.| +Jaya Ballard|Commander 2021|175|M|{2}{R}{R}{R}|Legendary Planeswalker - Jaya|5|+1: Add {R}{R}{R}. Spend this mana only to cast instant or sorcery spells.$+1: Discard up to three cards, then draw that many cards.$−8: You get an emblem with "You may cast instant and sorcery spells from your graveyard. If a spell cast this way would be put into your graveyard, exile it instead."| +Mana Geyser|Commander 2021|176|C|{3}{R}{R}|Sorcery|||Add {R} for each tapped land your opponents control.| +Pia Nalaar|Commander 2021|177|R|{2}{R}|Legendary Creature - Human Artificer|2|2|When Pia Nalaar enters the battlefield, create a 1/1 colorless Thopter artifact creature token with flying.${1}{R}: Target artifact creature gets +1/+0 until end of turn.${1}, Sacrifice an artifact: Target creature can't block this turn.| +Quicksmith Genius|Commander 2021|178|U|{2}{R}|Creature - Human Artificer|3|2|Whenever an artifact enters the battlefield under your control, you may discard a card. If you do, draw a card.| +Seething Song|Commander 2021|179|C|{2}{R}|Instant|||Add {R}{R}{R}{R}{R}.| +Sunbird's Invocation|Commander 2021|180|R|{5}{R}|Enchantment|||Whenever you cast a spell from your hand, reveal the top X cards of your library, where X is that spell's mana value. You may cast a spell with mana value X or less from among cards revealed this way without paying its mana cost. Put the rest on the bottom of your library in a random order.| +Thopter Engineer|Commander 2021|181|U|{2}{R}|Creature - Human Artificer|1|3|When Thopter Engineer enters the battlefield, create a 1/1 colorless Thopter artifact creature token with flying.$Artifact creatures you control have haste.| +Volcanic Vision|Commander 2021|182|R|{5}{R}{R}|Sorcery|||Return target instant or sorcery card from your graveyard to your hand. Volcanic Vision deals damage equal to that card's mana value to each creature your opponents control. Exile Volcanic Vision.| +Wildfire Devils|Commander 2021|183|R|{3}{R}|Creature - Devil|4|2|When Wildfire Devils enters the battlefield and at the beginning of your upkeep, choose a player at random. That player exiles an instant or sorcery card from their graveyard. Copy that card. You may cast the copy without paying its mana cost.| +Ageless Entity|Commander 2021|184|R|{3}{G}{G}|Creature - Elemental|4|4|Whenever you gain life, put that many +1/+1 counters on Ageless Entity.| +Arashi, the Sky Asunder|Commander 2021|185|R|{3}{G}{G}|Legendary Creature - Spirit|5|5|{X}{G}, {T}: Arashi, the Sky Asunder deals X damage to target creature with flying.$Channel — {X}{G}{G}, Discard Arashi: Arashi deals X damage to each creature with flying.| +Beast Within|Commander 2021|186|U|{2}{G}|Instant|||Destroy target permanent. Its controller creates a 3/3 green Beast creature token.| +Cultivate|Commander 2021|187|U|{2}{G}|Sorcery|||Search your library for up to two basic land cards, reveal those cards, put one onto the battlefield tapped and the other into your hand, then shuffle.| +Ezuri's Predation|Commander 2021|188|R|{5}{G}{G}{G}|Sorcery|||For each creature your opponents control, create a 4/4 green Beast creature token. Each of those Beasts fights a different one of those creatures.| +Forgotten Ancient|Commander 2021|189|R|{3}{G}|Creature - Elemental|0|3|Whenever a player casts a spell, you may put a +1/+1 counter on Forgotten Ancient.$At the beginning of your upkeep, you may move any number of +1/+1 counters from Forgotten Ancient onto other creatures.| +Garruk, Primal Hunter|Commander 2021|190|M|{2}{G}{G}{G}|Legendary Planeswalker - Garruk|3|+1: Create a 3/3 green Beast creature token.$−3: Draw cards equal to the greatest power among creatures you control.$−6: Create a 6/6 green Wurm creature token for each land you control.| +Gift of Paradise|Commander 2021|191|C|{2}{G}|Enchantment - Aura|||Enchant land$When Gift of Paradise enters the battlefield, you gain 3 life.$Enchanted land has "{T}: Add two mana of any one color."| +Hornet Nest|Commander 2021|192|R|{2}{G}|Creature - Insect|0|2|Defender$Whenever Hornet Nest is dealt damage, create that many 1/1 green Insect creature tokens with flying and deathtouch.| +Hornet Queen|Commander 2021|193|R|{4}{G}{G}{G}|Creature - Insect|2|2|Flying$Deathtouch$When Hornet Queen enters the battlefield, create four 1/1 green Insect creature tokens with flying and deathtouch.| +Hydra Broodmaster|Commander 2021|194|R|{4}{G}{G}|Creature - Hydra|7|7|{X}{X}{G}: Monstrosity X.$When Hydra Broodmaster becomes monstrous, create X X/X green Hydra creature tokens.| +Incubation Druid|Commander 2021|195|R|{1}{G}|Creature - Elf Druid|0|2|{T}: Add one mana of any type that a land you control could produce. If Incubation Druid has a +1/+1 counter on it, add three mana of that type instead.${3}{G}{G}: Adapt 3.| +Kazandu Tuskcaller|Commander 2021|196|R|{1}{G}|Creature - Human Shaman|1|1|Level up {1}{G}$LEVEL 2-5$1/1${T}: Create a 3/3 green Elephant creature token.$LEVEL 6+$1/1${T}: Create two 3/3 green Elephant creature tokens.| +Kodama's Reach|Commander 2021|197|C|{2}{G}|Sorcery - Arcane|||Search your library for up to two basic land cards, reveal those cards, and put one onto the battlefield tapped and the other into your hand. Then shuffle your library.| +Krosan Grip|Commander 2021|198|U|{2}{G}|Instant|||Split second$Destroy target artifact or enchantment.| +Managorger Hydra|Commander 2021|199|R|{2}{G}|Creature - Hydra|1|1|Trample$Whenever a player casts a spell, put a +1/+1 counter on Managorger Hydra.| +Nissa's Expedition|Commander 2021|200|U|{4}{G}|Sorcery|||Convoke$Search your library for up to two basic land cards, put them onto the battlefield tapped, then shuffle.| +Nissa's Renewal|Commander 2021|201|R|{5}{G}|Sorcery|||Search your library for up to three basic land cards, put them onto the battlefield tapped, then shuffle your library. You gain 7 life.| +Pulse of Murasa|Commander 2021|202|U|{2}{G}|Instant|||Return target creature or land card from a graveyard to its owner's hand. You gain 6 life.| +Rampaging Baloths|Commander 2021|203|R|{4}{G}{G}|Creature - Beast|6|6|Trample$Landfall — Whenever a land enters the battlefield under your control, you may create a 4/4 green Beast creature token.| +Rampant Growth|Commander 2021|204|C|{1}{G}|Sorcery|||Search your library for a basic land card, put that card onto the battlefield tapped, then shuffle.| +Return of the Wildspeaker|Commander 2021|205|R|{4}{G}|Instant|||Choose one —$• Draw cards equal to the greatest power among non-Human creatures you control.$• Non-Human creatures you control get +3/+3 until end of turn.| +Shamanic Revelation|Commander 2021|206|R|{3}{G}{G}|Sorcery|||Draw a card for each creature you control.$Ferocious — You gain 4 life for each creature you control with power 4 or greater.| +Terastodon|Commander 2021|207|R|{6}{G}{G}|Creature - Elephant|9|9|When Terastodon enters the battlefield, you may destroy up to three target noncreature permanents. For each permanent put into a graveyard this way, its controller creates a 3/3 green Elephant creature token.| +Verdant Sun's Avatar|Commander 2021|208|R|{5}{G}{G}|Creature - Dinosaur Avatar|5|5|Whenever Verdant Sun's Avatar or another creature enters the battlefield under your control, you gain life equal to that creature's toughness.| +Biomass Mutation|Commander 2021|209|R|{X}{G/U}{G/U}|Instant|||Creatures you control have base power and toughness X/X until end of turn.| +Boros Charm|Commander 2021|210|U|{R}{W}|Instant|||Choose one —$• Boros Charm deals 4 damage to target player or planeswalker.$• Permanents you control gain indestructible until end of turn.$• Target creature gains double strike until end of turn.| +Call the Skybreaker|Commander 2021|211|R|{5}{U/R}{U/R}|Sorcery|||Create a 5/5 blue and red Elemental creature token with flying.$Retrace| +Coiling Oracle|Commander 2021|212|C|{G}{U}|Creature - Snake Elf Druid|1|1|When Coiling Oracle enters the battlefield, reveal the top card of your library. If it's a land card, put it onto the battlefield. Otherwise, put that card into your hand.| +Crackling Drake|Commander 2021|213|U|{U}{U}{R}{R}|Creature - Drake|*|4|Flying$Crackling Drake's power is equal to the total number of instant and sorcery cards you own in exile and in your graveyard.$When Crackling Drake enters the battlefield, draw a card.| +Deathbringer Liege|Commander 2021|214|R|{2}{W/B}{W/B}{W/B}|Creature - Horror|3|4|Other white creatures you control get +1/+1.$Other black creatures you control get +1/+1.$Whenever you cast a white spell, you may tap target creature.$Whenever you cast a black spell, you may destroy target creature if it's tapped.| +Debtors' Knell|Commander 2021|215|R|{4}{W/B}{W/B}{W/B}|Enchantment|||({W/B} can be paid with either {W} or {B}.)$At the beginning of your upkeep, put target creature card from a graveyard onto the battlefield under your control.| +Epic Experiment|Commander 2021|216|M|{X}{U}{R}|Sorcery|||Exile the top X cards of your library. You may cast instant and sorcery spells with converted mana cost X or less from among them without paying their mana costs. Then put all cards exiled this way that weren't cast into your graveyard.| +Gaze of Granite|Commander 2021|217|R|{X}{B}{B}{G}|Sorcery|||Destroy each nonland permanent with converted mana cost X or less.| +Gluttonous Troll|Commander 2021|218|R|{2}{B}{G}|Creature - Troll|3|3|Trample$When Gluttonous Troll enters the battlefield, create a number of Food tokens equal to the number of opponents you have.${1}{G}, Sacrifice another nonland permanent: Gluttonous Troll gets +2/+2 until end of turn.| +Incongruity|Commander 2021|219|U|{1}{G}{U}|Instant|||Exile target creature. That creature's controller creates a 3/3 green Frog Lizard creature token.| +Incubation|Commander 2021|219|U|{G/U}|Sorcery|||Look at the top five cards of your library. You may reveal a creature card from among them and put it into your hand. Put the rest on the bottom of your library in a random order.| +Jor Kadeen, the Prevailer|Commander 2021|220|R|{3}{R}{W}|Legendary Creature - Human Warrior|5|4|First strike$Metalcraft — Creatures you control get +3/+0 as long as you control three or more artifacts.| +Kaseto, Orochi Archmage|Commander 2021|221|M|{1}{G}{U}|Legendary Creature - Snake Wizard|2|2|{G}{U}: Target creature can't be blocked this turn. If that creature is a Snake, it gets +2/+2 until end of turn.| +Leyline Prowler|Commander 2021|222|U|{1}{B}{G}|Creature - Nightmare Beast|2|3|Deathtouch, lifelink${T}: Add one mana of any color.| +Magister of Worth|Commander 2021|223|R|{4}{W}{B}|Creature - Angel|4|4|Flying$Will of the council — When Magister of Worth enters the battlefield, starting with you, each player votes for grace or condemnation. If grace gets more votes, each player returns each creature card from their graveyard to the battlefield. If condemnation gets more votes or the vote is tied, destroy all creatures other than Magister of Worth.| +Master Biomancer|Commander 2021|224|M|{2}{G}{U}|Creature - Elf Wizard|2|4|Each other creature you control enters the battlefield with a number of additional +1/+1 counters on it equal to Master Biomancer's power and as a Mutant in addition to its other types.| +Moldervine Reclamation|Commander 2021|225|U|{3}{B}{G}|Enchantment|||Whenever a creature you control dies, you gain 1 life and draw a card.| +Plaxcaster Frogling|Commander 2021|226|U|{1}{G}{U}|Creature - Frog Mutant|0|0|Graft 3${2}: Target creature with a +1/+1 counter on it gains shroud until end of turn.| +Primal Empathy|Commander 2021|227|U|{1}{G}{U}|Enchantment|||At the beginning of your upkeep, draw a card if you control a creature with the greatest power among creatures on the battlefield. Otherwise, put a +1/+1 counter on a creature you control.| +Sapling of Colfenor|Commander 2021|228|R|{3}{B/G}{B/G}|Legendary Creature - Treefolk Shaman|2|5|Indestructible$Whenever Sapling of Colfenor attacks, reveal the top card of your library. If it's a creature card, you gain life equal to that card's toughness, lose life equal to its power, then put it into your hand.| +Spitting Image|Commander 2021|229|R|{4}{G/U}{G/U}|Sorcery|||Create a token that's a copy of target creature.$Retrace| +Teysa, Envoy of Ghosts|Commander 2021|230|R|{5}{W}{B}|Legendary Creature - Human Advisor|4|4|Vigilance, protection from creatures$Whenever a creature deals combat damage to you, destroy that creature. Create a 1/1 white and black Spirit creature token with flying.| +Trygon Predator|Commander 2021|231|U|{1}{G}{U}|Creature - Beast|2|3|Flying$Whenever Trygon Predator deals combat damage to a player, you may destroy target artifact or enchantment that player controls.| +Utter End|Commander 2021|232|R|{2}{W}{B}|Instant|||Exile target nonland permanent.| +Alhammarret's Archive|Commander 2021|233|M|{5}|Legendary Artifact|||If you would gain life, you gain twice that much life instead.$If you would draw a card except the first one you draw in each of your draw steps, draw two cards instead.| +Arcane Signet|Commander 2021|234|C|{2}|Artifact|||{T}: Add one mana of any color in your commander's color identity.| +Bloodthirsty Blade|Commander 2021|235|U|{2}|Artifact - Equipment|||Equipped creature gets +2/+0 and is goaded.${1}: Attach Bloodthirsty Blade to target creature an opponent controls. Activate this ability only any time you could cast a sorcery.| +Boros Locket|Commander 2021|236|C|{3}|Artifact|||{T}: Add {R} or {W}.${R/W}{R/W}{R/W}{R/W}, {T}, Sacrifice Boros Locket: Draw two cards.| +Bosh, Iron Golem|Commander 2021|237|R|{8}|Legendary Artifact Creature - Golem|6|7|Trample${3}{R}, Sacrifice an artifact: Bosh, Iron Golem deals damage equal to the sacrificed artifact's mana value to any target.| +Burnished Hart|Commander 2021|238|U|{3}|Artifact Creature - Elk|2|2|{3}, Sacrifice Burnished Hart: Search your library for up to two basic land cards, put them onto the battlefield tapped, then shuffle.| +Commander's Sphere|Commander 2021|239|C|{3}|Artifact|||{T}: Add one mana of any color in your commander's color identity.$Sacrifice Commander's Sphere: Draw a card.| +Coveted Jewel|Commander 2021|240|R|{6}|Artifact|||When Coveted Jewel enters the battlefield, draw three cards.${T}: Add three mana of any one color.$Whenever one or more creatures an opponent controls attack you and aren't blocked, that player draws three cards and gains control of Coveted Jewel. Untap it.| +Druidic Satchel|Commander 2021|241|R|{3}|Artifact|||{2}, {T}: Reveal the top card of your library. If it's a creature card, create a 1/1 green Saproling creature token. If it's a land card, put that card onto the battlefield under your control. If it's a noncreature, nonland card, you gain 2 life.| +Duplicant|Commander 2021|242|R|{6}|Artifact Creature - Shapeshifter|2|4|Imprint — When Duplicant enters the battlefield, you may exile target nontoken creature.$As long as a card exiled with Duplicant is a creature card, Duplicant has the power, toughness, and creature types of the last creature card exiled with Duplicant. It's still a Shapeshifter.| +Elixir of Immortality|Commander 2021|243|U|{1}|Artifact|||{2}, {T}: You gain 5 life. Shuffle Elixir of Immortality and your graveyard into their owner's library.| +Hedron Archive|Commander 2021|244|U|{4}|Artifact|||{T}: Add {C}{C}.${2}, {T}, Sacrifice Hedron Archive: Draw two cards.| +Ichor Wellspring|Commander 2021|245|C|{2}|Artifact|||When Ichor Wellspring enters the battlefield or is put into a graveyard from the battlefield, draw a card.| +Idol of Oblivion|Commander 2021|246|R|{2}|Artifact|||{T}: Draw a card. Activate only if you created a token this turn.${8}, {T}, Sacrifice Idol of Oblivion: Create a 10/10 colorless Eldrazi creature token.| +Izzet Signet|Commander 2021|247|C|{2}|Artifact|||{1}, {T}: Add {U}{R}.| +Key to the City|Commander 2021|248|R|{2}|Artifact|||{T}, Discard a card: Up to one target creature can't be blocked this turn.$Whenever Key to the City becomes untapped, you may pay {2}. If you do, draw a card.| +Loxodon Warhammer|Commander 2021|249|R|{3}|Artifact - Equipment|||Equipped creature gets +3/+0 and has trample and lifelink.$Equip {3}| +Meteor Golem|Commander 2021|250|U|{7}|Artifact Creature - Golem|3|3|When Meteor Golem enters the battlefield, destroy target nonland permanent an opponent controls.| +Mind Stone|Commander 2021|251|U|{2}|Artifact|||{T}: Add {C}.${1}, {T}, Sacrifice Mind Stone: Draw a card.| +Mycosynth Wellspring|Commander 2021|252|C|{2}|Artifact|||When Mycosynth Wellspring enters the battlefield or is put into a graveyard from the battlefield, you may search your library for a basic land card, reveal it, put it into your hand, then shuffle.| +Myr Battlesphere|Commander 2021|253|R|{7}|Artifact Creature - Myr Construct|4|7|When Myr Battlesphere enters the battlefield, create four 1/1 colorless Myr artifact creature tokens.$Whenever Myr Battlesphere attacks, you may tap X untapped Myr you control. If you do, Myr Battlesphere gets +X/+0 until end of turn and deals X damage to the player or planeswalker it's attacking.| +Orzhov Signet|Commander 2021|254|U|{2}|Artifact|||{1}, {T}: Add {W}{B}.| +Paradise Plume|Commander 2021|255|U|{4}|Artifact|||As Paradise Plume enters the battlefield, choose a color.$Whenever a player casts a spell of the chosen color, you may gain 1 life.${T}: Add one mana of the chosen color.| +Pendant of Prosperity|Commander 2021|256|R|{3}|Artifact|||Pendant of Prosperity enters the battlefield under the control of an opponent of your choice.${2}, {T}: Draw a card, then you may put a land card from your hand onto the battlefield. Pendant of Prosperity's owner draws a card, then that player may put a land card from their hand onto the battlefield.| +Pilgrim's Eye|Commander 2021|257|C|{3}|Artifact Creature - Thopter|1|1|Flying$When Pilgrim's Eye enters the battlefield, you may search your library for a basic land card, reveal it, put it into your hand, then shuffle.| +Pristine Talisman|Commander 2021|258|C|{3}|Artifact|||{T}: Add {C}. You gain 1 life.| +Pyromancer's Goggles|Commander 2021|259|M|{5}|Legendary Artifact|||{T}: Add {R}. When that mana is spent to cast a red instant or sorcery spell, copy that spell and you may choose new targets for the copy.| +Scrap Trawler|Commander 2021|260|R|{3}|Artifact Creature - Construct|3|2|Whenever Scrap Trawler or another artifact you control is put into a graveyard from the battlefield, return to your hand target artifact card in your graveyard with lesser converted mana cost.| +Sculpting Steel|Commander 2021|261|R|{3}|Artifact|||You may have Sculpting Steel enter the battlefield as a copy of any artifact on the battlefield.| +Simic Signet|Commander 2021|262|C|{2}|Artifact|||{1}, {T}: Add {G}{U}.| +Sol Ring|Commander 2021|263|U|{1}|Artifact|||{T}: Add {C}{C}.| +Solemn Simulacrum|Commander 2021|264|R|{4}|Artifact Creature - Golem|2|2|When Solemn Simulacrum enters the battlefield, you may search your library for a basic land card, put that card onto the battlefield tapped, then shuffle your library.$When Solemn Simulacrum dies, you may draw a card.| +Spectral Searchlight|Commander 2021|265|U|{3}|Artifact|||{T}: Choose a player. That player adds one mana of any color they choose.| +Steel Hellkite|Commander 2021|266|R|{6}|Artifact Creature - Dragon|5|5|Flying${2}: Steel Hellkite gets +1/+0 until end of turn.${X}: Destroy each nonland permanent with mana value X whose controller was dealt combat damage by Steel Hellkite this turn. Activate only once each turn.| +Steel Overseer|Commander 2021|267|R|{2}|Artifact Creature - Construct|1|1|{T}: Put a +1/+1 counter on each artifact creature you control.| +Sun Droplet|Commander 2021|268|U|{2}|Artifact|||Whenever you're dealt damage, put that many charge counters on Sun Droplet.$At the beginning of each upkeep, you may remove a charge counter from Sun Droplet. If you do, you gain 1 life.| +Talisman of Creativity|Commander 2021|269|U|{2}|Artifact|||{T}: Add {C}.${T}: Add {U} or {R}. Talisman of Creativity deals 1 damage to you.| +Talisman of Resilience|Commander 2021|270|U|{2}|Artifact|||{T}: Add {C}.${T}: Add {B} or {G}. Talisman of Resilience deals 1 damage to you.| +Thousand-Year Elixir|Commander 2021|271|R|{3}|Artifact|||You may activate abilities of creatures you control as though those creatures had haste.${1}, {T}: Untap target creature.| +Unstable Obelisk|Commander 2021|272|U|{3}|Artifact|||{T}: Add {C}.${7}, {T}, Sacrifice Unstable Obelisk: Destroy target permanent.| +Venser's Journal|Commander 2021|273|R|{5}|Artifact|||You have no maximum hand size.$At the beginning of your upkeep, you gain 1 life for each card in your hand.| +Victory Chimes|Commander 2021|274|R|{3}|Artifact|||Untap Victory Chimes during each other player's untap step.${T}: A player of your choice adds {C}.| +Well of Lost Dreams|Commander 2021|275|R|{4}|Artifact|||Whenever you gain life, you may pay {X}, where X is less than or equal to the amount of life you gained. If you do, draw X cards.| +Ancient Den|Commander 2021|276|C||Artifact Land|||{T}: Add {W}.| +Barren Moor|Commander 2021|277|U||Land|||Barren Moor enters the battlefield tapped.${T}: Add {B}.$Cycling {B}| +Battlefield Forge|Commander 2021|278|R||Land|||{T}: Add {C}.${T}: Add {R} or {W}. Battlefield Forge deals 1 damage to you.| +Blighted Cataract|Commander 2021|279|U||Land|||{T}: Add {C}.${5}{U}, {T}, Sacrifice Blighted Cataract: Draw two cards.| +Blighted Woodland|Commander 2021|280|U||Land|||{T}: Add {C}.${3}{G}, {T}, Sacrifice Blighted Woodland: Search your library for up to two basic land cards, put them onto the battlefield tapped, then shuffle.| +Bojuka Bog|Commander 2021|281|C||Land|||Bojuka Bog enters the battlefield tapped.$When Bojuka Bog enters the battlefield, exile all cards from target player's graveyard.${T}: Add {B}.| +Boros Garrison|Commander 2021|282|U||Land|||Boros Garrison enters the battlefield tapped.$When Boros Garrison enters the battlefield, return a land you control to its owner's hand.${T}: Add {R}{W}.| +Caves of Koilos|Commander 2021|283|R||Land|||{T}: Add {C}.${T}: Add {W} or {B}. Caves of Koilos deals 1 damage to you.| +Command Tower|Commander 2021|284|C||Land|||{T}: Add one mana of any color in your commander's color identity.| +Darksteel Citadel|Commander 2021|285|U||Artifact Land|||Indestructible${T}: Add {C}.| +Desert of the Fervent|Commander 2021|286|C||Land - Desert|||Desert of the Fervent enters the battlefield tapped.${T}: Add {R}.$Cycling {1}{R}| +Desert of the Mindful|Commander 2021|287|C||Land - Desert|||Desert of the Mindful enters the battlefield tapped.${T}: Add {U}.$Cycling {1}{U}| +Exotic Orchard|Commander 2021|288|R||Land|||{T}: Add one mana of any color that a land an opponent controls could produce.| +Forgotten Cave|Commander 2021|289|C||Land|||Forgotten Cave enters the battlefield tapped.${T}: Add {R}.$Cycling {R}| +Gingerbread Cabin|Commander 2021|290|C||Land - Forest|||({T}: Add {G}.)$Gingerbread Cabin enters the battlefield tapped unless you control three or more other Forests.$When Gingerbread Cabin enters the battlefield untapped, create a Food token.| +Golgari Rot Farm|Commander 2021|291|U||Land|||Golgari Rot Farm enters the battlefield tapped.$When Golgari Rot Farm enters the battlefield, return a land you control to its owner's hand.${T}: Add {B}{G}.| +Great Furnace|Commander 2021|292|C||Artifact Land|||{T}: Add {R}.| +High Market|Commander 2021|293|R||Land|||{T}: Add {C}.${T}, Sacrifice a creature: You gain 1 life.| +Izzet Boilerworks|Commander 2021|294|U||Land|||Izzet Boilerworks enters the battlefield tapped.$When Izzet Boilerworks enters the battlefield, return a land you control to its owner's hand.${T}: Add {U}{R}.| +Jungle Hollow|Commander 2021|295|C||Land|||Jungle Hollow enters the battlefield tapped.$When Jungle Hollow enters the battlefield, you gain 1 life.${T}: Add {B} or {G}.| +Llanowar Reborn|Commander 2021|296|U||Land|||Llanowar Reborn enters the battlefield tapped.${T}: Add {G}.$Graft 1| +Llanowar Wastes|Commander 2021|297|R||Land|||{T}: Add {C}.${T}: Add {B} or {G}. Llanowar Wastes deals 1 damage to you.| +Lonely Sandbar|Commander 2021|298|C||Land|||Lonely Sandbar enters the battlefield tapped.${T}: Add {U}.$Cycling {U}| +Lumbering Falls|Commander 2021|299|R||Land|||Lumbering Falls enters the battlefield tapped.${T}: Add {G} or {U}.${2}{G}{U}: Lumbering Falls becomes a 3/3 green and blue Elemental creature with hexproof until end of turn. It's still a land.| +Mage-Ring Network|Commander 2021|300|U||Land|||{T}: Add {C}.${1}, {T}: Put a storage counter on Mage-Ring Network.${T}, Remove any number of storage counters from Mage-Ring Network: Add {C} for each storage counter removed this way.| +Memorial to Genius|Commander 2021|301|U||Land|||Memorial to Genius enters the battlefield tapped.${T}: Add {U}.${4}{U}, {T}, Sacrifice Memorial to Genius: Draw two cards.| +Mikokoro, Center of the Sea|Commander 2021|302|R||Legendary Land|||{T}: Add {C}.${2}, {T}: Each player draws a card.| +Mosswort Bridge|Commander 2021|303|R||Land|||Hideaway${T}: Add {G}.${G}, {T}: You may play the exiled card without paying its mana cost if creatures you control have total power 10 or greater.| +Myriad Landscape|Commander 2021|304|U||Land|||Myriad Landscape enters the battlefield tapped.${T}: Add {C}.${2}, {T}, Sacrifice Myriad Landscape: Search your library for up to two basic land cards that share a land type, put them onto the battlefield tapped, then shuffle.| +Novijen, Heart of Progress|Commander 2021|305|U||Land|||{T}: Add {C}.${G}{U}, {T}: Put a +1/+1 counter on each creature that entered the battlefield this turn.| +Opal Palace|Commander 2021|306|C||Land|||{T}: Add {C}.${1}, {T}: Add one mana of any color in your commander's color identity. If you spend this mana to cast your commander, it enters the battlefield with a number of additional +1/+1 counters on it equal to the number of times it's been cast from the command zone this game.| +Oran-Rief, the Vastwood|Commander 2021|307|R||Land|||Oran-Rief, the Vastwood enters the battlefield tapped.${T}: Add {G}.${T}: Put a +1/+1 counter on each green creature that entered the battlefield this turn.| +Orzhov Basilica|Commander 2021|308|U||Land|||Orzhov Basilica enters the battlefield tapped.$When Orzhov Basilica enters the battlefield, return a land you control to its owner's hand.${T}: Add {W}{B}.| +Phyrexia's Core|Commander 2021|309|U||Land|||{T}: Add {C}.${1}, {T}, Sacrifice an artifact: You gain 1 life.| +Radiant Fountain|Commander 2021|310|C||Land|||When Radiant Fountain enters the battlefield, you gain 2 life.${T}: Add {C}.| +Reliquary Tower|Commander 2021|311|U||Land|||You have no maximum hand size.${T}: Add {C}.| +Rogue's Passage|Commander 2021|312|U||Land|||{T}: Add {C}.${4}, {T}: Target creature can't be blocked this turn.| +Sapseep Forest|Commander 2021|313|U||Land - Forest|||({T}: Add {G}.)$Sapseep Forest enters the battlefield tapped.${G}, {T}: You gain 1 life. Activate only if you control two or more green permanents.| +Scavenger Grounds|Commander 2021|314|R||Land - Desert|||{T}: Add {C}.${2}, {T}, Sacrifice a Desert: Exile all cards from all graveyards.| +Secluded Steppe|Commander 2021|315|C||Land|||Secluded Steppe enters the battlefield tapped.${T}: Add {W}.$Cycling {W}| +Shivan Reef|Commander 2021|316|R||Land|||{T}: Add {C}.${T}: Add {U} or {R}. Shivan Reef deals 1 damage to you.| +Simic Growth Chamber|Commander 2021|317|U||Land|||Simic Growth Chamber enters the battlefield tapped.$When Simic Growth Chamber enters the battlefield, return a land you control to its owner's hand.${T}: Add {G}{U}.| +Slayers' Stronghold|Commander 2021|318|R||Land|||{T}: Add {C}.${R}{W}, {T}: Target creature gets +2/+0 and gains vigilance and haste until end of turn.| +Sunhome, Fortress of the Legion|Commander 2021|319|U||Land|||{T}: Add {C}.${2}{R}{W}, {T}: Target creature gains double strike until end of turn.| +Tainted Field|Commander 2021|320|U||Land|||{T}: Add {C}.${T}: Add {W} or {B}. Activate this ability only if you control a Swamp.| +Tainted Wood|Commander 2021|321|U||Land|||{T}: Add {C}.${T}: Add {B} or {G}. Activate only if you control a Swamp.| +Temple of Epiphany|Commander 2021|322|R||Land|||Temple of Epiphany enters the battlefield tapped.$When Temple of Epiphany enters the battlefield, scry 1.${T}: Add {U} or {R}.| +Temple of Malady|Commander 2021|323|R||Land|||Temple of Malady enters the battlefield tapped.$When Temple of Malady enters the battlefield, scry 1.${T}: Add {B} or {G}.| +Temple of Mystery|Commander 2021|324|R||Land|||Temple of Mystery enters the battlefield tapped.$When Temple of Mystery enters the battlefield, scry 1.${T}: Add {G} or {U}.| +Temple of Silence|Commander 2021|325|R||Land|||Temple of Silence enters the battlefield tapped.$When Temple of Silence enters the battlefield, scry 1.${T}: Add {W} or {B}.| +Temple of the False God|Commander 2021|326|U||Land|||{T}: Add {C}{C}. Activate only if you control five or more lands.| +Temple of Triumph|Commander 2021|327|R||Land|||Temple of Triumph enters the battlefield tapped.$When Temple of Triumph enters the battlefield, scry 1.${T}: Add {R} or {W}.| +Tranquil Thicket|Commander 2021|408|C||Land|||Tranquil Thicket enters the battlefield tapped.${T}: Add {G}.$Cycling {G}| +Yavimaya Coast|Commander 2021|409|R||Land|||{T}: Add {C}.${T}: Add {G} or {U}. Yavimaya Coast deals 1 damage to you.| diff --git a/Utils/mtg-sets-data.txt b/Utils/mtg-sets-data.txt index c7b9e248687b..106b9fa9ae5b 100644 --- a/Utils/mtg-sets-data.txt +++ b/Utils/mtg-sets-data.txt @@ -35,6 +35,7 @@ Commander 2017 Edition|C17| Commander 2018 Edition|C18| Commander 2019 Edition|C19| Commander 2020 Edition|C20| +Commander 2021 Edition|C21| Commander Legends|CMR| Champions of Kamigawa|CHK| Chronicles|CHR| @@ -181,6 +182,7 @@ Rise of the Eldrazi|ROE| Return to Ravnica|RTR| Starter 2000|S00| Starter 1999|S99| +Strixhaven: School of Mages|STX| Scourge|SCG| Secret Lair|SLD| Shadowmoor|SHM| diff --git a/pom.xml b/pom.xml index 097ab14a1b30..39b0251be94c 100644 --- a/pom.xml +++ b/pom.xml @@ -123,7 +123,7 @@ com.google.guava guava - 29.0-jre + 30.1.1-jre org.junit.jupiter diff --git a/readme.md b/readme.md index c8c06670b395..d96cad4a3dce 100644 --- a/readme.md +++ b/readme.md @@ -73,10 +73,11 @@ XMageLauncher in `Settings > Java > Server java options`. Github issues page contain [popular problems and fixes](https://github.com/magefree/mage/issues?q=is%3Aissue+label%3AFAQ+): * [Program freezes on startup (white/blue/black screen)](https://github.com/magefree/mage/issues/4461#issuecomment-361108597); * [Can't download images or it stops after some time](https://www.reddit.com/r/XMage/comments/agmcjf/new_xmage_release_with_ravnica_allegiance_rna/); -* [MacOS client freezes in GUI](https://github.com/magefree/mage/issues/4920#issuecomment-517944308); -* [Battlefield broken or ugly cards drawing](https://github.com/magefree/mage/issues/4626#issuecomment-374640070); +* [MacOS can't open launcher](https://www.reddit.com/r/XMage/comments/kf8l34/updated_java_on_osx_xmage_not_working/ggej8cq/) +* [MacOS client freezes in GUI (on connect dialog, on new match)](https://github.com/magefree/mage/issues/4920#issuecomment-517944308); +* [Ugly cards and GUI drawing in games](https://github.com/magefree/mage/issues/4626#issuecomment-374640070); * [No texts or small buttons in launcher](https://github.com/magefree/mage/issues/4126); -* [Could not open ...jvm.cfg](https://github.com/magefree/mage/issues/1272#issuecomment-529789018). +* [Can't run client, could not open ...jvm.cfg](https://github.com/magefree/mage/issues/1272#issuecomment-529789018). ## Performance tweaks